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)
|
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 即生效)
|
# import 即注册(与投标产线一致:宿主 import pipeline_opportunity 即生效)
|
||||||
register_opp_ability()
|
register_opp_ability()
|
||||||
register_opp_slash_commands()
|
register_opp_slash_commands()
|
||||||
|
register_opp_dingtalk_hook()
|
||||||
|
|||||||
@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from ahserver.serverenv import ServerEnv
|
||||||
|
|
||||||
from .opp_common import (
|
from .opp_common import (
|
||||||
get_db, new_id, rec_to_dict, rows_to_dicts, can_transition,
|
get_db, new_id, rec_to_dict, rows_to_dicts, can_transition,
|
||||||
RP_DRAFT, RP_CONFIRMED, RP_APPROVAL_INITIATED, RP_APPROVED, RP_REJECTED,
|
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 "已退回草稿"
|
return True, "已确认" if ok else "已退回草稿"
|
||||||
|
|
||||||
|
|
||||||
async def initiate_approval(report_id, note="", created_by="agent.opportunity"):
|
async def initiate_approval(report_id, note="", created_by="agent.opportunity",
|
||||||
"""发起研发审批:要求报告已 confirmed(人工确认门禁)。"""
|
applicant_id=""):
|
||||||
|
"""发起研发审批:要求报告已 confirmed(人工确认门禁)。
|
||||||
|
|
||||||
|
审批通道优先级:
|
||||||
|
1. 钉钉审批——dda_approval_configs 配了 biz_type=opp_dev_approval 且启用时,
|
||||||
|
调 dingdingflow.submit_approval 发起,审批人节点链由配置的
|
||||||
|
approvers_config 决定;结果经回调写回(见 opp_common 注册的钩子)。
|
||||||
|
2. 降级人工——无钉钉配置/发起失败时,落平台内人工审批任务兜底,
|
||||||
|
保证产线闭环不因缺少钉钉环境而卡死。
|
||||||
|
"""
|
||||||
db, dbname = get_db()
|
db, dbname = get_db()
|
||||||
async with db.sqlorContext(dbname) as sor:
|
async with db.sqlorContext(dbname) as sor:
|
||||||
rep = await get_report(sor, report_id)
|
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", {
|
await sor.U("opp_reports", {
|
||||||
"id": report_id, "status": RP_APPROVAL_INITIATED})
|
"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", {})
|
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
|
return True, aid
|
||||||
|
|
||||||
|
|
||||||
@ -194,6 +233,25 @@ async def resolve_approval(approval_id, result, operator=""):
|
|||||||
return True, result
|
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=""):
|
async def list_approvals(project_id="", status=""):
|
||||||
db, dbname = get_db()
|
db, dbname = get_db()
|
||||||
async with db.sqlorContext(dbname) as sor:
|
async with db.sqlorContext(dbname) as sor:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user