qc_task_assignee.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. """质检任务负责人 CRUD"""
  2. from __future__ import annotations
  3. from datetime import datetime
  4. from typing import Dict, List, Optional, Set
  5. from sqlalchemy.orm import Session
  6. from app.core.db_collation import eq_unicode
  7. from app.core.utils import generate_uuid
  8. from app.models.models import BizQcTaskAssignee, SysUser
  9. def _active_users_in_tenant(
  10. db: Session, user_ids: List[str], tenant_no: str
  11. ) -> Set[str]:
  12. ids = [x.strip() for x in user_ids if (x or "").strip()]
  13. if not ids:
  14. return set()
  15. rows = (
  16. db.query(SysUser.id)
  17. .filter(
  18. SysUser.id.in_(ids),
  19. SysUser.tenant_no == tenant_no,
  20. SysUser.del_flag == 0,
  21. SysUser.status == "1",
  22. )
  23. .all()
  24. )
  25. return {r[0] for r in rows}
  26. def validate_assignee_user_ids(
  27. db: Session, user_ids: List[str], tenant_no: str
  28. ) -> List[str]:
  29. """去重并校验用户有效,返回有序列表。"""
  30. seen: Set[str] = set()
  31. ordered: List[str] = []
  32. for uid in user_ids:
  33. u = (uid or "").strip()
  34. if not u or u in seen:
  35. continue
  36. seen.add(u)
  37. ordered.append(u)
  38. if not ordered:
  39. raise ValueError("请至少指定一名负责人")
  40. valid = _active_users_in_tenant(db, ordered, tenant_no)
  41. invalid = [u for u in ordered if u not in valid]
  42. if invalid:
  43. raise ValueError("存在无效或已停用的负责人用户")
  44. return ordered
  45. def sync_assignees_for_task(
  46. db: Session,
  47. task_id: str,
  48. user_ids: List[str],
  49. *,
  50. tenant_no: str,
  51. operator_id: Optional[str] = None,
  52. create_dept_id: str = "",
  53. ) -> None:
  54. """全量替换任务负责人(软删旧记录 + 插入新记录)。"""
  55. normalized = validate_assignee_user_ids(db, user_ids, tenant_no)
  56. now = datetime.now()
  57. existing = (
  58. db.query(BizQcTaskAssignee)
  59. .filter(
  60. BizQcTaskAssignee.task_id == task_id,
  61. BizQcTaskAssignee.tenant_no == tenant_no,
  62. BizQcTaskAssignee.del_flag == 0,
  63. )
  64. .all()
  65. )
  66. keep_ids = set(normalized)
  67. for row in existing:
  68. if row.user_id not in keep_ids:
  69. row.del_flag = 1
  70. row.update_by = operator_id
  71. row.update_time = now
  72. db.add(row)
  73. existing_map = {r.user_id: r for r in existing if r.del_flag == 0}
  74. for idx, uid in enumerate(normalized):
  75. if uid in existing_map:
  76. row = existing_map[uid]
  77. row.order_num = idx + 1
  78. row.update_by = operator_id
  79. row.update_time = now
  80. db.add(row)
  81. continue
  82. db.add(
  83. BizQcTaskAssignee(
  84. id=generate_uuid(),
  85. task_id=task_id,
  86. user_id=uid,
  87. tenant_no=tenant_no,
  88. order_num=idx + 1,
  89. create_by=operator_id,
  90. create_dept_id=create_dept_id or "",
  91. create_time=now,
  92. update_by=operator_id,
  93. update_time=now,
  94. del_flag=0,
  95. )
  96. )
  97. def soft_delete_assignees_by_task_ids(
  98. db: Session, task_ids: List[str], tenant_no: str
  99. ) -> None:
  100. if not task_ids:
  101. return
  102. now = datetime.now()
  103. rows = (
  104. db.query(BizQcTaskAssignee)
  105. .filter(
  106. BizQcTaskAssignee.task_id.in_(task_ids),
  107. BizQcTaskAssignee.tenant_no == tenant_no,
  108. BizQcTaskAssignee.del_flag == 0,
  109. )
  110. .all()
  111. )
  112. for row in rows:
  113. row.del_flag = 1
  114. row.update_time = now
  115. db.add(row)
  116. def list_assignees_by_task_ids(
  117. db: Session, task_ids: List[str], tenant_no: str
  118. ) -> Dict[str, List[Dict[str, str]]]:
  119. if not task_ids:
  120. return {}
  121. rows = (
  122. db.query(BizQcTaskAssignee, SysUser)
  123. .outerjoin(SysUser, eq_unicode(SysUser.id, BizQcTaskAssignee.user_id))
  124. .filter(
  125. BizQcTaskAssignee.task_id.in_(task_ids),
  126. BizQcTaskAssignee.tenant_no == tenant_no,
  127. BizQcTaskAssignee.del_flag == 0,
  128. )
  129. .order_by(BizQcTaskAssignee.order_num.asc(), BizQcTaskAssignee.create_time.asc())
  130. .all()
  131. )
  132. out: Dict[str, List[Dict[str, str]]] = {}
  133. for assignee, user in rows:
  134. out.setdefault(assignee.task_id, []).append(
  135. {
  136. "userId": assignee.user_id,
  137. "userName": (user.nick_name or user.user_no or assignee.user_id)
  138. if user
  139. else assignee.user_id,
  140. }
  141. )
  142. return out
  143. def is_user_assignee(db: Session, task_id: str, user_id: str, tenant_no: str) -> bool:
  144. if not task_id or not user_id:
  145. return False
  146. row = (
  147. db.query(BizQcTaskAssignee.id)
  148. .filter(
  149. BizQcTaskAssignee.task_id == task_id,
  150. BizQcTaskAssignee.user_id == user_id,
  151. BizQcTaskAssignee.tenant_no == tenant_no,
  152. BizQcTaskAssignee.del_flag == 0,
  153. )
  154. .first()
  155. )
  156. return row is not None
  157. def enrich_tasks_with_assignees(
  158. db: Session, items: List[dict], tenant_no: str
  159. ) -> List[dict]:
  160. if not items:
  161. return items
  162. task_ids = [t.get("id") for t in items if t.get("id")]
  163. mapping = list_assignees_by_task_ids(db, task_ids, tenant_no)
  164. for item in items:
  165. rows = mapping.get(item.get("id") or "", [])
  166. item["assigneeUserIds"] = [r["userId"] for r in rows]
  167. item["assigneeUserNames"] = "、".join(r["userName"] for r in rows if r.get("userName"))
  168. return items