瀏覽代碼

feat: 收款账户选项只返回当前登记人并带上所属人姓名

发起校验能识别已删除、他租户和他人账户,编辑账户不会改掉 create_by。

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu 1 周之前
父節點
當前提交
7aa685840f
共有 3 個文件被更改,包括 87 次插入 和 9 次删除
  1. 9 1
      backend/app/api/oa_approval.py
  2. 32 8
      backend/app/crud/oa_receive_account.py
  3. 46 0
      backend/app/services/oa_receive_account.py

+ 9 - 1
backend/app/api/oa_approval.py

@@ -84,7 +84,15 @@ async def list_receive_account_options(
     db: Session = Depends(get_db),
 ):
     tenant_no = _get_tenant_no(db, user)
-    return success_response({"list": ora_crud.list_options(db, tenant_no=tenant_no)})
+    return success_response(
+        {
+            "list": ora_crud.list_options(
+                db,
+                tenant_no=tenant_no,
+                user_id=user.get("user_id") or "",
+            )
+        }
+    )
 
 
 @router.post("/receive-accounts/batch-delete")

+ 32 - 8
backend/app/crud/oa_receive_account.py

@@ -8,7 +8,7 @@ from sqlalchemy import func, or_
 from sqlalchemy.orm import Session
 
 from app.core.utils import generate_uuid
-from app.models.models import OaReceiveAccount
+from app.models.models import OaReceiveAccount, SysUser
 
 
 def row_to_dict(r: OaReceiveAccount) -> Dict[str, Any]:
@@ -26,9 +26,26 @@ def row_to_dict(r: OaReceiveAccount) -> Dict[str, Any]:
         "remark": r.remark or "",
         "createTime": r.create_time.strftime("%Y-%m-%d %H:%M:%S") if r.create_time else "",
         "updateTime": r.update_time.strftime("%Y-%m-%d %H:%M:%S") if r.update_time else "",
+        "ownerUserId": (r.create_by or "").strip(),
     }
 
 
+def _owner_name_map(db: Session, user_ids: List[str]) -> Dict[str, str]:
+    ids = [item for item in {uid.strip() for uid in user_ids if uid and uid.strip()}]
+    if not ids:
+        return {}
+    rows = db.query(SysUser.id, SysUser.nick_name).filter(SysUser.id.in_(ids)).all()
+    return {row.id: (row.nick_name or "").strip() for row in rows}
+
+
+def _attach_owner_names(db: Session, items: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
+    names = _owner_name_map(db, [item.get("ownerUserId") or "" for item in items])
+    for item in items:
+        uid = item.get("ownerUserId") or ""
+        item["ownerName"] = names.get(uid) or "—"
+    return items
+
+
 def _filtered_query(
     db: Session,
     *,
@@ -74,17 +91,24 @@ def list_accounts(
         .limit(page_size)
         .all()
     )
-    return [row_to_dict(x) for x in rows], int(total)
+    return _attach_owner_names(db, [row_to_dict(x) for x in rows]), int(total)
 
 
-def list_options(db: Session, *, tenant_no: str) -> List[Dict[str, Any]]:
+def list_options(db: Session, *, tenant_no: str, user_id: str) -> List[Dict[str, Any]]:
+    owner_id = (user_id or "").strip()
+    if not owner_id:
+        return []
     rows = (
         db.query(OaReceiveAccount)
-        .filter(OaReceiveAccount.tenant_no == tenant_no, OaReceiveAccount.del_flag == 0)
+        .filter(
+            OaReceiveAccount.tenant_no == tenant_no,
+            OaReceiveAccount.del_flag == 0,
+            func.trim(OaReceiveAccount.create_by) == owner_id,
+        )
         .order_by(OaReceiveAccount.create_time.desc())
         .all()
     )
-    return [row_to_dict(x) for x in rows]
+    return _attach_owner_names(db, [row_to_dict(x) for x in rows])
 
 
 def get_account(db: Session, account_id: str, tenant_no: str) -> Optional[Dict[str, Any]]:
@@ -97,7 +121,7 @@ def get_account(db: Session, account_id: str, tenant_no: str) -> Optional[Dict[s
         )
         .first()
     )
-    return row_to_dict(row) if row else None
+    return _attach_owner_names(db, [row_to_dict(row)])[0] if row else None
 
 
 def create_account(
@@ -131,7 +155,7 @@ def create_account(
     db.add(obj)
     db.commit()
     db.refresh(obj)
-    return row_to_dict(obj)
+    return _attach_owner_names(db, [row_to_dict(obj)])[0]
 
 
 def update_account(
@@ -168,7 +192,7 @@ def update_account(
     db.add(obj)
     db.commit()
     db.refresh(obj)
-    return row_to_dict(obj)
+    return _attach_owner_names(db, [row_to_dict(obj)])[0]
 
 
 def soft_delete_account(

+ 46 - 0
backend/app/services/oa_receive_account.py

@@ -3,6 +3,10 @@ from __future__ import annotations
 
 from typing import Any, Dict, List, Optional, Tuple
 
+from sqlalchemy.orm import Session
+
+from app.models.models import OaReceiveAccount
+
 ACCOUNT_TYPES = frozenset({"alipay", "personal", "corporate"})
 
 _BANK_FIELD_ALIASES = {
@@ -199,3 +203,45 @@ def validate_receive_account_form_values(
             continue
         errors.extend(_validate_receive_account_rule(rule, data))
     return errors
+
+
+def validate_receive_account_ownership(
+    db: Session,
+    schema: Any,
+    form_data: Optional[Dict[str, Any]],
+    *,
+    tenant_no: str,
+    initiator_user_id: str,
+) -> List[str]:
+    """校验收款账户快照 id 属于发起人。调用前须已通过格式校验。"""
+    data = form_data or {}
+    owner_id = (initiator_user_id or "").strip()
+    for rule in _schema_rules(schema):
+        if str(rule.get("type") or "") != "receiveAccount":
+            continue
+        field = str(rule.get("field") or "").strip()
+        if not field:
+            continue
+        value = data.get(field)
+        if not isinstance(value, list):
+            continue
+        label = _field_label(rule)
+        for item in value:
+            if not isinstance(item, dict):
+                continue
+            item_id = str(item.get("id") or "").strip()
+            if not item_id:
+                continue
+            row = (
+                db.query(OaReceiveAccount)
+                .filter(
+                    OaReceiveAccount.id == item_id,
+                    OaReceiveAccount.tenant_no == tenant_no,
+                )
+                .first()
+            )
+            if row is None or int(row.del_flag or 0) == 1:
+                return [f"「{label}」不存在或已删除"]
+            if (row.create_by or "").strip() != owner_id:
+                return [f"「{label}」不属于当前用户"]
+    return []