feat: 审批改走钉钉审批(dingdingflow)——发起时推钉钉+回调钩子写回,无配置降级人工; 补.gitignore
This commit is contained in:
parent
4b530212e6
commit
025368d66b
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -333,6 +333,32 @@ def register_opp_slash_commands():
|
||||
register_slash_command(cmd)
|
||||
|
||||
|
||||
OPP_DD_BIZ_TYPE = "opp_dev_approval"
|
||||
|
||||
|
||||
def register_opp_dingtalk_hook():
|
||||
"""把商机产线审批结论回流挂到钉钉审批回调上。
|
||||
|
||||
dingdingflow 已加载时注册成功;未加载(宿主没装钉钉模块)时静默跳过,
|
||||
发起审批会走降级人工通道,闭环不受影响。
|
||||
"""
|
||||
try:
|
||||
from ahserver.serverenv import ServerEnv
|
||||
env = ServerEnv()
|
||||
register_biz_handler = getattr(env, "register_biz_handler", None)
|
||||
if register_biz_handler is None:
|
||||
logger.info("[opp] dingdingflow 未加载,跳过钉钉审批钩子注册")
|
||||
return False
|
||||
from .opp_report_capability import on_dingtalk_approval_done
|
||||
register_biz_handler(OPP_DD_BIZ_TYPE, on_dingtalk_approval_done)
|
||||
logger.info("[opp] 钉钉审批钩子已注册 biz_type=%s", OPP_DD_BIZ_TYPE)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.warning("[opp] 钉钉审批钩子注册失败(不影响降级人工): %s", str(e)[:200])
|
||||
return False
|
||||
|
||||
|
||||
# import 即注册(与投标产线一致:宿主 import pipeline_opportunity 即生效)
|
||||
register_opp_ability()
|
||||
register_opp_slash_commands()
|
||||
register_opp_dingtalk_hook()
|
||||
|
||||
@ -12,6 +12,8 @@
|
||||
|
||||
import logging
|
||||
|
||||
from ahserver.serverenv import ServerEnv
|
||||
|
||||
from .opp_common import (
|
||||
get_db, new_id, rec_to_dict, rows_to_dicts, can_transition,
|
||||
RP_DRAFT, RP_CONFIRMED, RP_APPROVAL_INITIATED, RP_APPROVED, RP_REJECTED,
|
||||
@ -133,8 +135,17 @@ async def confirm_report(report_id, ok, operator=""):
|
||||
return True, "已确认" if ok else "已退回草稿"
|
||||
|
||||
|
||||
async def initiate_approval(report_id, note="", created_by="agent.opportunity"):
|
||||
"""发起研发审批:要求报告已 confirmed(人工确认门禁)。"""
|
||||
async def initiate_approval(report_id, note="", created_by="agent.opportunity",
|
||||
applicant_id=""):
|
||||
"""发起研发审批:要求报告已 confirmed(人工确认门禁)。
|
||||
|
||||
审批通道优先级:
|
||||
1. 钉钉审批——dda_approval_configs 配了 biz_type=opp_dev_approval 且启用时,
|
||||
调 dingdingflow.submit_approval 发起,审批人节点链由配置的
|
||||
approvers_config 决定;结果经回调写回(见 opp_common 注册的钩子)。
|
||||
2. 降级人工——无钉钉配置/发起失败时,落平台内人工审批任务兜底,
|
||||
保证产线闭环不因缺少钉钉环境而卡死。
|
||||
"""
|
||||
db, dbname = get_db()
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
rep = await get_report(sor, report_id)
|
||||
@ -153,12 +164,40 @@ async def initiate_approval(report_id, note="", created_by="agent.opportunity"):
|
||||
})
|
||||
await sor.U("opp_reports", {
|
||||
"id": report_id, "status": RP_APPROVAL_INITIATED})
|
||||
# 发人工审批任务
|
||||
await create_human_task(
|
||||
sor, rep.get("project_id", ""), HT_DEV_APPROVAL,
|
||||
"研发审批:%s" % rep.get("title", ""),
|
||||
note or "请审批《%s》的研发立项。" % rep.get("title", ""))
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
|
||||
# 尝试钉钉审批通道
|
||||
dd_ok = False
|
||||
env = ServerEnv()
|
||||
submit_approval = getattr(env, "submit_approval", None)
|
||||
if submit_approval is not None:
|
||||
try:
|
||||
org_id = rep.get("org_id", "") or "0"
|
||||
r = await submit_approval(
|
||||
"opp_dev_approval", aid,
|
||||
"研发立项审批:%s" % rep.get("title", ""),
|
||||
applicant_id or created_by, org_id)
|
||||
if isinstance(r, dict) and r.get("success") and r.get("instance_id"):
|
||||
dd_ok = True
|
||||
logger.info("研发审批已推钉钉: report=%s instance=%s",
|
||||
report_id, r.get("instance_id"))
|
||||
else:
|
||||
logger.warning("钉钉审批发起未成功(降级人工): report=%s msg=%s",
|
||||
report_id, (r or {}).get("message", ""))
|
||||
except Exception as e:
|
||||
logger.warning("钉钉审批发起异常(降级人工): report=%s err=%s",
|
||||
report_id, str(e)[:200])
|
||||
|
||||
if not dd_ok:
|
||||
# 降级:平台内人工审批任务
|
||||
db, dbname = get_db()
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
await create_human_task(
|
||||
sor, rep.get("project_id", ""), HT_DEV_APPROVAL,
|
||||
"研发审批:%s" % rep.get("title", ""),
|
||||
note or "请审批《%s》的研发立项。" % rep.get("title", ""))
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
return True, aid
|
||||
return True, aid
|
||||
|
||||
|
||||
@ -194,6 +233,25 @@ async def resolve_approval(approval_id, result, operator=""):
|
||||
return True, result
|
||||
|
||||
|
||||
async def on_dingtalk_approval_done(biz_id, status, approval_id, comment):
|
||||
"""钉钉审批结果回调钩子(dingdingflow register_biz_handler 签名)。
|
||||
|
||||
biz_id = opp_approvals.id(发起审批时传入)。
|
||||
status ∈ approved / rejected / cancelled,cancelled 按驳回处理
|
||||
(产线不能停在 approval_initiated 永久卡死)。
|
||||
"""
|
||||
if status == "approved":
|
||||
result = AP_APPROVED
|
||||
else:
|
||||
result = AP_REJECTED
|
||||
ok, msg = await resolve_approval(
|
||||
biz_id, result, operator="dingtalk:%s" % (approval_id or ""))
|
||||
if ok:
|
||||
logger.info("钉钉审批结论已回流: opp_approval=%s -> %s", biz_id, result)
|
||||
else:
|
||||
logger.warning("钉钉审批结论回流失败: opp_approval=%s msg=%s", biz_id, msg)
|
||||
|
||||
|
||||
async def list_approvals(project_id="", status=""):
|
||||
db, dbname = get_db()
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user