fix: 任务树轮次节点显示该轮QC结果(任务已批准≠QC通过,消除误导);组节点标签用最新QC分
This commit is contained in:
parent
ee62a2bbf9
commit
6a4518ef22
@ -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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user