users.py 4.1 KB

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