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': 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 = {} groups = {}
for t in role_tasks: for t in role_tasks:
key = (_params_of(t).get('analysis_dim') or '').strip() or '__legacy__' key = (_params_of(t).get('analysis_dim') or '').strip() or '__legacy__'
@ -181,10 +189,16 @@ async with DBPools().sqlorContext(dbname) as sor:
}) })
else: else:
final_state = getattr(ts[-1], 'state', '') or '' 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({ nodes.append({
"id": "grp:" + role + ":" + key, "id": "grp:" + role + ":" + key,
"label": "{}({} 轮)最新[{}]".format(label, len(ts), "label": "{}({} 轮)最新[{}]".format(label, len(ts), tail),
_state_zh.get(final_state, final_state)),
"is_leaf": False, "is_leaf": False,
}) })
return json.dumps(nodes, ensure_ascii=False) return json.dumps(nodes, ensure_ascii=False)
@ -277,6 +291,10 @@ async with DBPools().sqlorContext(dbname) as sor:
except Exception: except Exception:
return {} return {}
# 维度→QC类型映射(用于给 analyst 轮次挂该轮 QC 结果)
DIM_QC = {'scoring': 'scoring_items', 'quals': 'qualifications',
'reqs_outline': 'doc_requirements', 'cost_benefit': 'cost_benefit'}
if role == 'agent.qc': if role == 'agent.qc':
# QC 组:按审核记录轮次升序展开(同一任务多轮复审各占一行) # QC 组:按审核记录轮次升序展开(同一任务多轮复审各占一行)
revs = await sor.sqlExe( 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)}) "is_leaf": not (del_cnt.get(getattr(t, 'id', ''), 0) > 0)})
return json.dumps(nodes, ensure_ascii=False) return json.dumps(nodes, ensure_ascii=False)
# analyst 组:按任务创建序(=轮次升序)展开 # analyst 组:按任务创建序展开,轮次号与QC结果取自审核记录
# (匹配规则:审核记录的 created_at 落在 [本任务updated_at, 下一任务created_at) 区间)
ts = [] ts = []
for t in all_tasks: for t in all_tasks:
if (getattr(t, 'role', '') or '').strip() != role: if (getattr(t, 'role', '') or '').strip() != role:
@ -324,15 +343,45 @@ async with DBPools().sqlorContext(dbname) as sor:
if k == key: if k == key:
ts.append(t) ts.append(t)
ts.sort(key=lambda x: str(getattr(x, 'created_at', '') or '')) 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 = [] nodes = []
used_rounds = set()
for i, t in enumerate(ts): for i, t in enumerate(ts):
tid = getattr(t, 'id', '') tid = getattr(t, 'id', '')
st = getattr(t, 'state', '') or '' 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({ nodes.append({
"id": tid, "id": tid,
"label": "第%d轮 %s [%s] %s" % (i + 1, _state_icons.get(st, '\u2b1c'), "label": "第%d轮 %s [任务%s] %s %s" % (
_state_zh.get(st, st), upd), 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), "is_leaf": not (step_cnt.get(tid, 0) > 0 or del_cnt.get(tid, 0) > 0),
}) })
return json.dumps(nodes, ensure_ascii=False) return json.dumps(nodes, ensure_ascii=False)