organizations.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import logging
  2. from typing import List, Any, Dict
  3. from fastapi import APIRouter, Depends, HTTPException, Request
  4. from sqlalchemy.orm import Session
  5. from sqlalchemy.exc import IntegrityError
  6. from app.api.v1 import deps
  7. from app.core.utils import get_client_ip
  8. from app.models.user import User
  9. from app.models.organization import Organization
  10. from app.schemas.organization import (
  11. OrganizationCreate,
  12. OrganizationUpdate,
  13. OrganizationResponse,
  14. )
  15. from app.services.captcha_service import CaptchaService
  16. from app.services.log_service import LogService
  17. from app.schemas.operation_log import ActionType
  18. router = APIRouter()
  19. logger = logging.getLogger(__name__)
  20. @router.get("/", response_model=List[OrganizationResponse], summary="组织列表")
  21. def list_organizations(
  22. db: Session = Depends(deps.get_db),
  23. current_user: User = Depends(deps.get_current_active_user),
  24. ):
  25. """扁平组织列表(登录用户可拉取下拉选项)。"""
  26. return (
  27. db.query(Organization)
  28. .order_by(Organization.sort_order.asc(), Organization.id.asc())
  29. .all()
  30. )
  31. @router.post("/", response_model=OrganizationResponse, summary="创建组织")
  32. def create_organization(
  33. body: OrganizationCreate,
  34. request: Request,
  35. db: Session = Depends(deps.get_db),
  36. current_user: User = Depends(deps.get_current_active_user),
  37. ):
  38. if current_user.role != "SUPER_ADMIN":
  39. raise HTTPException(status_code=403, detail="权限不足")
  40. org = Organization(
  41. name=body.name.strip(),
  42. description=body.description,
  43. sort_order=body.sort_order,
  44. )
  45. db.add(org)
  46. try:
  47. db.commit()
  48. db.refresh(org)
  49. except IntegrityError:
  50. db.rollback()
  51. raise HTTPException(status_code=400, detail="组织名称已存在")
  52. LogService.create_log(
  53. db=db,
  54. operator_id=current_user.id,
  55. action_type=ActionType.ORG_CREATE,
  56. target_user_id=None,
  57. target_mobile=None,
  58. ip_address=get_client_ip(request),
  59. details={
  60. "resource": "organization",
  61. "organization_id": org.id,
  62. "name": org.name,
  63. "description": org.description,
  64. "sort_order": org.sort_order,
  65. },
  66. )
  67. logger.info(f"创建组织: {org.name} (Operator: {current_user.mobile})")
  68. return org
  69. @router.put("/{organization_id}", response_model=OrganizationResponse, summary="更新组织")
  70. def update_organization(
  71. organization_id: int,
  72. body: OrganizationUpdate,
  73. request: Request,
  74. db: Session = Depends(deps.get_db),
  75. current_user: User = Depends(deps.get_current_active_user),
  76. ):
  77. if current_user.role != "SUPER_ADMIN":
  78. raise HTTPException(status_code=403, detail="权限不足")
  79. if not CaptchaService.verify_captcha(body.captcha_id, body.captcha_code):
  80. logger.warning(f"更新组织失败: 验证码错误 (User: {current_user.mobile})")
  81. raise HTTPException(status_code=400, detail="验证码错误或已过期")
  82. org = db.query(Organization).filter(Organization.id == organization_id).first()
  83. if not org:
  84. raise HTTPException(status_code=404, detail="组织不存在")
  85. raw = body.model_dump(exclude_unset=True, exclude={"captcha_id", "captcha_code"})
  86. if not raw:
  87. raise HTTPException(status_code=400, detail="请至少修改一项内容")
  88. changes: Dict[str, Any] = {}
  89. if "name" in raw and raw["name"] is not None:
  90. new_name = raw["name"].strip()
  91. if new_name != org.name:
  92. changes["name"] = {"old": org.name, "new": new_name}
  93. if "description" in raw:
  94. if raw["description"] != org.description:
  95. changes["description"] = {"old": org.description, "new": raw["description"]}
  96. if "sort_order" in raw and raw["sort_order"] is not None:
  97. if raw["sort_order"] != org.sort_order:
  98. changes["sort_order"] = {"old": org.sort_order, "new": raw["sort_order"]}
  99. if not changes:
  100. raise HTTPException(status_code=400, detail="未检测到变更")
  101. if "name" in raw and raw["name"] is not None:
  102. org.name = raw["name"].strip()
  103. if "description" in raw:
  104. org.description = raw["description"]
  105. if "sort_order" in raw and raw["sort_order"] is not None:
  106. org.sort_order = raw["sort_order"]
  107. try:
  108. db.commit()
  109. db.refresh(org)
  110. except IntegrityError:
  111. db.rollback()
  112. raise HTTPException(status_code=400, detail="组织名称已存在")
  113. LogService.create_log(
  114. db=db,
  115. operator_id=current_user.id,
  116. action_type=ActionType.ORG_UPDATE,
  117. target_user_id=None,
  118. target_mobile=None,
  119. ip_address=get_client_ip(request),
  120. details={
  121. "resource": "organization",
  122. "organization_id": org.id,
  123. "changes": changes,
  124. },
  125. )
  126. logger.info(f"更新组织: {org.name} (Operator: {current_user.mobile})")
  127. return org