models.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. import hashlib
  2. import os
  3. from pathlib import PurePosixPath
  4. from django.contrib.auth.models import AbstractUser
  5. from django.core.files.base import File
  6. from django.db import models
  7. from application import dispatch
  8. from application.settings import BASE_DIR
  9. from dvadmin.utils.models import CoreModel, table_prefix
  10. STATUS_CHOICES = (
  11. (0, "禁用"),
  12. (1, "启用"),
  13. )
  14. class Empty(CoreModel):
  15. class Meta:
  16. db_table = table_prefix + "empty"
  17. verbose_name = "empty"
  18. verbose_name_plural = verbose_name
  19. ordering = ("id",)
  20. class Users(CoreModel, AbstractUser):
  21. username = models.CharField(max_length=150, unique=True, db_index=True, verbose_name="用户账号",
  22. help_text="用户账号")
  23. employee_no = models.CharField(max_length=150, unique=True, db_index=True, null=True, blank=True,
  24. verbose_name="工号", help_text="工号")
  25. email = models.EmailField(max_length=255, verbose_name="邮箱", null=True, blank=True, help_text="邮箱")
  26. mobile = models.CharField(max_length=255, verbose_name="电话", null=True, blank=True, help_text="电话")
  27. avatar = models.CharField(max_length=255, verbose_name="头像", null=True, blank=True, help_text="头像")
  28. name = models.CharField(max_length=40, verbose_name="姓名", help_text="姓名")
  29. GENDER_CHOICES = (
  30. (0, "未知"),
  31. (1, "男"),
  32. (2, "女"),
  33. )
  34. gender = models.IntegerField(
  35. choices=GENDER_CHOICES, default=0, verbose_name="性别", null=True, blank=True, help_text="性别"
  36. )
  37. USER_TYPE = (
  38. (0, "后台用户"),
  39. (1, "前台用户"),
  40. )
  41. user_type = models.IntegerField(
  42. choices=USER_TYPE, default=0, verbose_name="用户类型", null=True, blank=True, help_text="用户类型"
  43. )
  44. post = models.ManyToManyField(to="Post", blank=True, verbose_name="关联岗位", db_constraint=False,
  45. help_text="关联岗位")
  46. role = models.ManyToManyField(to="Role", blank=True, verbose_name="关联角色", db_constraint=False,
  47. help_text="关联角色")
  48. dept = models.ForeignKey(
  49. to="Dept",
  50. verbose_name="所属部门",
  51. on_delete=models.PROTECT,
  52. db_constraint=False,
  53. null=True,
  54. blank=True,
  55. help_text="关联部门",
  56. )
  57. last_token = models.CharField(max_length=255, null=True, blank=True, verbose_name="最后一次登录Token",
  58. help_text="最后一次登录Token")
  59. def set_password(self, raw_password):
  60. super().set_password(hashlib.md5(raw_password.encode(encoding="UTF-8")).hexdigest())
  61. class Meta:
  62. db_table = table_prefix + "system_users"
  63. verbose_name = "用户表"
  64. verbose_name_plural = verbose_name
  65. ordering = ("-create_datetime",)
  66. class Post(CoreModel):
  67. name = models.CharField(null=False, max_length=64, verbose_name="岗位名称", help_text="岗位名称")
  68. code = models.CharField(max_length=32, verbose_name="岗位编码", help_text="岗位编码")
  69. sort = models.IntegerField(default=1, verbose_name="岗位顺序", help_text="岗位顺序")
  70. STATUS_CHOICES = (
  71. (0, "离职"),
  72. (1, "在职"),
  73. )
  74. status = models.IntegerField(choices=STATUS_CHOICES, default=1, verbose_name="岗位状态", help_text="岗位状态")
  75. class Meta:
  76. db_table = table_prefix + "system_post"
  77. verbose_name = "岗位表"
  78. verbose_name_plural = verbose_name
  79. ordering = ("sort",)
  80. class Role(CoreModel):
  81. name = models.CharField(max_length=64, verbose_name="角色名称", help_text="角色名称")
  82. key = models.CharField(max_length=64, unique=True, verbose_name="权限字符", help_text="权限字符")
  83. sort = models.IntegerField(default=1, verbose_name="角色顺序", help_text="角色顺序")
  84. status = models.BooleanField(default=True, verbose_name="角色状态", help_text="角色状态")
  85. admin = models.BooleanField(default=False, verbose_name="是否为admin", help_text="是否为admin")
  86. DATASCOPE_CHOICES = (
  87. (0, "仅本人数据权限"),
  88. (1, "本部门及以下数据权限"),
  89. (2, "本部门数据权限"),
  90. (3, "全部数据权限"),
  91. (4, "自定数据权限"),
  92. )
  93. data_range = models.IntegerField(default=0, choices=DATASCOPE_CHOICES, verbose_name="数据权限范围",
  94. help_text="数据权限范围")
  95. remark = models.TextField(verbose_name="备注", help_text="备注", null=True, blank=True)
  96. dept = models.ManyToManyField(to="Dept", verbose_name="数据权限-关联部门", db_constraint=False,
  97. help_text="数据权限-关联部门")
  98. menu = models.ManyToManyField(to="Menu", verbose_name="关联菜单", db_constraint=False, help_text="关联菜单")
  99. permission = models.ManyToManyField(
  100. to="MenuButton", verbose_name="关联菜单的接口按钮", db_constraint=False, help_text="关联菜单的接口按钮"
  101. )
  102. class Meta:
  103. db_table = table_prefix + "system_role"
  104. verbose_name = "角色表"
  105. verbose_name_plural = verbose_name
  106. ordering = ("sort",)
  107. class Dept(CoreModel):
  108. name = models.CharField(max_length=64, verbose_name="部门名称", help_text="部门名称")
  109. key = models.CharField(max_length=64, unique=True, null=True, blank=True, verbose_name="关联字符",
  110. help_text="关联字符")
  111. sort = models.IntegerField(default=1, verbose_name="显示排序", help_text="显示排序")
  112. owner = models.CharField(max_length=32, verbose_name="负责人", null=True, blank=True, help_text="负责人")
  113. phone = models.CharField(max_length=32, verbose_name="联系电话", null=True, blank=True, help_text="联系电话")
  114. email = models.EmailField(max_length=32, verbose_name="邮箱", null=True, blank=True, help_text="邮箱")
  115. status = models.BooleanField(default=True, verbose_name="部门状态", null=True, blank=True, help_text="部门状态")
  116. parent = models.ForeignKey(
  117. to="Dept",
  118. on_delete=models.CASCADE,
  119. default=None,
  120. verbose_name="上级部门",
  121. db_constraint=False,
  122. null=True,
  123. blank=True,
  124. help_text="上级部门",
  125. db_index=True
  126. )
  127. @classmethod
  128. def recursion_dept_info(cls, dept_id: int, dept_all_list=None, dept_list=None):
  129. """
  130. 递归获取部门的所有下级部门
  131. :param dept_id: 需要获取的id
  132. :param dept_all_list: 所有列表
  133. :param dept_list: 递归list
  134. :return:
  135. """
  136. if not dept_all_list:
  137. dept_all_list = Dept.objects.values("id", "parent")
  138. if dept_list is None:
  139. dept_list = [dept_id]
  140. for ele in dept_all_list:
  141. if ele.get("parent") == dept_id:
  142. dept_list.append(ele.get("id"))
  143. cls.recursion_dept_info(ele.get("id"), dept_all_list, dept_list)
  144. return list(set(dept_list))
  145. class Meta:
  146. db_table = table_prefix + "system_dept"
  147. verbose_name = "部门表"
  148. verbose_name_plural = verbose_name
  149. ordering = ("sort",)
  150. class Menu(CoreModel):
  151. parent = models.ForeignKey(
  152. to="Menu",
  153. on_delete=models.CASCADE,
  154. verbose_name="上级菜单",
  155. null=True,
  156. blank=True,
  157. db_constraint=False,
  158. help_text="上级菜单",
  159. )
  160. icon = models.CharField(max_length=64, verbose_name="菜单图标", null=True, blank=True, help_text="菜单图标")
  161. name = models.CharField(max_length=64, verbose_name="菜单名称", help_text="菜单名称")
  162. sort = models.IntegerField(default=1, verbose_name="显示排序", null=True, blank=True, help_text="显示排序")
  163. ISLINK_CHOICES = (
  164. (0, "否"),
  165. (1, "是"),
  166. )
  167. is_link = models.BooleanField(default=False, verbose_name="是否外链", help_text="是否外链")
  168. is_catalog = models.BooleanField(default=False, verbose_name="是否目录", help_text="是否目录")
  169. web_path = models.CharField(max_length=128, verbose_name="路由地址", null=True, blank=True, help_text="路由地址")
  170. component = models.CharField(max_length=128, verbose_name="组件地址", null=True, blank=True, help_text="组件地址")
  171. component_name = models.CharField(max_length=50, verbose_name="组件名称", null=True, blank=True,
  172. help_text="组件名称")
  173. status = models.BooleanField(default=True, blank=True, verbose_name="菜单状态", help_text="菜单状态")
  174. frame_out = models.BooleanField(default=False, blank=True, verbose_name="是否主框架外", help_text="是否主框架外")
  175. cache = models.BooleanField(default=False, blank=True, verbose_name="是否页面缓存", help_text="是否页面缓存")
  176. visible = models.BooleanField(default=True, blank=True, verbose_name="侧边栏中是否显示",
  177. help_text="侧边栏中是否显示")
  178. class Meta:
  179. db_table = table_prefix + "system_menu"
  180. verbose_name = "菜单表"
  181. verbose_name_plural = verbose_name
  182. ordering = ("sort",)
  183. class MenuButton(CoreModel):
  184. menu = models.ForeignKey(
  185. to="Menu",
  186. db_constraint=False,
  187. related_name="menuPermission",
  188. on_delete=models.PROTECT,
  189. verbose_name="关联菜单",
  190. help_text="关联菜单",
  191. )
  192. name = models.CharField(max_length=64, verbose_name="名称", help_text="名称")
  193. value = models.CharField(max_length=64, verbose_name="权限值", help_text="权限值")
  194. api = models.CharField(max_length=200, verbose_name="接口地址", help_text="接口地址")
  195. METHOD_CHOICES = (
  196. (0, "GET"),
  197. (1, "POST"),
  198. (2, "PUT"),
  199. (3, "DELETE"),
  200. )
  201. method = models.IntegerField(default=0, verbose_name="接口请求方法", null=True, blank=True,
  202. help_text="接口请求方法")
  203. class Meta:
  204. db_table = table_prefix + "system_menu_button"
  205. verbose_name = "菜单权限表"
  206. verbose_name_plural = verbose_name
  207. ordering = ("-name",)
  208. class Dictionary(CoreModel):
  209. TYPE_LIST = (
  210. (0, "text"),
  211. (1, "number"),
  212. (2, "date"),
  213. (3, "datetime"),
  214. (4, "time"),
  215. (5, "files"),
  216. (6, "boolean"),
  217. (7, "images"),
  218. )
  219. label = models.CharField(max_length=100, blank=True, null=True, verbose_name="字典名称", help_text="字典名称")
  220. value = models.CharField(max_length=200, blank=True, null=True, verbose_name="字典编号",
  221. help_text="字典编号/实际值")
  222. parent = models.ForeignKey(
  223. to="self",
  224. related_name="sublist",
  225. db_constraint=False,
  226. on_delete=models.PROTECT,
  227. blank=True,
  228. null=True,
  229. verbose_name="父级",
  230. help_text="父级",
  231. )
  232. type = models.IntegerField(choices=TYPE_LIST, default=0, verbose_name="数据值类型", help_text="数据值类型")
  233. color = models.CharField(max_length=20, blank=True, null=True, verbose_name="颜色", help_text="颜色")
  234. is_value = models.BooleanField(default=False, verbose_name="是否为value值",
  235. help_text="是否为value值,用来做具体值存放")
  236. status = models.BooleanField(default=True, verbose_name="状态", help_text="状态")
  237. sort = models.IntegerField(default=1, verbose_name="显示排序", null=True, blank=True, help_text="显示排序")
  238. remark = models.CharField(max_length=2000, blank=True, null=True, verbose_name="备注", help_text="备注")
  239. class Meta:
  240. db_table = table_prefix + "system_dictionary"
  241. verbose_name = "字典表"
  242. verbose_name_plural = verbose_name
  243. ordering = ("sort",)
  244. def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
  245. super().save(force_insert, force_update, using, update_fields)
  246. dispatch.refresh_dictionary() # 有更新则刷新字典配置
  247. def delete(self, using=None, keep_parents=False):
  248. res = super().delete(using, keep_parents)
  249. dispatch.refresh_dictionary()
  250. return res
  251. class OperationLog(CoreModel):
  252. request_modular = models.CharField(max_length=64, verbose_name="请求模块", null=True, blank=True,
  253. help_text="请求模块")
  254. request_path = models.CharField(max_length=400, verbose_name="请求地址", null=True, blank=True,
  255. help_text="请求地址")
  256. request_body = models.TextField(verbose_name="请求参数", null=True, blank=True, help_text="请求参数")
  257. request_method = models.CharField(max_length=8, verbose_name="请求方式", null=True, blank=True,
  258. help_text="请求方式")
  259. request_msg = models.TextField(verbose_name="操作说明", null=True, blank=True, help_text="操作说明")
  260. request_ip = models.CharField(max_length=32, verbose_name="请求ip地址", null=True, blank=True,
  261. help_text="请求ip地址")
  262. request_browser = models.CharField(max_length=64, verbose_name="请求浏览器", null=True, blank=True,
  263. help_text="请求浏览器")
  264. response_code = models.CharField(max_length=32, verbose_name="响应状态码", null=True, blank=True,
  265. help_text="响应状态码")
  266. request_os = models.CharField(max_length=64, verbose_name="操作系统", null=True, blank=True, help_text="操作系统")
  267. json_result = models.TextField(verbose_name="返回信息", null=True, blank=True, help_text="返回信息")
  268. status = models.BooleanField(default=False, verbose_name="响应状态", help_text="响应状态")
  269. class Meta:
  270. db_table = table_prefix + "system_operation_log"
  271. verbose_name = "操作日志"
  272. verbose_name_plural = verbose_name
  273. ordering = ("-create_datetime",)
  274. def media_file_name(instance, filename):
  275. h = instance.md5sum
  276. basename, ext = os.path.splitext(filename)
  277. return PurePosixPath("files", h[:1], h[1:2], h + ext.lower())
  278. class FileList(CoreModel):
  279. name = models.CharField(max_length=200, null=True, blank=True, verbose_name="名称", help_text="名称")
  280. url = models.FileField(upload_to=media_file_name, null=True, blank=True, )
  281. file_url = models.CharField(max_length=255, blank=True, verbose_name="文件地址", help_text="文件地址")
  282. engine = models.CharField(max_length=100, default='local', blank=True, verbose_name="引擎", help_text="引擎")
  283. mime_type = models.CharField(max_length=100, blank=True, verbose_name="Mime类型", help_text="Mime类型")
  284. size = models.BigIntegerField(default=0, blank=True, verbose_name="文件大小", help_text="文件大小")
  285. md5sum = models.CharField(max_length=36, blank=True, verbose_name="文件md5", help_text="文件md5")
  286. @classmethod
  287. def save_file(cls, request, file_path, file_name, mime_type):
  288. # 保存到File model中
  289. instance = FileList()
  290. instance.name = file_name
  291. instance.engine = dispatch.get_system_config_values("file_storage.file_engine") or 'local'
  292. instance.file_url = os.path.join(file_path, file_name)
  293. instance.mime_type = mime_type
  294. instance.creator = request.user
  295. instance.modifier = request.user.id
  296. instance.dept_belong_id = request.user.dept_id
  297. file_backup = dispatch.get_system_config_values("file_storage.file_backup")
  298. file_engine = dispatch.get_system_config_values("file_storage.file_engine") or 'local'
  299. if file_backup:
  300. instance.url = os.path.join(file_path.replace('media/', ''), file_name)
  301. if file_engine == 'oss':
  302. from dvadmin_cloud_storage.views.aliyun import ali_oss_upload
  303. with open(os.path.join(BASE_DIR, file_path, file_name), 'rb') as file:
  304. file_path = ali_oss_upload(file, file_name=os.path.join(file_path.replace('media/', ''), file_name))
  305. if file_path:
  306. instance.file_url = file_path
  307. else:
  308. raise ValueError("上传失败")
  309. elif file_engine == 'cos':
  310. from dvadmin_cloud_storage.views.tencent import tencent_cos_upload
  311. with open(os.path.join(BASE_DIR, file_path, file_name), 'rb') as file:
  312. file_path = tencent_cos_upload(file, file_name=os.path.join(file_path.replace('media/', ''), file_name))
  313. if file_path:
  314. instance.file_url = file_path
  315. else:
  316. raise ValueError("上传失败")
  317. else:
  318. instance.url = os.path.join(file_path.replace('media/', ''), file_name)
  319. instance.save()
  320. return instance
  321. def save(self, *args, **kwargs):
  322. if not self.md5sum and self.url: # file is new
  323. md5 = hashlib.md5()
  324. for chunk in self.url.chunks():
  325. md5.update(chunk)
  326. self.md5sum = md5.hexdigest()
  327. if not self.size and self.url:
  328. self.size = self.url.size
  329. if not self.file_url:
  330. url = media_file_name(self, self.name)
  331. self.file_url = f'media/{url}'
  332. super(FileList, self).save(*args, **kwargs)
  333. class Meta:
  334. db_table = table_prefix + "system_file_list"
  335. verbose_name = "文件管理"
  336. verbose_name_plural = verbose_name
  337. ordering = ("-create_datetime",)
  338. class Area(CoreModel):
  339. name = models.CharField(max_length=100, verbose_name="名称", help_text="名称")
  340. code = models.CharField(max_length=20, verbose_name="地区编码", help_text="地区编码", unique=True, db_index=True)
  341. level = models.BigIntegerField(verbose_name="地区层级(1省份 2城市 3区县 4乡级)",
  342. help_text="地区层级(1省份 2城市 3区县 4乡级)")
  343. pinyin = models.CharField(max_length=255, verbose_name="拼音", help_text="拼音")
  344. initials = models.CharField(max_length=20, verbose_name="首字母", help_text="首字母")
  345. enable = models.BooleanField(default=True, verbose_name="是否启用", help_text="是否启用")
  346. pcode = models.ForeignKey(
  347. to="self",
  348. verbose_name="父地区编码",
  349. to_field="code",
  350. on_delete=models.PROTECT,
  351. db_constraint=False,
  352. null=True,
  353. blank=True,
  354. help_text="父地区编码",
  355. )
  356. class Meta:
  357. db_table = table_prefix + "system_area"
  358. verbose_name = "地区表"
  359. verbose_name_plural = verbose_name
  360. ordering = ("code",)
  361. def __str__(self):
  362. return f"{self.name}"
  363. class ApiWhiteList(CoreModel):
  364. url = models.CharField(max_length=200, help_text="url地址", verbose_name="url")
  365. METHOD_CHOICES = (
  366. (0, "GET"),
  367. (1, "POST"),
  368. (2, "PUT"),
  369. (3, "DELETE"),
  370. )
  371. method = models.IntegerField(default=0, verbose_name="接口请求方法", null=True, blank=True,
  372. help_text="接口请求方法")
  373. enable_datasource = models.BooleanField(default=True, verbose_name="激活数据权限", help_text="激活数据权限",
  374. blank=True)
  375. class Meta:
  376. db_table = table_prefix + "api_white_list"
  377. verbose_name = "接口白名单"
  378. verbose_name_plural = verbose_name
  379. ordering = ("-create_datetime",)
  380. class SystemConfig(CoreModel):
  381. parent = models.ForeignKey(
  382. to="self",
  383. verbose_name="父级",
  384. on_delete=models.PROTECT,
  385. db_constraint=False,
  386. null=True,
  387. blank=True,
  388. help_text="父级",
  389. )
  390. title = models.CharField(max_length=50, verbose_name="标题", help_text="标题")
  391. key = models.CharField(max_length=200, verbose_name="键", help_text="键", db_index=True)
  392. value = models.JSONField(max_length=500, verbose_name="值", help_text="值", null=True, blank=True)
  393. sort = models.IntegerField(default=0, verbose_name="排序", help_text="排序", blank=True)
  394. status = models.BooleanField(default=True, verbose_name="启用状态", help_text="启用状态")
  395. data_options = models.JSONField(verbose_name="数据options", help_text="数据options", null=True, blank=True)
  396. FORM_ITEM_TYPE_LIST = (
  397. (0, "text"),
  398. (1, "datetime"),
  399. (2, "date"),
  400. (3, "textarea"),
  401. (4, "select"),
  402. (5, "checkbox"),
  403. (6, "radio"),
  404. (7, "img"),
  405. (8, "file"),
  406. (9, "switch"),
  407. (10, "number"),
  408. (11, "array"),
  409. (12, "imgs"),
  410. (13, "foreignkey"),
  411. (14, "manytomany"),
  412. (15, "time"),
  413. )
  414. form_item_type = models.IntegerField(
  415. choices=FORM_ITEM_TYPE_LIST, verbose_name="表单类型", help_text="表单类型", default=0, blank=True
  416. )
  417. rule = models.JSONField(null=True, blank=True, verbose_name="校验规则", help_text="校验规则")
  418. placeholder = models.CharField(max_length=100, null=True, blank=True, verbose_name="提示信息", help_text="提示信息")
  419. setting = models.JSONField(null=True, blank=True, verbose_name="配置", help_text="配置")
  420. class Meta:
  421. db_table = table_prefix + "system_config"
  422. verbose_name = "系统配置表"
  423. verbose_name_plural = verbose_name
  424. ordering = ("sort",)
  425. unique_together = (("key", "parent_id"),)
  426. def __str__(self):
  427. return f"{self.title}"
  428. def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
  429. # from application.websocketConfig import websocket_push
  430. # websocket_push("dvadmin", message={"sender": 'system', "contentType": 'SYSTEM',
  431. # "content": '系统配置有变化~', "systemConfig": True})
  432. super().save(force_insert, force_update, using, update_fields)
  433. dispatch.refresh_system_config() # 有更新则刷新系统配置
  434. def delete(self, using=None, keep_parents=False):
  435. res = super().delete(using, keep_parents)
  436. dispatch.refresh_system_config()
  437. from application.websocketConfig import websocket_push
  438. websocket_push("dvadmin", message={"sender": 'system', "contentType": 'SYSTEM',
  439. "content": '系统配置有变化~', "systemConfig": True})
  440. return res
  441. class LoginLog(CoreModel):
  442. LOGIN_TYPE_CHOICES = (
  443. (1, "普通登录"),
  444. (2, "普通扫码登录"),
  445. (3, "微信扫码登录"),
  446. (4, "飞书扫码登录"),
  447. (5, "钉钉扫码登录"),
  448. (6, "短信登录")
  449. )
  450. username = models.CharField(max_length=150, verbose_name="登录用户名", null=True, blank=True,
  451. help_text="登录用户名")
  452. ip = models.CharField(max_length=32, verbose_name="登录ip", null=True, blank=True, help_text="登录ip")
  453. agent = models.TextField(verbose_name="agent信息", null=True, blank=True, help_text="agent信息")
  454. browser = models.CharField(max_length=200, verbose_name="浏览器名", null=True, blank=True, help_text="浏览器名")
  455. os = models.CharField(max_length=200, verbose_name="操作系统", null=True, blank=True, help_text="操作系统")
  456. continent = models.CharField(max_length=50, verbose_name="州", null=True, blank=True, help_text="州")
  457. country = models.CharField(max_length=50, verbose_name="国家", null=True, blank=True, help_text="国家")
  458. province = models.CharField(max_length=50, verbose_name="省份", null=True, blank=True, help_text="省份")
  459. city = models.CharField(max_length=50, verbose_name="城市", null=True, blank=True, help_text="城市")
  460. district = models.CharField(max_length=50, verbose_name="县区", null=True, blank=True, help_text="县区")
  461. isp = models.CharField(max_length=50, verbose_name="运营商", null=True, blank=True, help_text="运营商")
  462. area_code = models.CharField(max_length=50, verbose_name="区域代码", null=True, blank=True, help_text="区域代码")
  463. country_english = models.CharField(max_length=50, verbose_name="英文全称", null=True, blank=True,
  464. help_text="英文全称")
  465. country_code = models.CharField(max_length=50, verbose_name="简称", null=True, blank=True, help_text="简称")
  466. longitude = models.CharField(max_length=50, verbose_name="经度", null=True, blank=True, help_text="经度")
  467. latitude = models.CharField(max_length=50, verbose_name="纬度", null=True, blank=True, help_text="纬度")
  468. login_type = models.IntegerField(default=1, choices=LOGIN_TYPE_CHOICES, verbose_name="登录类型",
  469. help_text="登录类型")
  470. class Meta:
  471. db_table = table_prefix + "system_login_log"
  472. verbose_name = "登录日志"
  473. verbose_name_plural = verbose_name
  474. ordering = ("-create_datetime",)
  475. class MessageCenter(CoreModel):
  476. title = models.CharField(max_length=100, verbose_name="标题", help_text="标题")
  477. content = models.TextField(verbose_name="内容", help_text="内容")
  478. target_type = models.IntegerField(default=0, verbose_name="目标类型", help_text="目标类型")
  479. target_user = models.ManyToManyField(to=Users, related_name='user', through='MessageCenterTargetUser',
  480. through_fields=('messagecenter', 'users'), blank=True, verbose_name="目标用户",
  481. help_text="目标用户")
  482. target_dept = models.ManyToManyField(to=Dept, blank=True, db_constraint=False,
  483. verbose_name="目标部门", help_text="目标部门")
  484. target_role = models.ManyToManyField(to=Role, blank=True, db_constraint=False,
  485. verbose_name="目标角色", help_text="目标角色")
  486. class Meta:
  487. db_table = table_prefix + "message_center"
  488. verbose_name = "消息中心"
  489. verbose_name_plural = verbose_name
  490. ordering = ("-create_datetime",)
  491. class MessageCenterTargetUser(CoreModel):
  492. users = models.ForeignKey(Users, related_name="target_user", on_delete=models.CASCADE, db_constraint=False,
  493. verbose_name="关联用户表", help_text="关联用户表")
  494. messagecenter = models.ForeignKey(MessageCenter, on_delete=models.CASCADE, db_constraint=False,
  495. verbose_name="关联消息中心表", help_text="关联消息中心表")
  496. is_read = models.BooleanField(default=False, blank=True, null=True, verbose_name="是否已读", help_text="是否已读")
  497. class Meta:
  498. db_table = table_prefix + "message_center_target_user"
  499. verbose_name = "消息中心目标用户表"
  500. verbose_name_plural = verbose_name