Files
zkxw/backend/app/crud/oa_approval.py
T

921 lines
28 KiB
Python

"""OA 审批 CRUD(工作流角色、分组、应用、草稿等)"""
from __future__ import annotations
import json
from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple
from sqlalchemy import and_
from sqlalchemy.orm import Session
from app.core.db_collation import eq_unicode
from app.core.utils import generate_uuid
from app.models.models import OaApp, OaAppGroup, OaAppVersion, OaRole, OaRoleUser, SysUser
DEFAULT_ADVANCED_SETTINGS: Dict[str, Any] = {
"supervisorFallbackToDeptLeader": True,
"allowWithdrawRunning": True,
"approverDedup": False,
"commentRequired": False,
"allowAddSign": True,
"allowReturn": True,
"autoPassWhenApproverIsInitiator": False,
}
def _dumps(data: Any) -> Optional[str]:
if data is None:
return None
if isinstance(data, str):
return data
return json.dumps(data, ensure_ascii=False)
def _loads(text: Optional[str], default: Any = None) -> Any:
if text is None or text == "":
return default
if not isinstance(text, str):
return text
try:
return json.loads(text)
except (TypeError, ValueError):
return default
PRESET_ROLES = (
("GM", "总经理"),
("CHAIRMAN", "董事长"),
)
def _role_to_dict(r: OaRole, *, member_count: int = 0) -> Dict[str, Any]:
return {
"id": r.id,
"roleName": r.role_name,
"roleCode": r.role_code,
"roleKind": r.role_kind,
"status": r.status,
"tenantNo": r.tenant_no,
"orderNum": r.order_num if r.order_num is not None else 0,
"remark": r.remark or "",
"memberCount": member_count,
"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 "",
}
def ensure_presets(db: Session, tenant_no: str, user_id: Optional[str] = None) -> List[OaRole]:
"""幂等写入预置角色 GM / CHAIRMAN。"""
now = datetime.now()
created: List[OaRole] = []
for code, name in PRESET_ROLES:
exists = (
db.query(OaRole)
.filter(
OaRole.tenant_no == tenant_no,
OaRole.role_code == code,
OaRole.del_flag == 0,
)
.first()
)
if exists:
continue
obj = OaRole(
id=generate_uuid(),
role_name=name,
role_code=code,
role_kind="preset",
status="1",
tenant_no=tenant_no,
order_num=0 if code == "GM" else 1,
remark="系统预置工作流角色",
create_by=user_id,
create_dept_id="",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(obj)
created.append(obj)
if created:
db.commit()
for obj in created:
db.refresh(obj)
return created
def _member_count_map(db: Session, role_ids: List[str], tenant_no: str) -> Dict[str, int]:
if not role_ids:
return {}
rows = (
db.query(OaRoleUser.oa_role_id, OaRoleUser.id)
.filter(
OaRoleUser.oa_role_id.in_(role_ids),
OaRoleUser.tenant_no == tenant_no,
OaRoleUser.del_flag == 0,
)
.all()
)
counts: Dict[str, int] = {rid: 0 for rid in role_ids}
for rid, _ in rows:
counts[rid] = counts.get(rid, 0) + 1
return counts
def list_roles(
db: Session,
*,
tenant_no: str,
keyword: Optional[str] = None,
status: Optional[str] = None,
) -> List[Dict[str, Any]]:
ensure_presets(db, tenant_no)
q = db.query(OaRole).filter(OaRole.tenant_no == tenant_no, OaRole.del_flag == 0)
kw = (keyword or "").strip()
if kw:
like = f"%{kw}%"
q = q.filter((OaRole.role_name.like(like)) | (OaRole.role_code.like(like)))
if status:
q = q.filter(OaRole.status == status)
rows = q.order_by(OaRole.role_kind.desc(), OaRole.order_num.asc(), OaRole.create_time.desc()).all()
counts = _member_count_map(db, [r.id for r in rows], tenant_no)
return [_role_to_dict(r, member_count=counts.get(r.id, 0)) for r in rows]
def get_role(db: Session, role_id: str, tenant_no: str) -> Optional[OaRole]:
return (
db.query(OaRole)
.filter(OaRole.id == role_id, OaRole.tenant_no == tenant_no, OaRole.del_flag == 0)
.first()
)
def create_role(
db: Session,
*,
tenant_no: str,
role_name: str,
role_code: str,
status: str = "1",
order_num: Optional[int] = None,
remark: Optional[str] = None,
user_id: Optional[str] = None,
) -> OaRole:
name = (role_name or "").strip()
code = (role_code or "").strip().upper()
if not name:
raise ValueError("角色名称不能为空")
if not code:
raise ValueError("角色编码不能为空")
if code in {c for c, _ in PRESET_ROLES}:
raise ValueError("该编码为系统预置,请勿占用")
exists = (
db.query(OaRole)
.filter(OaRole.tenant_no == tenant_no, OaRole.role_code == code, OaRole.del_flag == 0)
.first()
)
if exists:
raise ValueError("角色编码已存在")
now = datetime.now()
obj = OaRole(
id=generate_uuid(),
role_name=name,
role_code=code,
role_kind="custom",
status=status or "1",
tenant_no=tenant_no,
order_num=order_num if order_num is not None else 10,
remark=(remark or "").strip() or None,
create_by=user_id,
create_dept_id="",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(obj)
db.commit()
db.refresh(obj)
return obj
def update_role(
db: Session,
role_id: str,
*,
tenant_no: str,
role_name: Optional[str] = None,
status: Optional[str] = None,
order_num: Optional[int] = None,
remark: Optional[str] = None,
user_id: Optional[str] = None,
) -> OaRole:
obj = get_role(db, role_id, tenant_no)
if not obj:
raise ValueError("角色不存在")
if role_name is not None:
name = (role_name or "").strip()
if not name:
raise ValueError("角色名称不能为空")
obj.role_name = name
if status is not None:
obj.status = status
if order_num is not None:
obj.order_num = order_num
if remark is not None:
obj.remark = (remark or "").strip() or None
obj.update_by = user_id
obj.update_time = datetime.now()
db.add(obj)
db.commit()
db.refresh(obj)
return obj
def delete_role(db: Session, role_id: str, *, tenant_no: str, user_id: Optional[str] = None) -> None:
obj = get_role(db, role_id, tenant_no)
if not obj:
raise ValueError("角色不存在")
if obj.role_kind == "preset":
raise ValueError("预置角色不可删除")
obj.del_flag = 1
obj.update_by = user_id
obj.update_time = datetime.now()
db.add(obj)
db.commit()
def list_role_users(db: Session, role_id: str, tenant_no: str) -> List[Dict[str, Any]]:
rows = (
db.query(OaRoleUser, SysUser)
.outerjoin(
SysUser,
and_(eq_unicode(SysUser.id, OaRoleUser.user_id), SysUser.del_flag == 0),
)
.filter(
OaRoleUser.oa_role_id == role_id,
OaRoleUser.tenant_no == tenant_no,
OaRoleUser.del_flag == 0,
)
.all()
)
out: List[Dict[str, Any]] = []
for ru, u in rows:
out.append(
{
"id": ru.id,
"userId": ru.user_id,
"nickName": (u.nick_name if u else "") or "",
"userNo": (u.user_no if u else "") or "",
}
)
return out
def replace_role_users(
db: Session,
role_id: str,
*,
tenant_no: str,
user_ids: List[str],
operator_id: Optional[str] = None,
) -> None:
obj = get_role(db, role_id, tenant_no)
if not obj:
raise ValueError("角色不存在")
now = datetime.now()
existing = (
db.query(OaRoleUser)
.filter(
OaRoleUser.oa_role_id == role_id,
OaRoleUser.tenant_no == tenant_no,
OaRoleUser.del_flag == 0,
)
.all()
)
for row in existing:
row.del_flag = 1
row.update_by = operator_id
row.update_time = now
db.add(row)
seen = set()
for uid in user_ids or []:
uid = (uid or "").strip()
if not uid or uid in seen:
continue
seen.add(uid)
db.add(
OaRoleUser(
id=generate_uuid(),
oa_role_id=role_id,
user_id=uid,
tenant_no=tenant_no,
order_num=0,
remark=None,
create_by=operator_id,
create_dept_id="",
create_time=now,
update_by=operator_id,
update_time=now,
del_flag=0,
)
)
db.commit()
def list_oa_role_user_ids(db: Session, role_ids: List[str], tenant_no: str) -> List[str]:
if not role_ids:
return []
enabled = {
r.id
for r in db.query(OaRole)
.filter(
OaRole.id.in_(role_ids),
OaRole.tenant_no == tenant_no,
OaRole.del_flag == 0,
OaRole.status == "1",
)
.all()
}
if not enabled:
return []
rows = (
db.query(OaRoleUser.user_id, OaRoleUser.oa_role_id)
.filter(
OaRoleUser.oa_role_id.in_(list(enabled)),
OaRoleUser.tenant_no == tenant_no,
OaRoleUser.del_flag == 0,
)
.all()
)
out: List[str] = []
seen = set()
for uid, rid in rows:
if rid in enabled and uid and uid not in seen:
seen.add(uid)
out.append(uid)
return out
# ---------------------------------------------------------------------------
# 分组 oa_app_group
# ---------------------------------------------------------------------------
def _group_to_dict(g: OaAppGroup) -> Dict[str, Any]:
return {
"id": g.id,
"groupName": g.group_name,
"tenantNo": g.tenant_no,
"orderNum": g.order_num if g.order_num is not None else 0,
"remark": g.remark or "",
"createTime": g.create_time.strftime("%Y-%m-%d %H:%M:%S") if g.create_time else "",
"updateTime": g.update_time.strftime("%Y-%m-%d %H:%M:%S") if g.update_time else "",
}
def list_groups(db: Session, *, tenant_no: str) -> List[Dict[str, Any]]:
rows = (
db.query(OaAppGroup)
.filter(OaAppGroup.tenant_no == tenant_no, OaAppGroup.del_flag == 0)
.order_by(OaAppGroup.order_num.asc(), OaAppGroup.create_time.asc())
.all()
)
return [_group_to_dict(r) for r in rows]
def get_group(db: Session, group_id: str, tenant_no: str) -> Optional[OaAppGroup]:
return (
db.query(OaAppGroup)
.filter(OaAppGroup.id == group_id, OaAppGroup.tenant_no == tenant_no, OaAppGroup.del_flag == 0)
.first()
)
def create_group(
db: Session,
*,
tenant_no: str,
group_name: str,
order_num: Optional[int] = None,
remark: Optional[str] = None,
user_id: Optional[str] = None,
) -> OaAppGroup:
name = (group_name or "").strip()
if not name:
raise ValueError("分组名称不能为空")
now = datetime.now()
obj = OaAppGroup(
id=generate_uuid(),
group_name=name,
tenant_no=tenant_no,
order_num=order_num if order_num is not None else 0,
remark=(remark or "").strip() or None,
create_by=user_id,
create_dept_id="",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(obj)
db.commit()
db.refresh(obj)
return obj
def update_group(
db: Session,
group_id: str,
*,
tenant_no: str,
group_name: Optional[str] = None,
order_num: Optional[int] = None,
remark: Optional[str] = None,
user_id: Optional[str] = None,
) -> OaAppGroup:
obj = get_group(db, group_id, tenant_no)
if not obj:
raise ValueError("分组不存在")
if group_name is not None:
name = (group_name or "").strip()
if not name:
raise ValueError("分组名称不能为空")
obj.group_name = name
if order_num is not None:
obj.order_num = order_num
if remark is not None:
obj.remark = (remark or "").strip() or None
obj.update_by = user_id
obj.update_time = datetime.now()
db.add(obj)
db.commit()
db.refresh(obj)
return obj
def delete_group(db: Session, group_id: str, *, tenant_no: str, user_id: Optional[str] = None) -> None:
obj = get_group(db, group_id, tenant_no)
if not obj:
raise ValueError("分组不存在")
used = (
db.query(OaApp.id)
.filter(OaApp.group_id == group_id, OaApp.tenant_no == tenant_no, OaApp.del_flag == 0)
.first()
)
if used:
raise ValueError("分组下仍有应用,无法删除")
obj.del_flag = 1
obj.update_by = user_id
obj.update_time = datetime.now()
db.add(obj)
db.commit()
# ---------------------------------------------------------------------------
# 应用 oa_app / 草稿 oa_app_version
# ---------------------------------------------------------------------------
def _app_to_dict(a: OaApp, *, group_name: str = "", has_draft: bool = False, latest_published: Optional[int] = None) -> Dict[str, Any]:
return {
"id": a.id,
"groupId": a.group_id or "",
"groupName": group_name or "",
"appName": a.app_name,
"iconUrl": a.icon_url or "",
"description": a.description or "",
"initiatorScope": a.initiator_scope or "all",
"initiatorUserIds": _loads(a.initiator_user_ids, []),
"adminScope": a.admin_scope or "all_oa_admin",
"adminUserIds": _loads(a.admin_user_ids, []),
"status": a.status,
"tenantNo": a.tenant_no,
"orderNum": a.order_num if a.order_num is not None else 0,
"remark": a.remark or "",
"hasDraft": has_draft,
"latestPublishedVersion": latest_published,
"createTime": a.create_time.strftime("%Y-%m-%d %H:%M:%S") if a.create_time else "",
"updateTime": a.update_time.strftime("%Y-%m-%d %H:%M:%S") if a.update_time else "",
}
def get_app(db: Session, app_id: str, tenant_no: str) -> Optional[OaApp]:
return (
db.query(OaApp)
.filter(OaApp.id == app_id, OaApp.tenant_no == tenant_no, OaApp.del_flag == 0)
.first()
)
def list_apps(
db: Session,
*,
tenant_no: str,
group_id: Optional[str] = None,
keyword: Optional[str] = None,
status: Optional[str] = None,
) -> List[Dict[str, Any]]:
q = db.query(OaApp).filter(OaApp.tenant_no == tenant_no, OaApp.del_flag == 0)
if group_id:
q = q.filter(OaApp.group_id == group_id)
if status:
q = q.filter(OaApp.status == status)
kw = (keyword or "").strip()
if kw:
like = f"%{kw}%"
q = q.filter(OaApp.app_name.like(like))
rows = q.order_by(OaApp.order_num.asc(), OaApp.create_time.desc()).all()
group_ids = list({r.group_id for r in rows if r.group_id})
group_names: Dict[str, str] = {}
if group_ids:
for g in (
db.query(OaAppGroup)
.filter(OaAppGroup.id.in_(group_ids), OaAppGroup.del_flag == 0)
.all()
):
group_names[g.id] = g.group_name
out: List[Dict[str, Any]] = []
for r in rows:
draft = (
db.query(OaAppVersion.id)
.filter(
OaAppVersion.app_id == r.id,
OaAppVersion.tenant_no == tenant_no,
OaAppVersion.status == "draft",
OaAppVersion.del_flag == 0,
)
.first()
)
pub = (
db.query(OaAppVersion)
.filter(
OaAppVersion.app_id == r.id,
OaAppVersion.tenant_no == tenant_no,
OaAppVersion.is_latest_published == "1",
OaAppVersion.del_flag == 0,
)
.first()
)
out.append(
_app_to_dict(
r,
group_name=group_names.get(r.group_id or "", ""),
has_draft=bool(draft),
latest_published=pub.version_no if pub else None,
)
)
return out
def _create_version(
db: Session,
*,
app_id: str,
tenant_no: str,
version_no: int,
status: str,
form_schema: Any,
flow_tree: Any,
advanced: Any,
user_id: Optional[str],
is_latest_published: str = "0",
) -> OaAppVersion:
now = datetime.now()
ver = OaAppVersion(
id=generate_uuid(),
app_id=app_id,
version_no=version_no,
status=status,
is_latest_published=is_latest_published,
form_schema_json=_dumps(form_schema if form_schema is not None else {}),
flow_tree_json=_dumps(flow_tree if flow_tree is not None else {}),
advanced_settings_json=_dumps(advanced if advanced is not None else DEFAULT_ADVANCED_SETTINGS),
tenant_no=tenant_no,
order_num=0,
remark=None,
create_by=user_id,
create_dept_id="",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(ver)
return ver
def create_app(
db: Session,
*,
tenant_no: str,
app_name: str,
group_id: Optional[str] = None,
icon_url: Optional[str] = None,
description: Optional[str] = None,
initiator_scope: str = "all",
initiator_user_ids: Optional[List[str]] = None,
admin_scope: str = "all_oa_admin",
admin_user_ids: Optional[List[str]] = None,
status: str = "1",
order_num: Optional[int] = None,
remark: Optional[str] = None,
user_id: Optional[str] = None,
) -> OaApp:
name = (app_name or "").strip()
if not name:
raise ValueError("应用名称不能为空")
if len(name) > 50:
raise ValueError("应用名称不能超过50字")
gid = (group_id or "").strip() or None
if gid and not get_group(db, gid, tenant_no):
raise ValueError("分组不存在")
now = datetime.now()
obj = OaApp(
id=generate_uuid(),
group_id=gid,
app_name=name,
icon_url=(icon_url or "").strip() or None,
description=(description or "").strip() or None,
initiator_scope=initiator_scope or "all",
initiator_user_ids=_dumps(initiator_user_ids or []),
admin_scope=admin_scope or "all_oa_admin",
admin_user_ids=_dumps(admin_user_ids or []),
status=status or "1",
tenant_no=tenant_no,
order_num=order_num if order_num is not None else 0,
remark=(remark or "").strip() or None,
create_by=user_id,
create_dept_id="",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(obj)
_create_version(
db,
app_id=obj.id,
tenant_no=tenant_no,
version_no=1,
status="draft",
form_schema={},
flow_tree={},
advanced=DEFAULT_ADVANCED_SETTINGS,
user_id=user_id,
)
db.commit()
db.refresh(obj)
return obj
def update_app(
db: Session,
app_id: str,
*,
tenant_no: str,
app_name: Optional[str] = None,
group_id: Optional[str] = None,
icon_url: Optional[str] = None,
description: Optional[str] = None,
initiator_scope: Optional[str] = None,
initiator_user_ids: Optional[List[str]] = None,
admin_scope: Optional[str] = None,
admin_user_ids: Optional[List[str]] = None,
status: Optional[str] = None,
order_num: Optional[int] = None,
remark: Optional[str] = None,
user_id: Optional[str] = None,
) -> OaApp:
obj = get_app(db, app_id, tenant_no)
if not obj:
raise ValueError("应用不存在")
if app_name is not None:
name = (app_name or "").strip()
if not name:
raise ValueError("应用名称不能为空")
if len(name) > 50:
raise ValueError("应用名称不能超过50字")
obj.app_name = name
if group_id is not None:
gid = (group_id or "").strip() or None
if gid and not get_group(db, gid, tenant_no):
raise ValueError("分组不存在")
obj.group_id = gid
if icon_url is not None:
obj.icon_url = (icon_url or "").strip() or None
if description is not None:
desc = (description or "").strip()
if len(desc) > 100:
raise ValueError("表单说明不能超过100字")
obj.description = desc or None
if initiator_scope is not None:
obj.initiator_scope = initiator_scope
if initiator_user_ids is not None:
obj.initiator_user_ids = _dumps(initiator_user_ids)
if admin_scope is not None:
obj.admin_scope = admin_scope
if admin_user_ids is not None:
obj.admin_user_ids = _dumps(admin_user_ids)
if status is not None:
obj.status = status
if order_num is not None:
obj.order_num = order_num
if remark is not None:
obj.remark = (remark or "").strip() or None
obj.update_by = user_id
obj.update_time = datetime.now()
db.add(obj)
db.commit()
db.refresh(obj)
return obj
def delete_app(db: Session, app_id: str, *, tenant_no: str, user_id: Optional[str] = None) -> None:
obj = get_app(db, app_id, tenant_no)
if not obj:
raise ValueError("应用不存在")
now = datetime.now()
obj.del_flag = 1
obj.update_by = user_id
obj.update_time = now
db.add(obj)
for ver in (
db.query(OaAppVersion)
.filter(OaAppVersion.app_id == app_id, OaAppVersion.tenant_no == tenant_no, OaAppVersion.del_flag == 0)
.all()
):
ver.del_flag = 1
ver.update_by = user_id
ver.update_time = now
db.add(ver)
db.commit()
def _find_draft(db: Session, app_id: str, tenant_no: str) -> Optional[OaAppVersion]:
return (
db.query(OaAppVersion)
.filter(
OaAppVersion.app_id == app_id,
OaAppVersion.tenant_no == tenant_no,
OaAppVersion.status == "draft",
OaAppVersion.del_flag == 0,
)
.order_by(OaAppVersion.version_no.desc())
.first()
)
def _find_latest_published(db: Session, app_id: str, tenant_no: str) -> Optional[OaAppVersion]:
return (
db.query(OaAppVersion)
.filter(
OaAppVersion.app_id == app_id,
OaAppVersion.tenant_no == tenant_no,
OaAppVersion.is_latest_published == "1",
OaAppVersion.del_flag == 0,
)
.first()
)
def _version_draft_to_dict(app: OaApp, ver: OaAppVersion) -> Dict[str, Any]:
advanced = _loads(ver.advanced_settings_json, None)
if not isinstance(advanced, dict):
advanced = dict(DEFAULT_ADVANCED_SETTINGS)
else:
merged = dict(DEFAULT_ADVANCED_SETTINGS)
merged.update(advanced)
advanced = merged
return {
"appId": app.id,
"versionId": ver.id,
"versionNo": ver.version_no,
"status": ver.status,
"basic": {
"groupId": app.group_id or "",
"appName": app.app_name,
"iconUrl": app.icon_url or "",
"description": app.description or "",
"initiatorScope": app.initiator_scope or "all",
"initiatorUserIds": _loads(app.initiator_user_ids, []),
"adminScope": app.admin_scope or "all_oa_admin",
"adminUserIds": _loads(app.admin_user_ids, []),
"status": app.status,
"orderNum": app.order_num if app.order_num is not None else 0,
"remark": app.remark or "",
},
"formSchemaJson": _loads(ver.form_schema_json, {}),
"flowTreeJson": _loads(ver.flow_tree_json, {}),
"advancedSettingsJson": advanced,
"updateTime": ver.update_time.strftime("%Y-%m-%d %H:%M:%S") if ver.update_time else "",
}
def get_or_create_draft(
db: Session, app_id: str, *, tenant_no: str, user_id: Optional[str] = None
) -> Dict[str, Any]:
app = get_app(db, app_id, tenant_no)
if not app:
raise ValueError("应用不存在")
draft = _find_draft(db, app_id, tenant_no)
if draft:
return _version_draft_to_dict(app, draft)
published = _find_latest_published(db, app_id, tenant_no)
if published:
next_no = (published.version_no or 0) + 1
draft = _create_version(
db,
app_id=app_id,
tenant_no=tenant_no,
version_no=next_no,
status="draft",
form_schema=_loads(published.form_schema_json, {}),
flow_tree=_loads(published.flow_tree_json, {}),
advanced=_loads(published.advanced_settings_json, DEFAULT_ADVANCED_SETTINGS),
user_id=user_id,
)
else:
max_row = (
db.query(OaAppVersion)
.filter(OaAppVersion.app_id == app_id, OaAppVersion.tenant_no == tenant_no, OaAppVersion.del_flag == 0)
.order_by(OaAppVersion.version_no.desc())
.first()
)
next_no = (max_row.version_no if max_row else 0) + 1
draft = _create_version(
db,
app_id=app_id,
tenant_no=tenant_no,
version_no=next_no or 1,
status="draft",
form_schema={},
flow_tree={},
advanced=DEFAULT_ADVANCED_SETTINGS,
user_id=user_id,
)
db.commit()
db.refresh(draft)
return _version_draft_to_dict(app, draft)
def save_draft(
db: Session,
app_id: str,
*,
tenant_no: str,
basic: Optional[Dict[str, Any]] = None,
form_schema_json: Any = None,
flow_tree_json: Any = None,
advanced_settings_json: Any = None,
user_id: Optional[str] = None,
) -> Dict[str, Any]:
app = get_app(db, app_id, tenant_no)
if not app:
raise ValueError("应用不存在")
# 确保草稿存在
get_or_create_draft(db, app_id, tenant_no=tenant_no, user_id=user_id)
draft = _find_draft(db, app_id, tenant_no)
if not draft:
raise ValueError("草稿不存在")
if basic:
field_map = {
"appName": "app_name",
"groupId": "group_id",
"iconUrl": "icon_url",
"description": "description",
"initiatorScope": "initiator_scope",
"initiatorUserIds": "initiator_user_ids",
"adminScope": "admin_scope",
"adminUserIds": "admin_user_ids",
"status": "status",
"orderNum": "order_num",
"remark": "remark",
}
kwargs = {field_map[k]: basic[k] for k in field_map if k in basic}
if kwargs:
update_app(db, app_id, tenant_no=tenant_no, user_id=user_id, **kwargs)
app = get_app(db, app_id, tenant_no)
draft = _find_draft(db, app_id, tenant_no)
if not draft:
raise ValueError("草稿不存在")
now = datetime.now()
if form_schema_json is not None:
draft.form_schema_json = _dumps(form_schema_json)
if flow_tree_json is not None:
draft.flow_tree_json = _dumps(flow_tree_json)
if advanced_settings_json is not None:
if isinstance(advanced_settings_json, dict):
merged = dict(DEFAULT_ADVANCED_SETTINGS)
merged.update(advanced_settings_json)
draft.advanced_settings_json = _dumps(merged)
else:
draft.advanced_settings_json = _dumps(advanced_settings_json)
draft.update_by = user_id
draft.update_time = now
db.add(draft)
db.commit()
db.refresh(draft)
assert app is not None
return _version_draft_to_dict(app, draft)