数据表基本class设计:

    1. #encoding: utf-8
    2. from apps.app import db
    3. from apps.configs.db_config import DB_CONFIG
    4. from datetime import datetime
    5. import time
    6. import copy
    7. from sqlalchemy.ext.declarative import declared_attr
    8. from sqlalchemy_serializer import SerializerMixin
    9. from sqlalchemy import text
    10. from apps.app import logger
    11. def current_timestamp():
    12. """
    13. 返回毫秒级时间戳
    14. :return:
    15. """
    16. return int(round(time.time() * 1000))
    17. class BaseModel(db.Model, SerializerMixin):
    18. """
    19. 设为db基类。
    20. SerializerMixin为数据序列化插件
    21. """
    22. # Flask-SQLAlchemy创建table时,如何声明基类(这个类不会创建表,可以被继承)
    23. # 方法就是把__abstract__这个属性设置为True,这个类为基类,不会被创建为表!
    24. __abstract__ = True
    25. # 添加配置设置编码
    26. # __table_args__ = {
    27. # 'mysql_charset': DB_CONFIG['mysql']['charset']
    28. # }
    29. @declared_attr
    30. def __tablename__(cls):
    31. # 将表类名自动小写,_分割,去掉末尾的"Model"字样
    32. if "_" in cls.__name__:
    33. name =cls.__name__
    34. else:
    35. name = ''.join([('_' + ch.lower()) if ch.isupper() else ch
    36. for ch in cls.__name__]).strip('_')
    37. if name[-6:] == "_model":
    38. name = name[:-6]
    39. _table_prefix = DB_CONFIG['mysql']['prefix']
    40. return _table_prefix + name if _table_prefix else name
    41. # 每个表都自动带上创建时间、更新时间
    42. # server_default表示数据库字段默认值,会写入到数据库字段定义中。且值只接受字符串,不接收整型、布尔型等,需要用text()函数格式化。
    43. create_time = db.Column(db.BigInteger, default=current_timestamp, server_default=text('0'), comment='创建时间') # 存储毫秒级时间戳
    44. # create_time = db.Column(db.Integer, default=time.time, comment='创建时间') # 存储秒级时间戳
    45. create_by = db.Column(db.String(100), default="", server_default='', comment='创建人')
    46. update_time = db.Column(db.DateTime, default=datetime.now, server_default=text('CURRENT_TIMESTAMP'), onupdate=datetime.now, server_onupdate=text('CURRENT_TIMESTAMP'), comment='更新时间')
    47. update_by = db.Column(db.String(100), default="", server_default='', comment='更新人')
    48. def toDict(self):
    49. """
    50. 返回dict结果
    51. 用法:
    52. r = model.query.……
    53. > r.toDict() # 输出dict结果数据
    54. > r.toDict().__str__() # 输出字符串结果数据
    55. :return: c_dict
    56. """
    57. # 使用深拷贝,避免引用对象self.__dict__缺少"_sa_instance_state"属性
    58. c_dict = copy.deepcopy(self.__dict__)
    59. for (k, v) in c_dict.items():
    60. # 将datetime内容格式化为[Y-m-d H:M:S]
    61. if isinstance(v, datetime):
    62. c_dict[k] = v.strftime("%Y-%m-%d %H:%M:%S")
    63. try:
    64. if "_sa_instance_state" in c_dict:
    65. del c_dict["_sa_instance_state"]
    66. # if "create_time" in c_dict:
    67. # del c_dict["create_time"]
    68. if "update_time" in c_dict:
    69. del c_dict["update_time"]
    70. except Exception as e:
    71. logger.error(e)
    72. return c_dict

    用户相关表结构定义:

    1. # encoding:utf-8
    2. # 用户模型
    3. from apps.app import db
    4. from apps.models.base import BaseModel
    5. from apps.configs.db_config import DB_CONFIG
    6. import shortuuid
    7. from werkzeug.security import generate_password_hash, check_password_hash
    8. from apps.models.enums import GenderEnumModel, DomainEnumModel
    9. from flask_login import UserMixin, current_user, AnonymousUserMixin
    10. from apps.app import logger
    11. class UserPermissionModel(BaseModel):
    12. """
    13. 权限集表
    14. 角色权限关系:
    15. UserPermissionModel:存储每个页面/操作权限的权限编码;
    16. UserRoleModel:角色表;
    17. role_to_permission:角色的权限通过角色权限关联表进行一对多关联;
    18. UserModel:用户可多选角色;
    19. role_to_user:用户与角色通过用户角色关联表进行一对多关联;
    20. """
    21. # __tablename__ = DB_CONFIG['mysql']['prefix'] + 'user_permission'
    22. __table_args__ = {'comment': '菜单权限表'} # 添加索引和表注释
    23. # 序列化排除字段
    24. serialize_rules = ("-roles",)
    25. id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    26. name = db.Column(db.String(100), default='', comment="权限名称")
    27. code = db.Column(db.String(50), nullable=False, comment='权限编码', unique=True)
    28. parent_id = db.Column(db.Integer, default=0, comment="上级权限ID")
    29. type = db.Column(db.Integer, comment="权限类型:1 目录,2 菜单,3 按钮,9 接口")
    30. url = db.Column(db.String(100), default="", comment='请求链接')
    31. is_frame = db.Column(db.Integer, default=0, comment="是否外链:0 否,1 是")
    32. explain = db.Column(db.String(255), default='', comment="说明")
    33. domain = db.Column(db.Enum(DomainEnumModel), default=DomainEnumModel.ADMIN, unique=False, comment='权限所属域')
    34. sort_num = db.Column(db.Integer, default=10, comment='排序码,数值越小越靠前')
    35. is_default = db.Column(db.Integer, default=0, comment="是否默认拥有的权限:0 非默认;1 默认权限")
    36. status = db.Column(db.Integer, default=0, comment='状态:0 正常/显示,-1 禁用/隐藏')
    37. is_delete = db.Column(db.Integer, default=0, comment='是否删除:0 正常,1 表示已删除')
    38. # # 上级权限id
    39. # parent_id = db.Column(db.Integer, db.ForeignKey('%s.id' % (DB_CONFIG['mysql']['prefix'] + 'user_permission')), nullable=True, comment='上级权限id')
    40. # # 当上级只有一个时,直接关联上级即可。自引用UserPermissionModel.id,需加入remote_side参数
    41. # parent = db.relationship('UserPermissionModel', remote_side=[id], backref="children")
    42. def default_permission(self, domain=None):
    43. """
    44. 查询未删除的默认权限集
    45. :param domain: 如果传入站点标识,则只查询该站点的默认权限集
    46. :return:
    47. """
    48. # 查询条件
    49. q = [UserPermissionModel.is_delete==0]
    50. if domain:
    51. q.append(UserPermissionModel.domain==domain)
    52. return UserPermissionModel.query.filter(*q).all()
    53. def all_permission(self, domain=None):
    54. """
    55. 查询全部未删除的权限集
    56. :param domain: 如果传入站点标识,则只查询该站点的全部权限集
    57. :return:
    58. """
    59. # 查询条件
    60. q = [UserPermissionModel.is_delete == 0]
    61. if domain:
    62. q.append(UserPermissionModel.domain == domain)
    63. return UserPermissionModel.query.filter(*q).all()
    64. def has_permission(self, code):
    65. """
    66. 判断权限是否有入库
    67. :param code <UserPermissionModel.code>:
    68. :return:
    69. """
    70. permission = UserPermissionModel.query.filter(UserPermissionModel.is_delete==0, UserPermissionModel.code==code).first()
    71. if permission:
    72. return True
    73. else:
    74. return False
    75. # 角色-权限 关联表
    76. role_to_permission = db.Table(
    77. DB_CONFIG['mysql']['prefix'] + 'user_role_to_permission',
    78. db.Column('role_id', db.String(100), db.ForeignKey('%s.id' % (DB_CONFIG['mysql']['prefix'] + 'user_role')),
    79. primary_key=True),
    80. db.Column('permission_id', db.Integer, db.ForeignKey('%s.id' % UserPermissionModel.__tablename__), primary_key=True)
    81. )
    82. class UserRoleModel(BaseModel):
    83. # 用户角色表
    84. # __tablename__ = DB_CONFIG['mysql']['prefix'] + 'user_role'
    85. __table_args__ = {'comment': '用户角色表'} # 添加索引和表注释
    86. serialize_rules = ("-users", "-permissions")
    87. # serialize_only = ("-users",)
    88. id = db.Column(db.String(100), primary_key=True, default=shortuuid.uuid, comment='唯一标识')
    89. code = db.Column(db.String(100), unique=False, comment='角色编码')
    90. name = db.Column(db.String(50), default='', comment='角色名称')
    91. desc = db.Column(db.String(100), default='', comment='角色介绍')
    92. domain = db.Column(db.Enum(DomainEnumModel), default=DomainEnumModel.ADMIN, unique=False, comment='角色所属域')
    93. sort_num = db.Column(db.Integer, default=10, comment='排序码,数值越小越靠前')
    94. is_default = db.Column(db.Integer, default=0, comment='默认角色:0 非默认,1 默认')
    95. status = db.Column(db.Integer, default=0, comment='状态:0 正常,-1 禁用')
    96. is_delete = db.Column(db.Integer, default=0, comment='是否删除:0 正常,1 表示已删除')
    97. # 角色权限
    98. permissions = db.relationship('UserPermissionModel', secondary=role_to_permission, backref='roles')
    99. def toDict(self):
    100. # 重载基类toDict方法
    101. c_dict = BaseModel.toDict(self)
    102. try:
    103. if "domain" in c_dict:
    104. # 枚举类型处理
    105. c_dict["domain_name"] = self.domain.name
    106. c_dict["domain_value"] = self.domain.value
    107. del c_dict["domain"]
    108. except Exception as e:
    109. logger.error(e)
    110. return c_dict
    111. @property
    112. def role_info(self):
    113. info = {
    114. # 'id': self.id,
    115. 'name': self.name,
    116. 'code': self.code,
    117. 'domain': self.domain.name,
    118. 'status': self.status
    119. }
    120. return info
    121. # 用户-角色 关联表
    122. role_to_user = db.Table(
    123. DB_CONFIG['mysql']['prefix'] + 'user_role_to_user',
    124. db.Column('role_id', db.String(100), db.ForeignKey('%s.id' % UserRoleModel.__tablename__), primary_key=True),
    125. db.Column('user_id', db.String(100), db.ForeignKey('%s.id' % (DB_CONFIG['mysql']['prefix'] + 'user')),
    126. primary_key=True)
    127. )
    128. class UserModel(BaseModel, UserMixin):
    129. # 用户信息表
    130. __tablename__ = DB_CONFIG['mysql']['prefix'] + 'user'
    131. __table_args__ = {'comment': '用户信息表'} # 添加索引和表注释
    132. # 序列化排除字段
    133. serialize_rules = ("-_password",)
    134. # serialize_only = ("username", "telephone")
    135. id = db.Column(db.String(100), primary_key=True, default=shortuuid.uuid, comment='用户标识')
    136. username = db.Column(db.String(50), nullable=False, comment='用户名')
    137. telephone = db.Column(db.String(11), unique=False, default="", comment='手机号')
    138. _password = db.Column(db.String(100), nullable=False, comment='密码')
    139. email = db.Column(db.String(50), default="", comment='邮箱')
    140. avatar_url = db.Column(db.String(100), default="", comment='头像地址')
    141. signature = db.Column(db.String(100), default="", comment='个性签名')
    142. realname = db.Column(db.String(50), default="", comment='姓名')
    143. province_id = db.Column(db.Integer, default=0, comment='省份id')
    144. city_id = db.Column(db.Integer, default=0, comment='城市id')
    145. region_id = db.Column(db.Integer, default=0, comment='区域id')
    146. gender = db.Column(db.Enum(GenderEnumModel), default=GenderEnumModel.UNKNOW, comment='性别')
    147. login_error_num = db.Column(db.Integer, default=0, comment='登录错误次数,每次登录成功清零')
    148. last_login_time = db.Column(db.DateTime, comment='最后登录时间')
    149. is_developer = db.Column(db.Integer, default=0, comment="是否开发者:0 否,1 是")
    150. status = db.Column(db.Integer, default=0, comment='状态:0 正常,-1 禁用')
    151. active = db.Column(db.Integer, default=1, comment='是否可用:0 不可用,1 表示可用')
    152. is_delete = db.Column(db.Integer, default=0, comment='是否删除:0 正常,1 表示已删除')
    153. # 上级用户
    154. # superior_user_id = db.Column(db.String(100), db.ForeignKey('%s.id' % UserModel.__tablename__), nullable=True, default="", comment='上级用户id')
    155. # 当上级用户有多个时,使用多对多的关系
    156. # superior_user = db.relationship('UserModel', secondary=superior_users, backref='users')
    157. # 当上级用户只有一个时,直接关联上级用户即可。自引用user.id,需加入remote_side参数
    158. # superior_user = db.relationship('UserModel', remote_side=[id])
    159. # 用户角色
    160. roles = db.relationship('UserRoleModel', secondary=role_to_user, backref='users')
    161. def __init__(self, user_id=None, *args, **kwargs):
    162. if "password" in kwargs:
    163. self.password = kwargs.get('password')
    164. kwargs.pop("password")
    165. super(UserModel, self).__init__(*args, **kwargs)
    166. # def toDict(self):
    167. # # 重载基类toDict方法
    168. # c_dict = BaseModel.toDict(self)
    169. #
    170. # try:
    171. # roles = []
    172. # for role in self.roles:
    173. # roles.append(role.toDict())
    174. # c_dict["roles"] = roles
    175. #
    176. # if "_password" in c_dict:
    177. # del c_dict["_password"]
    178. # if "gender" in c_dict:
    179. # # 枚举类型处理
    180. # c_dict["gender_name"] = self.gender.name
    181. # c_dict["gender_value"] = self.gender.value
    182. # del c_dict["gender"]
    183. # except Exception as e:
    184. # logger.err(e)
    185. #
    186. # return c_dict
    187. @property
    188. def jwt_login_time_dict(self):
    189. """
    190. 将jwt_login_time格式化为dict
    191. :return:
    192. """
    193. jwt_login_times = {}
    194. for v in self.jwt_login_time:
    195. jwt_login_times[v.id] = round(v.login_time / 1000000, 6)
    196. return jwt_login_times
    197. @property
    198. def password(self):
    199. return self._password
    200. @password.setter
    201. def password(self, raw_password):
    202. # password赋值时,自动加密
    203. self._password = generate_password_hash(raw_password)
    204. def check_password(self, raw_password):
    205. result = check_password_hash(self.password, raw_password)
    206. return result
    207. @property
    208. def is_authenticated(self):
    209. return True
    210. @property
    211. def is_active(self):
    212. if self.status == 0:
    213. return True
    214. else:
    215. return False
    216. # return self.active
    217. @property
    218. def is_anonymous(self):
    219. return False
    220. @property
    221. def permissions(self):
    222. """
    223. 返回该用户的所有权限
    224. :return:
    225. """
    226. # 开发者直接返回所有权限
    227. if self.is_developer:
    228. return UserPermissionModel.all_permission("all")
    229. # 如果没有关联角色,返回空
    230. if not self.roles:
    231. return []
    232. # 定义权限集合
    233. all_permissions = []
    234. # 循环合并多角色的权限,最后去重
    235. for role in self.roles:
    236. all_permissions.extend(role.permissions)
    237. return list(set(all_permissions))
    238. def can(self, permission):
    239. """
    240. 判断用户是否有传入权限
    241. :param permission <UserPermissionModel.code>: 权限编码
    242. :return:
    243. """
    244. result = False
    245. has_per = UserPermissionModel.has_permission(permission)
    246. # 如果权限有入库
    247. if has_per:
    248. # 查询当前用户所有权限
    249. all_permissions = self.permissions
    250. for p in all_permissions:
    251. # 传入权限属于该用户
    252. if permission == p.code:
    253. result = True
    254. else:
    255. # 如果权限没有入库,直接返回通过
    256. result = True
    257. return result
    258. @property
    259. def user_info(self):
    260. """
    261. 返回用户个人信息
    262. :return:
    263. """
    264. # info = {
    265. # "name": self.realname,
    266. # "status": self.status,
    267. # "is_delete": self.is_delete,
    268. # "email": self.email,
    269. # "telephone": self.telephone,
    270. # "avatar_url": self.avatar_url,
    271. # "id": self.id
    272. # }
    273. # roles = []
    274. # for role in self.roles:
    275. # roles.append(role.role_info)
    276. # info["roles"] = roles
    277. # return info
    278. return self.to_dict(only=('id', 'username','realname', 'status', 'is_delete', 'email', 'telephone', 'avatar_url', 'roles'))
    279. def __repr__(self):
    280. return '<User %r>' % (self.username)
    281. # # 上级用户关联表,如有多个用户上级,则需要用该表
    282. # superior_users = db.Table(
    283. # 'bbx_superior_users',
    284. # db.Column('superior_user_id', db.String(100), db.ForeignKey('%s.id' % UserModel.__tablename__), primary_key=True),
    285. # db.Column('user_id', db.String(100), db.ForeignKey('%s.id' % UserModel.__tablename__), primary_key=True)
    286. # )
    287. class AnonymousUser(AnonymousUserMixin):
    288. # 匿名用户
    289. def __init__(self, **kwargs):
    290. super(AnonymousUser, self).__init__(**kwargs)
    291. @property
    292. def id(self):
    293. return None
    294. @property
    295. def is_active(self):
    296. return False
    297. @property
    298. def is_authenticated(self):
    299. return False
    300. @property
    301. def is_anonymous(self):
    302. return True
    303. @property
    304. def user_info(self):
    305. return None
    306. @property
    307. def permissions(self):
    308. return None
    309. def can(self, permissions):
    310. return False
    311. # def get_id(self):
    312. # return None
    313. class UserJwtLoginTimeModel(BaseModel):
    314. # 用户JWT登录时间表
    315. # __tablename__ = DB_CONFIG['mysql']['prefix'] + 'user_jwt_login_time'
    316. __table_args__ = {'comment': '用户JWT登录时间表'} # 添加索引和表注释
    317. # 序列化排除字段
    318. serialize_rules = ("-user",)
    319. id = db.Column(db.String(100), primary_key=True, comment='cid')
    320. user_id = db.Column(db.String(100), db.ForeignKey('%s.id' % UserModel.__tablename__), comment='用户ID')
    321. login_time = db.Column(db.BigInteger, comment='jwt登录时间')
    322. user = db.relationship('UserModel', backref='jwt_login_time')
    323. class UserLoginLogModel(BaseModel):
    324. # 用户登录日志表
    325. # __tablename__ = DB_CONFIG['mysql']['prefix'] + 'user_login_log'
    326. __table_args__ = {'comment': '用户登录日志表'} # 添加索引和表注释
    327. # 序列化排除字段
    328. serialize_rules = ("-user",) ,
    329. id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment='唯一标识')
    330. user_id = db.Column(db.String(100), db.ForeignKey('%s.id' % UserModel.__tablename__), comment='用户ID')
    331. pass_error = db.Column(db.Integer, default=0, comment='登录结果:0 成功,1 失败')
    332. login_account = db.Column(db.String(100), default='', comment='登录账号')
    333. login_msg = db.Column(db.String(100), default='', comment='登录信息')
    334. ip = db.Column(db.String(20), default='', comment='登录IP')
    335. client = db.Column(db.String(100), default='', comment='客户端')
    336. user = db.relationship('UserModel', backref='login_log')