feat: 日报/周报月报分开接收人 + 人均产出只算中期提交人
- 日报推送12人(主管+组长+制片+股东) - 周报/月报推送4人(仅股东+主管,含成本信息) - 人均日产出分母改为只算有中期提交的人,与产出口径一致 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e8eb2b5b26
commit
6361c94204
@ -14,5 +14,7 @@ ARK_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
|
|||||||
FEISHU_APP_ID=cli_a90478156bf85bd7
|
FEISHU_APP_ID=cli_a90478156bf85bd7
|
||||||
FEISHU_APP_SECRET=87N2nnx6Yv56TPjl2GraLdKOjFiGOSGp
|
FEISHU_APP_SECRET=87N2nnx6Yv56TPjl2GraLdKOjFiGOSGp
|
||||||
|
|
||||||
# 报告接收人手机号(逗号分隔)
|
# 日报接收人手机号(主管+组长+制片+股东)
|
||||||
|
DAILY_REPORT_RECEIVERS=18002277047,13811803069,13636518028,13811126887,19521028015,13570527019,15920585849,17762840667,17798147128,13726331058,13751770010,18826166683
|
||||||
|
# 周报/月报接收人手机号(含成本信息,仅股东+主管)
|
||||||
REPORT_RECEIVERS=18002277047,13811803069,13636518028,13811126887
|
REPORT_RECEIVERS=18002277047,13811803069,13636518028,13811126887
|
||||||
|
|||||||
@ -36,4 +36,5 @@ FEISHU_APP_ID = os.getenv("FEISHU_APP_ID", "")
|
|||||||
FEISHU_APP_SECRET = os.getenv("FEISHU_APP_SECRET", "")
|
FEISHU_APP_SECRET = os.getenv("FEISHU_APP_SECRET", "")
|
||||||
|
|
||||||
# 报告接收人手机号
|
# 报告接收人手机号
|
||||||
|
DAILY_REPORT_RECEIVERS = [p.strip() for p in os.getenv("DAILY_REPORT_RECEIVERS", "").split(",") if p.strip()]
|
||||||
REPORT_RECEIVERS = [p.strip() for p in os.getenv("REPORT_RECEIVERS", "").split(",") if p.strip()]
|
REPORT_RECEIVERS = [p.strip() for p in os.getenv("REPORT_RECEIVERS", "").split(",") if p.strip()]
|
||||||
|
|||||||
@ -85,9 +85,11 @@ def get_dashboard(
|
|||||||
Submission.project_phase == PhaseGroup.PRODUCTION,
|
Submission.project_phase == PhaseGroup.PRODUCTION,
|
||||||
).scalar() or 0
|
).scalar() or 0
|
||||||
|
|
||||||
# 活跃人数
|
# 活跃人数(只算有中期提交的人,与产出口径一致)
|
||||||
active_users = db.query(Submission.user_id).filter(
|
active_users = db.query(Submission.user_id).filter(
|
||||||
Submission.submit_date >= month_start,
|
Submission.submit_date >= month_start,
|
||||||
|
Submission.submit_date <= today,
|
||||||
|
Submission.project_phase == PhaseGroup.PRODUCTION,
|
||||||
).distinct().count()
|
).distinct().count()
|
||||||
working_days = max(1, (today - month_start).days + 1)
|
working_days = max(1, (today - month_start).days + 1)
|
||||||
avg_daily = round(monthly_secs / max(1, active_users) / working_days, 1)
|
avg_daily = round(monthly_secs / max(1, active_users) / working_days, 1)
|
||||||
|
|||||||
@ -9,8 +9,8 @@ from auth import require_permission
|
|||||||
router = APIRouter(prefix="/api/reports", tags=["AI报告"])
|
router = APIRouter(prefix="/api/reports", tags=["AI报告"])
|
||||||
|
|
||||||
|
|
||||||
async def _push_card(card: dict, test_mobile: Optional[str] = None) -> dict:
|
async def _push_card(card: dict, test_mobile: Optional[str] = None, receivers: list = None) -> dict:
|
||||||
"""推送卡片,支持 test_mobile 只推单人"""
|
"""推送卡片,支持 test_mobile 只推单人,或指定 receivers 列表"""
|
||||||
from services.feishu_service import feishu
|
from services.feishu_service import feishu
|
||||||
if test_mobile:
|
if test_mobile:
|
||||||
user_id = await feishu.get_user_id_by_mobile(test_mobile)
|
user_id = await feishu.get_user_id_by_mobile(test_mobile)
|
||||||
@ -20,7 +20,7 @@ async def _push_card(card: dict, test_mobile: Optional[str] = None) -> dict:
|
|||||||
if ok:
|
if ok:
|
||||||
return {"success": [test_mobile], "failed": []}
|
return {"success": [test_mobile], "failed": []}
|
||||||
return {"success": [], "failed": [{"mobile": test_mobile, "reason": "发送失败"}]}
|
return {"success": [], "failed": [{"mobile": test_mobile, "reason": "发送失败"}]}
|
||||||
return await feishu.send_report_card_to_all(card)
|
return await feishu.send_report_card_to_all(card, receivers=receivers)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/daily")
|
@router.post("/daily")
|
||||||
@ -32,10 +32,11 @@ async def trigger_daily_report(
|
|||||||
"""手动触发日报生成并推送飞书"""
|
"""手动触发日报生成并推送飞书"""
|
||||||
from services.report_service import generate_daily_report
|
from services.report_service import generate_daily_report
|
||||||
from services.feishu_service import build_daily_card
|
from services.feishu_service import build_daily_card
|
||||||
|
from config import DAILY_REPORT_RECEIVERS
|
||||||
|
|
||||||
report = generate_daily_report(db)
|
report = generate_daily_report(db)
|
||||||
card = build_daily_card(report["title"], report["card_data"])
|
card = build_daily_card(report["title"], report["card_data"])
|
||||||
push_result = await _push_card(card, test_mobile)
|
push_result = await _push_card(card, test_mobile, receivers=DAILY_REPORT_RECEIVERS)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"message": "日报生成并推送完成",
|
"message": "日报生成并推送完成",
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import time
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import httpx
|
import httpx
|
||||||
from config import FEISHU_APP_ID, FEISHU_APP_SECRET, REPORT_RECEIVERS
|
from config import FEISHU_APP_ID, FEISHU_APP_SECRET, REPORT_RECEIVERS, DAILY_REPORT_RECEIVERS
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -387,15 +387,16 @@ class FeishuService:
|
|||||||
}
|
}
|
||||||
return await self.send_card(user_id, card)
|
return await self.send_card(user_id, card)
|
||||||
|
|
||||||
async def send_report_card_to_all(self, card: dict) -> dict:
|
async def send_report_card_to_all(self, card: dict, receivers: list = None) -> dict:
|
||||||
"""给所有配置的接收人发送卡片报告"""
|
"""给指定接收人发送卡片报告,默认使用 REPORT_RECEIVERS"""
|
||||||
results = {"success": [], "failed": []}
|
results = {"success": [], "failed": []}
|
||||||
|
receiver_list = receivers or REPORT_RECEIVERS
|
||||||
|
|
||||||
if not REPORT_RECEIVERS:
|
if not receiver_list:
|
||||||
logger.warning("未配置报告接收人")
|
logger.warning("未配置报告接收人")
|
||||||
return results
|
return results
|
||||||
|
|
||||||
for mobile in REPORT_RECEIVERS:
|
for mobile in receiver_list:
|
||||||
user_id = await self.get_user_id_by_mobile(mobile)
|
user_id = await self.get_user_id_by_mobile(mobile)
|
||||||
if not user_id:
|
if not user_id:
|
||||||
results["failed"].append({"mobile": mobile, "reason": "未找到用户"})
|
results["failed"].append({"mobile": mobile, "reason": "未找到用户"})
|
||||||
|
|||||||
@ -17,6 +17,7 @@ async def _run_report_job(report_type: str):
|
|||||||
from services.feishu_service import (
|
from services.feishu_service import (
|
||||||
feishu, build_daily_card, build_weekly_card, build_monthly_card,
|
feishu, build_daily_card, build_weekly_card, build_monthly_card,
|
||||||
)
|
)
|
||||||
|
from config import DAILY_REPORT_RECEIVERS, REPORT_RECEIVERS
|
||||||
|
|
||||||
logger.info(f"[定时任务] 开始生成{report_type}...")
|
logger.info(f"[定时任务] 开始生成{report_type}...")
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
@ -24,18 +25,21 @@ async def _run_report_job(report_type: str):
|
|||||||
if report_type == "日报":
|
if report_type == "日报":
|
||||||
result = generate_daily_report(db)
|
result = generate_daily_report(db)
|
||||||
card = build_daily_card(result["title"], result["card_data"])
|
card = build_daily_card(result["title"], result["card_data"])
|
||||||
|
receivers = DAILY_REPORT_RECEIVERS
|
||||||
elif report_type == "周报":
|
elif report_type == "周报":
|
||||||
result = generate_weekly_report(db)
|
result = generate_weekly_report(db)
|
||||||
card = build_weekly_card(result["title"], result["card_data"])
|
card = build_weekly_card(result["title"], result["card_data"])
|
||||||
|
receivers = REPORT_RECEIVERS
|
||||||
elif report_type == "月报":
|
elif report_type == "月报":
|
||||||
result = generate_monthly_report(db)
|
result = generate_monthly_report(db)
|
||||||
card = build_monthly_card(result["title"], result["card_data"])
|
card = build_monthly_card(result["title"], result["card_data"])
|
||||||
|
receivers = REPORT_RECEIVERS
|
||||||
else:
|
else:
|
||||||
logger.error(f"未知报告类型: {report_type}")
|
logger.error(f"未知报告类型: {report_type}")
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.info(f"[定时任务] {report_type}生成完成,开始推送飞书...")
|
logger.info(f"[定时任务] {report_type}生成完成,开始推送飞书({len(receivers)}人)...")
|
||||||
push_result = await feishu.send_report_card_to_all(card)
|
push_result = await feishu.send_report_card_to_all(card, receivers=receivers)
|
||||||
logger.info(f"[定时任务] {report_type}推送完成: {push_result}")
|
logger.info(f"[定时任务] {report_type}推送完成: {push_result}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user