fix: 任务树轮次节点显示该轮QC结果(任务已批准≠QC通过,消除误导);组节点标签用最新QC分

This commit is contained in:
yumoqing 2026-09-03 17:07:04 +08:00
parent ee62a2bbf9
commit 6a4518ef22

View File

@ -151,6 +151,14 @@ async with DBPools().sqlorContext(dbname) as sor:
# ── 维度/类型分组 ──
if role == 'agent.tender_analyst':
# 各维度最新一轮 QC 结果(组节点标签用,消除"任务已批准≠QC通过"误导)
latest_qc = {}
for rv in await sor.sqlExe(
"SELECT qc_type, fit_score, passed, round FROM bid_qc_reviews "
"WHERE project_id=${p}$ ORDER BY qc_type, round DESC", {"p": pid}) or []:
qt = getattr(rv, 'qc_type', '') or ''
if qt not in latest_qc:
latest_qc[qt] = rv
groups = {}
for t in role_tasks:
key = (_params_of(t).get('analysis_dim') or '').strip() or '__legacy__'
@ -181,10 +189,16 @@ async with DBPools().sqlorContext(dbname) as sor:
})
else:
final_state = getattr(ts[-1], 'state', '') or ''
lq = latest_qc.get(key)
if lq:
lq_passed = str(getattr(lq, 'passed', '0')) == '1'
tail = "QC %.1f 分 %s" % (float(getattr(lq, 'fit_score', 0) or 0),
'通过' if lq_passed else '未通过')
else:
tail = "任务" + _state_zh.get(final_state, final_state)
nodes.append({
"id": "grp:" + role + ":" + key,
"label": "{}{} 轮)最新[{}]".format(label, len(ts),
_state_zh.get(final_state, final_state)),
"label": "{}{} 轮)最新[{}]".format(label, len(ts), tail),
"is_leaf": False,
})
return json.dumps(nodes, ensure_ascii=False)
@ -277,6 +291,10 @@ async with DBPools().sqlorContext(dbname) as sor:
except Exception:
return {}
# 维度→QC类型映射用于给 analyst 轮次挂该轮 QC 结果)
DIM_QC = {'scoring': 'scoring_items', 'quals': 'qualifications',
'reqs_outline': 'doc_requirements', 'cost_benefit': 'cost_benefit'}
if role == 'agent.qc':
# QC 组:按审核记录轮次升序展开(同一任务多轮复审各占一行)
revs = await sor.sqlExe(
@ -313,7 +331,8 @@ async with DBPools().sqlorContext(dbname) as sor:
"is_leaf": not (del_cnt.get(getattr(t, 'id', ''), 0) > 0)})
return json.dumps(nodes, ensure_ascii=False)
# analyst 组:按任务创建序(=轮次升序)展开
# analyst 组按任务创建序展开轮次号与QC结果取自审核记录
# (匹配规则:审核记录的 created_at 落在 [本任务updated_at, 下一任务created_at) 区间)
ts = []
for t in all_tasks:
if (getattr(t, 'role', '') or '').strip() != role:
@ -324,15 +343,45 @@ async with DBPools().sqlorContext(dbname) as sor:
if k == key:
ts.append(t)
ts.sort(key=lambda x: str(getattr(x, 'created_at', '') or ''))
qc_type = DIM_QC.get(key, '')
rev_by_round = {}
if qc_type:
for rv in await sor.sqlExe(
"SELECT round, fit_score, passed, created_at FROM bid_qc_reviews "
"WHERE project_id=${p}$ AND qc_type=${k}$ ORDER BY round ASC",
{"p": pid, "k": qc_type}) or []:
rev_by_round[int(getattr(rv, 'round', 0) or 0)] = rv
nodes = []
used_rounds = set()
for i, t in enumerate(ts):
tid = getattr(t, 'id', '')
st = getattr(t, 'state', '') or ''
upd = str(getattr(t, 'updated_at', '') or '')[5:16]
t_upd = str(getattr(t, 'updated_at', '') or '')
nxt_created = str(getattr(ts[i + 1], 'created_at', '') or '9999') if i + 1 < len(ts) else '9999'
matched = None
for rnd, rv in rev_by_round.items():
if rnd in used_rounds:
continue
rv_t = str(getattr(rv, 'created_at', '') or '')
if t_upd and rv_t >= t_upd and rv_t < nxt_created:
matched = (rnd, rv)
break
if matched:
rnd, rv = matched
used_rounds.add(rnd)
passed = str(getattr(rv, 'passed', '0')) == '1'
sc = float(getattr(rv, 'fit_score', 0) or 0)
qc_label = "→ QC %.1f 分 %s" % (sc, '通过' if passed else '未通过')
else:
rnd = i + 1
qc_label = '→ 未审核/审核记录未关联'
upd = t_upd[5:16]
nodes.append({
"id": tid,
"label": "第%d轮 %s [%s] %s" % (i + 1, _state_icons.get(st, '\u2b1c'),
_state_zh.get(st, st), upd),
"label": "第%d轮 %s [任务%s] %s %s" % (
rnd, _state_icons.get(st, '\u2b1c'), _state_zh.get(st, st), qc_label, upd),
"is_leaf": not (step_cnt.get(tid, 0) > 0 or del_cnt.get(tid, 0) > 0),
})
return json.dumps(nodes, ensure_ascii=False)