|
|
@@ -3,6 +3,9 @@ from __future__ import annotations
|
|
|
|
|
|
from typing import Any, Dict, List, Optional, Sequence, Tuple
|
|
|
|
|
|
+from app.models.models import OaApp, OaAppVersion, OaInstance
|
|
|
+from app.services.oa_engine import OaEngineError, _loads
|
|
|
+
|
|
|
LAYOUT_SKIP = frozenset(
|
|
|
{"fcRow", "fcTable", "col", "row", "elCard", "space", "divider", "html", "title", "text", "tag"}
|
|
|
)
|
|
|
@@ -112,3 +115,146 @@ def extract_related_approval_links(
|
|
|
seen.add(key)
|
|
|
links.append((field, tid))
|
|
|
return links
|
|
|
+
|
|
|
+
|
|
|
+def assert_linkable_instance(inst: OaInstance, *, user_id: str, tenant_no: str) -> None:
|
|
|
+ if not inst or inst.del_flag != 0 or inst.tenant_no != tenant_no:
|
|
|
+ raise OaEngineError("关联的审批单不存在")
|
|
|
+ if (inst.initiator_user_id or "") != user_id:
|
|
|
+ raise OaEngineError("只能关联本人发起的审批单")
|
|
|
+ if (inst.status or "") != "completed":
|
|
|
+ raise OaEngineError("只能关联已通过的审批单")
|
|
|
+
|
|
|
+
|
|
|
+def list_related_candidates(
|
|
|
+ db,
|
|
|
+ *,
|
|
|
+ tenant_no: str,
|
|
|
+ user_id: str,
|
|
|
+ keyword: str = "",
|
|
|
+ page: int = 1,
|
|
|
+ page_size: int = 20,
|
|
|
+) -> Dict[str, Any]:
|
|
|
+ q = db.query(OaInstance).filter(
|
|
|
+ OaInstance.tenant_no == tenant_no,
|
|
|
+ OaInstance.del_flag == 0,
|
|
|
+ OaInstance.initiator_user_id == user_id,
|
|
|
+ OaInstance.status == "completed",
|
|
|
+ )
|
|
|
+ kw = (keyword or "").strip()
|
|
|
+ if kw:
|
|
|
+ from sqlalchemy import or_
|
|
|
+
|
|
|
+ app_ids = [
|
|
|
+ row[0]
|
|
|
+ for row in db.query(OaApp.id)
|
|
|
+ .filter(
|
|
|
+ OaApp.tenant_no == tenant_no,
|
|
|
+ OaApp.del_flag == 0,
|
|
|
+ OaApp.app_name.like(f"%{kw}%"),
|
|
|
+ )
|
|
|
+ .all()
|
|
|
+ ]
|
|
|
+ clauses = [OaInstance.title.like(f"%{kw}%")]
|
|
|
+ if app_ids:
|
|
|
+ clauses.append(OaInstance.app_id.in_(app_ids))
|
|
|
+ q = q.filter(or_(*clauses))
|
|
|
+ total = q.count()
|
|
|
+ page = max(1, int(page or 1))
|
|
|
+ page_size = min(100, max(1, int(page_size or 20)))
|
|
|
+ rows = (
|
|
|
+ q.order_by(OaInstance.create_time.desc())
|
|
|
+ .offset((page - 1) * page_size)
|
|
|
+ .limit(page_size)
|
|
|
+ .all()
|
|
|
+ )
|
|
|
+ app_map = {}
|
|
|
+ ids = list({r.app_id for r in rows if r.app_id})
|
|
|
+ if ids:
|
|
|
+ for a in db.query(OaApp).filter(OaApp.id.in_(ids)).all():
|
|
|
+ app_map[a.id] = a.app_name or ""
|
|
|
+ items = []
|
|
|
+ for r in rows:
|
|
|
+ items.append(
|
|
|
+ {
|
|
|
+ "id": r.id,
|
|
|
+ "appName": app_map.get(r.app_id, ""),
|
|
|
+ "title": r.title or "",
|
|
|
+ "applyTime": r.create_time.strftime("%Y-%m-%d %H:%M:%S") if r.create_time else "",
|
|
|
+ "status": r.status or "",
|
|
|
+ }
|
|
|
+ )
|
|
|
+ return {"list": items, "total": total, "page": page, "pageSize": page_size}
|
|
|
+
|
|
|
+
|
|
|
+def build_related_summaries(
|
|
|
+ db,
|
|
|
+ *,
|
|
|
+ tenant_no: str,
|
|
|
+ user_id: str,
|
|
|
+ ids: List[str],
|
|
|
+ mode: str = "picker",
|
|
|
+) -> List[Dict[str, Any]]:
|
|
|
+ mode = (mode or "picker").strip()
|
|
|
+ out: List[Dict[str, Any]] = []
|
|
|
+ clean = [str(i).strip() for i in ids if str(i).strip()]
|
|
|
+ if not clean:
|
|
|
+ return out
|
|
|
+ insts = (
|
|
|
+ db.query(OaInstance)
|
|
|
+ .filter(
|
|
|
+ OaInstance.tenant_no == tenant_no,
|
|
|
+ OaInstance.del_flag == 0,
|
|
|
+ OaInstance.id.in_(clean),
|
|
|
+ )
|
|
|
+ .all()
|
|
|
+ )
|
|
|
+ by_id = {i.id: i for i in insts}
|
|
|
+ for iid in clean:
|
|
|
+ inst = by_id.get(iid)
|
|
|
+ if not inst:
|
|
|
+ out.append(
|
|
|
+ {
|
|
|
+ "id": iid,
|
|
|
+ "unavailable": True,
|
|
|
+ "appName": "",
|
|
|
+ "applyTime": "",
|
|
|
+ "status": "",
|
|
|
+ "fields": [],
|
|
|
+ }
|
|
|
+ )
|
|
|
+ continue
|
|
|
+ if mode == "picker":
|
|
|
+ try:
|
|
|
+ assert_linkable_instance(inst, user_id=user_id, tenant_no=tenant_no)
|
|
|
+ except OaEngineError:
|
|
|
+ out.append(
|
|
|
+ {
|
|
|
+ "id": iid,
|
|
|
+ "unavailable": True,
|
|
|
+ "appName": "",
|
|
|
+ "applyTime": "",
|
|
|
+ "status": "",
|
|
|
+ "fields": [],
|
|
|
+ }
|
|
|
+ )
|
|
|
+ continue
|
|
|
+ app = db.query(OaApp).filter(OaApp.id == inst.app_id, OaApp.del_flag == 0).first()
|
|
|
+ ver = (
|
|
|
+ db.query(OaAppVersion)
|
|
|
+ .filter(OaAppVersion.id == inst.app_version_id, OaAppVersion.del_flag == 0)
|
|
|
+ .first()
|
|
|
+ )
|
|
|
+ schema = _loads(ver.form_schema_json, {}) if ver else {}
|
|
|
+ form_data = _loads(inst.form_data_json, {}) if inst.form_data_json else {}
|
|
|
+ out.append(
|
|
|
+ {
|
|
|
+ "id": inst.id,
|
|
|
+ "unavailable": False,
|
|
|
+ "appName": (app.app_name if app else "") or (inst.title or ""),
|
|
|
+ "applyTime": inst.create_time.strftime("%Y-%m-%d %H:%M:%S") if inst.create_time else "",
|
|
|
+ "status": inst.status or "",
|
|
|
+ "fields": flatten_form_fields_for_summary(schema, form_data),
|
|
|
+ }
|
|
|
+ )
|
|
|
+ return out
|