fix(操作统计): 上传备注改为按任务负责人归因,历史数据可回溯

- 备注统计不用 task_logs(历史无record日志),改为 task_records 按任务 assignee 归因
- 排除系统自动生成的备注(以'['开头的接收/转交/撤回等),只计手动上传
- 移除 add_task_record 冗余的 record 日志(不再用于统计)
- 不改数据库,历史182条备注也能正确归属到人
This commit is contained in:
2026-08-28 13:26:26 +08:00
parent 41c19242cf
commit 477a187fc1
2 changed files with 39 additions and 33 deletions

View File

@ -483,29 +483,29 @@ async def get_user_operations(
since: datetime | None = None,
until: datetime | None = None,
) -> list[UserOperation]:
"""上帝视角 — 统计**全部人员**的操作次数(按时段过滤 task_logs)。
"""上帝视角 — 统计**全部人员**的操作次数(按时段过滤)。
返回所有 IRIS 部门人员(该时段无操作的计 0并追加有操作记录但不在
人员清单中的账号(如历史/已离职)。
操作口径:
- 接收: action_type='receive'
- 转交: action_type='complete'完工并移交下一道工序
- 上传备注: action_type='record'(工人手动追加的进度记录)
操作口径(均不修改数据库)
- 接收: task_logs.action_type='receive'(按操作人)
- 转交: task_logs.action_type='complete'按操作人
- 上传备注: task_records 按**任务负责人**归因(排除系统自动生成的
'[' 开头的备注,如 "[接收] 操作员已确认接收"),历史数据可回溯
"""
from app.models.task_log import TaskLog
from app.models.task import Task, TaskRecord
from app.core.mom_database import MomSessionLocal
from sqlalchemy import text
from sqlalchemy import text, or_
# ── 1. 统计 task_logs 操作次数(按 operator_id 聚合)──
# ── 1. 接收 / 转交(task_logs 按 operator_id 聚合)──
rcv = func.count().filter(TaskLog.action_type == "receive")
cpl = func.count().filter(TaskLog.action_type == "complete")
rcd = func.count().filter(TaskLog.action_type == "record")
stmt = (
select(TaskLog.operator_id, rcv.label("receive"), cpl.label("complete"), rcd.label("record"))
select(TaskLog.operator_id, rcv.label("receive"), cpl.label("complete"))
.where(
TaskLog.action_type.in_(["receive", "complete", "record"]),
TaskLog.action_type.in_(["receive", "complete"]),
TaskLog.operator_id.isnot(None),
)
.group_by(TaskLog.operator_id)
@ -514,11 +514,27 @@ async def get_user_operations(
stmt = stmt.where(TaskLog.created_at >= since)
if until:
stmt = stmt.where(TaskLog.created_at <= until)
op_rows = (await db.execute(stmt)).all()
op_map = {r[0]: (r[1] or 0, r[2] or 0) for r in op_rows}
result = await db.execute(stmt)
op_map = {r[0]: (r[1] or 0, r[2] or 0, r[3] or 0) for r in result.all()}
# ── 2. 上传备注task_records 按任务 assignee 归因,排除系统自动备注)──
rcd_stmt = (
select(Task.assignee_id, func.count(TaskRecord.id))
.join(Task, TaskRecord.task_id == Task.id)
.where(
Task.assignee_id.isnot(None),
or_(TaskRecord.remark.is_(None), ~TaskRecord.remark.like("[%")),
)
.group_by(Task.assignee_id)
)
if since:
rcd_stmt = rcd_stmt.where(TaskRecord.created_at >= since)
if until:
rcd_stmt = rcd_stmt.where(TaskRecord.created_at <= until)
rcd_rows = (await db.execute(rcd_stmt)).all()
record_map = {r[0]: r[1] or 0 for r in rcd_rows}
# ── 2. 获取全部 MOM 用户清单IRIS 部门)──
# ── 3. 获取全部 MOM 用户清单IRIS 部门)──
users: list[dict] = []
try:
dbm = MomSessionLocal()
@ -540,19 +556,19 @@ async def get_user_operations(
except Exception:
users = []
# ── 3. 合并:所有人员 + 有操作但不在清单的账号 ──
# ── 4. 合并:所有人员 + 有操作但不在清单的账号 ──
merged: dict[str, dict] = {}
for u in users:
merged[u["username"]] = {"name": u["full_name"], "rcv": 0, "cpl": 0, "rcd": 0}
for uid, (r, c, rd) in op_map.items():
if uid in merged:
for uid in set(op_map.keys()) | set(record_map.keys()):
if uid not in merged:
merged[uid] = {"name": uid, "rcv": 0, "cpl": 0, "rcd": 0}
r, c = op_map.get(uid, (0, 0))
merged[uid]["rcv"] = r
merged[uid]["cpl"] = c
merged[uid]["rcd"] = rd
else:
merged[uid] = {"name": uid, "rcv": r, "cpl": c, "rcd": rd}
merged[uid]["rcd"] = record_map.get(uid, 0)
# ── 4. 组装 + 排序(总次数降序,无操作的排在最后)──
# ── 5. 组装 + 排序(总次数降序,无操作的排在最后)──
items: list[UserOperation] = []
for uid, v in merged.items():
items.append(UserOperation(

View File

@ -1049,16 +1049,6 @@ async def add_task_record(
db.add(record)
await db.flush()
# 🔧 操作日志:记录一次「上传备注」操作(供人员操作统计)
if current_user:
from app.models.task_log import TaskLog
db.add(TaskLog(
task_id=task_id,
operator_id=current_user.get("username") or current_user.get("sub") or "",
action_type="record",
remark=f"上传备注: {(data.remark or '')[:100]}",
))
# 🚀 留言通知:给任务当前负责人发送提醒(不给自己发)
if (
current_user