From d1f335d6a24c90da11ab468560578bf4022a78f0 Mon Sep 17 00:00:00 2001 From: Marsway Date: Mon, 5 Jan 2026 17:37:28 +0800 Subject: [PATCH] update --- app/integrations/seeyon.py | 38 +++++++++++++ extensions/sync_oa_to_didi/job.py | 88 +++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/app/integrations/seeyon.py b/app/integrations/seeyon.py index 2056b75..80d4e81 100644 --- a/app/integrations/seeyon.py +++ b/app/integrations/seeyon.py @@ -77,4 +77,42 @@ class SeeyonClient(BaseClient): return super().request(method, path, headers=headers, **kwargs) raise + def export_cap4_form_soap( + self, + *, + templateCode: str, + senderLoginName: str | None = None, + rightId: str | None = None, + doTrigger: str | bool | None = None, + param: str | None = None, + extra: dict[str, Any] | None = None, + ) -> httpx.Response: + """ + 无流程表单导出(CAP4): + POST /seeyon/rest/cap4/form/soap/export + + 返回 httpx.Response,调用方可自行读取 resp.text / resp.headers 等信息。 + """ + body: dict[str, Any] = {"templateCode": templateCode} + if senderLoginName: + body["senderLoginName"] = senderLoginName + if rightId: + body["rightId"] = rightId + if doTrigger is not None: + body["doTrigger"] = doTrigger + if param is not None: + body["param"] = param + if extra: + # 兜底扩展字段:仅当 key 不冲突时注入,避免覆盖已显式指定的参数 + for k, v in extra.items(): + if k not in body: + body[k] = v + + return self.request( + "POST", + "/seeyon/rest/cap4/form/soap/export", + json=body, + headers={"Content-Type": "application/json"}, + ) + diff --git a/extensions/sync_oa_to_didi/job.py b/extensions/sync_oa_to_didi/job.py index f16c587..a607fd6 100644 --- a/extensions/sync_oa_to_didi/job.py +++ b/extensions/sync_oa_to_didi/job.py @@ -56,3 +56,91 @@ class SyncOAToDidiTokenJob(BaseJob): return {"token_masked": masked, "loginName": login_name or "", "base_url": base_url} +class SyncOAToDidiExportFormJob(BaseJob): + """ + 无流程表单导出(CAP4): + - 调用:POST /seeyon/rest/cap4/form/soap/export + - base_url 不包含 /seeyon/rest(例如 https://oa.example.com:8090) + + public_cfg: + - base_url: "https://oa.example.com:8090" + - templateCode: "employee" + - senderLoginName: "xxx" (可选) + - rightId: "xxx" (可选) + - doTrigger: "true" (可选) + - param: "0" (可选) + - extra: {...} (可选,兜底扩展字段) + + secret_cfg (解密后): + - rest_user + - rest_password + - loginName (可选) + """ + + job_id = "sync_oa_to_didi.export_form_soap" + + def run(self, params: dict[str, Any], secrets: dict[str, Any]) -> dict[str, Any]: + base_url = str(params.get("base_url") or "").strip() + if not base_url: + raise ValueError("public_cfg.base_url is required") + + template_code = str(params.get("templateCode") or "").strip() + if not template_code: + raise ValueError("public_cfg.templateCode is required") + + sender_login_name = params.get("senderLoginName") + sender_login_name = str(sender_login_name).strip() if sender_login_name else None + + right_id = params.get("rightId") + right_id = str(right_id).strip() if right_id else None + + do_trigger = params.get("doTrigger") + param = params.get("param") + param = str(param) if param is not None else None + + extra = params.get("extra") + if extra is not None and not isinstance(extra, dict): + raise ValueError("public_cfg.extra must be a JSON object (dict) if provided") + + rest_user = str(secrets.get("rest_user") or "").strip() + rest_password = str(secrets.get("rest_password") or "").strip() + login_name = secrets.get("loginName") + login_name = str(login_name).strip() if login_name else None + + if not rest_user or not rest_password: + raise ValueError("secret_cfg.rest_user and secret_cfg.rest_password are required") + + client = SeeyonClient(base_url=base_url, rest_user=rest_user, rest_password=rest_password, loginName=login_name) + try: + resp = client.export_cap4_form_soap( + templateCode=template_code, + senderLoginName=sender_login_name, + rightId=right_id, + doTrigger=do_trigger, + param=param, + extra=extra, + ) + raw_text = resp.text or "" + content_type = resp.headers.get("content-type", "") if getattr(resp, "headers", None) else "" + finally: + client.close() + + # 避免把 raw_text 打到日志或 run_log(会被截断且污染 JobLog) + logger.info( + "Seeyon export_form_soap done templateCode=%s content_length=%s content_type=%s base_url=%s", + template_code, + len(raw_text), + content_type, + base_url, + ) + + return { + "raw": raw_text, + "meta": { + "templateCode": template_code, + "content_length": len(raw_text), + "content_type": content_type, + }, + } + +