users.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. """当前用户、用户分页查询与用户维护写操作。"""
  2. from __future__ import annotations
  3. from flask import request
  4. from sqlalchemy import select
  5. from dms.api.v1.blueprint import api_v1
  6. from dms.common.errors import AuthenticationError, InvalidArgumentError
  7. from dms.common.pagination import PageRequest
  8. from dms.common.response import success_response
  9. from dms.extensions import db
  10. from dms.models import User
  11. from dms.security.auth_context import get_auth_context
  12. from dms.security.decorators import admin_required, bearer_auth_required
  13. from dms.services.user_service import (
  14. allowed_modules_for_user,
  15. create_user,
  16. delete_user,
  17. list_users,
  18. parse_string_id,
  19. reorder_users,
  20. reset_user_password,
  21. update_ui_theme,
  22. update_user,
  23. user_summary,
  24. )
  25. def _integer_query(name: str, default: int) -> int:
  26. raw_value = request.args.get(name)
  27. if raw_value is None or raw_value == "":
  28. return default
  29. try:
  30. return int(raw_value)
  31. except ValueError as exc:
  32. raise InvalidArgumentError(f"{name}必须是整数") from exc
  33. def _boolean_query(name: str, default: bool) -> bool:
  34. raw_value = request.args.get(name)
  35. if raw_value is None or raw_value == "":
  36. return default
  37. normalized = raw_value.lower()
  38. if normalized not in {"true", "false"}:
  39. raise InvalidArgumentError(f"{name}必须是true或false")
  40. return normalized == "true"
  41. def _json_body():
  42. if not request.is_json:
  43. raise InvalidArgumentError("请求体必须是JSON对象")
  44. body = request.get_json(silent=True)
  45. if not isinstance(body, dict):
  46. raise InvalidArgumentError("请求体必须是JSON对象")
  47. return body
  48. def _parse_user_id(value: str) -> int:
  49. if not value.isdecimal() or int(value) <= 0:
  50. raise InvalidArgumentError("id必须是正整数形式的字符串ID")
  51. return int(value)
  52. def _parse_delete_row_version(value: str | None) -> int:
  53. if value is None or not value.isdecimal():
  54. raise InvalidArgumentError("rowVersion必须是非负整数")
  55. return int(value)
  56. @api_v1.get("/users/me")
  57. @bearer_auth_required
  58. def current_user():
  59. context = get_auth_context()
  60. user = db.session.scalar(
  61. select(User).where(User.id == context.user_id, User.is_deleted.is_(False))
  62. )
  63. if user is None:
  64. raise AuthenticationError()
  65. # 会话自描述接口:模块可见性按该用户对文档的实际权限动态计算。
  66. return success_response(
  67. user_summary(user, allowed_modules=allowed_modules_for_user(user))
  68. )
  69. @api_v1.put("/users/me/ui-theme")
  70. @bearer_auth_required
  71. def update_ui_theme_route():
  72. # 本人主题偏好(契约1.20):无需管理权限,任意登录用户可改自己。
  73. return success_response(update_ui_theme(_json_body()))
  74. @api_v1.get("/users")
  75. @bearer_auth_required
  76. @admin_required
  77. def users():
  78. page_request = PageRequest(
  79. page=_integer_query("page", 1),
  80. page_size=_integer_query("pageSize", 20),
  81. )
  82. data = list_users(
  83. organization_id=parse_string_id(
  84. request.args.get("organizationId"),
  85. name="organizationId",
  86. ),
  87. include_descendants=_boolean_query("includeDescendants", False),
  88. keyword=request.args.get("keyword"),
  89. status=request.args.get("status"),
  90. page_request=page_request,
  91. )
  92. return success_response(data)
  93. @api_v1.post("/users")
  94. @bearer_auth_required
  95. @admin_required
  96. def create_user_route():
  97. return success_response(create_user(_json_body()), status=201)
  98. @api_v1.put("/users/reorder")
  99. @bearer_auth_required
  100. @admin_required
  101. def reorder_users_route():
  102. return success_response(reorder_users(_json_body()))
  103. @api_v1.put("/users/<id>")
  104. @bearer_auth_required
  105. @admin_required
  106. def update_user_route(id: str):
  107. return success_response(update_user(_parse_user_id(id), _json_body()))
  108. @api_v1.delete("/users/<id>")
  109. @bearer_auth_required
  110. @admin_required
  111. def delete_user_route(id: str):
  112. return success_response(
  113. delete_user(
  114. _parse_user_id(id),
  115. _parse_delete_row_version(request.args.get("rowVersion")),
  116. )
  117. )
  118. @api_v1.post("/users/<id>/reset-password")
  119. @bearer_auth_required
  120. @admin_required
  121. def reset_user_password_route(id: str):
  122. return success_response(
  123. reset_user_password(_parse_user_id(id), _json_body())
  124. )