diff --git a/app/services/daily_report_service.py b/app/services/daily_report_service.py index 2aa9b73..b98650d 100644 --- a/app/services/daily_report_service.py +++ b/app/services/daily_report_service.py @@ -38,6 +38,17 @@ def _format_duration(minutes: float) -> str: return f"{minutes / 60:.1f}小时" +def _format_age(minutes: int) -> str: + """把分钟数格式化为人类友好的时长""" + if minutes < 60: + return f"{minutes}分钟" + hours = minutes / 60 + if hours < 24: + return f"{hours:.1f}小时" + days = hours / 24 + return f"{days:.1f}天" + + def _format_ratio(numerator: int, denominator: int) -> str: if denominator <= 0: return "0%" @@ -246,8 +257,9 @@ async def _build_daily_report_data() -> Optional[Dict]: top_overdue = [] for order in overdue_orders[:3]: age_minutes = max(int((now - order.create_time).total_seconds() / 60), 0) + order_type_label = "安保" if order.order_type == "SECURITY" else "保洁" top_overdue.append( - f"{order.order_code or order.id}|{_location_name(order)}|挂起 {age_minutes} 分钟" + f"{_location_name(order)}({order_type_label},已挂起{_format_age(age_minutes)})" ) report = { @@ -288,56 +300,65 @@ async def _build_daily_report_data() -> Optional[Dict]: def _build_markdown(report: Dict) -> str: - """构建单条企微 markdown 日报""" + """构建单条企微 markdown 日报(领导视角,简洁直观)""" if report.get("empty"): return ( f"**{report['title']}**\n\n" - f">系统运行平稳,昨日无新增工单\n" - f">当前无待处理工单" + f">昨日系统运行平稳,无新增工单\n" + f">当前无待处理事项" ) s = report["summary"] + backlog = s["backlog_count"] lines = [ f"**{report['title']}**", "", - # ── 核心数字 ── + # ── 总览 ── f">昨日新增 {s['yesterday_total']} 条({report['change_str']})", - f">安保 {s['security_count']}|" - f"保洁 {s['clean_count']}|" - f"完成 {s['completed_count']}|" - f"取消 {s['cancelled_count']}|" - f"误报率 {s['false_alarm_rate']}", - f">当前待处理 {s['backlog_count']} 条" - f"(遗留 {s['carry_over_count']})", - "", - # ── 效率指标 ── - f"**响应效率**", - f">平均首响 {s['avg_resp']}|" - f"平均完结 {s['avg_close']}", + f">安保 {s['security_count']}|保洁 {s['clean_count']}|" + f"已完成 {s['completed_count']}|误报 {s['false_alarm_rate']}", ] - # ── 风险分布 ── - tops = report["tops"] - has_risk = any(v != "暂无数据" for v in tops.values()) - if has_risk: - lines.append("") - lines.append("**风险分布**") - if tops["alarm_types"] != "暂无数据": - lines.append(f">告警类型:{tops['alarm_types']}") - if tops["cleaning_types"] != "暂无数据": - lines.append(f">保洁类型:{tops['cleaning_types']}") - if tops["areas"] != "暂无数据": - lines.append(f">高发区域:{tops['areas']}") - if tops["cameras"] != "暂无数据": - lines.append(f">高发摄像头:{tops['cameras']}") + # 待处理:0 用绿色,>0 用橙色警示 + if backlog == 0: + lines.append(f">待处理 0 条,全部清零") + else: + lines.append( + f">待处理 {backlog} 条" + f"(其中遗留 {s['carry_over_count']})" + ) - # ── 超时跟进 ── + # ── 效率 ── + lines.append("") + lines.append( + f">响应效率:首响 {s['avg_resp']}" + f"|完结 {s['avg_close']}" + ) + + # ── 风险分布(仅有数据时展示)── + tops = report["tops"] + risk_items = [] + if tops["alarm_types"] != "暂无数据": + risk_items.append(f">告警类型|{tops['alarm_types']}") + if tops["cleaning_types"] != "暂无数据": + risk_items.append(f">保洁类型|{tops['cleaning_types']}") + if tops["areas"] != "暂无数据": + risk_items.append(f">高发区域|{tops['areas']}") + if tops["cameras"] != "暂无数据": + risk_items.append(f">高发设备|{tops['cameras']}") + + if risk_items: + lines.append("") + lines.append("**热点分布**") + lines.extend(risk_items) + + # ── 超时跟进(仅有遗留时展示)── if report["top_overdue"]: lines.append("") - lines.append(f"**超时未处理 {s['carry_over_count']} 条**") - for idx, item in enumerate(report["top_overdue"], 1): - lines.append(f">{idx}. {item}") + lines.append(f"**需关注({s['carry_over_count']}条超时)**") + for item in report["top_overdue"]: + lines.append(f">{item}") return "\n".join(lines)