1
0
forked from erp-dev/erp
Files
erpnew/printing/services.py

144 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Printing domain services.
This module is intended for reusable business logic that may be called by:
- management commands
- API views
- signal handlers
"""
from __future__ import annotations
from dataclasses import dataclass
from django.conf import settings
from django.utils import timezone
from api_v1.utils.wecom_webhook import send_wecom_webhook_message
from printing.models import PrintingJob
@dataclass(frozen=True)
class PrintingJobLatestState:
state_name: str
advanced_at: str
def _user_label(user) -> str:
"""
Format a human-readable sender label from Django User.
Prefer employee.name if available.
"""
if not user:
return "系统自动发送"
emp = getattr(user, "employee", None)
name = getattr(emp, "name", None) if emp is not None else None
if name:
return str(name)
username = getattr(user, "username", None)
if username:
return str(username)
return "系统自动发送"
def get_printing_job_latest_state(*, job: PrintingJob) -> PrintingJobLatestState:
"""
Latest state is defined as the latest non-cancelled StateFlowRecord
on job.business_object.
"""
if not job.business_object:
raise ValueError(f"PrintingJob 未绑定 business_object无法获取状态id={job.id}")
state_log = (
job.business_object.state_logs.filter(is_cancelled=False)
.select_related("state")
.order_by("-completed_at", "-id")
.first()
)
if state_log is None:
return PrintingJobLatestState(state_name="未开始", advanced_at="-")
state_name = getattr(state_log.state, "name", None) or str(state_log.state)
advanced_at_dt = state_log.completed_at
advanced_at = timezone.localtime(advanced_at_dt).strftime("%Y-%m-%d %H:%M:%S")
return PrintingJobLatestState(state_name=str(state_name), advanced_at=str(advanced_at))
def render_printing_job_state_markdown(
*,
printing_order_id: str,
printing_job_id: str,
state_name: str,
advanced_at: str,
sender_label: str,
) -> str:
template = getattr(
settings,
"PRINTING_JOB_STATE_ADVANCED_WECOM_MARKDOWN_TEMPLATE",
(
"### 印染任务状态推进\n"
"\n"
"- **印染订单ID**`{printing_order_id}`\n"
"- **印染任务ID**`{printing_job_id}`\n"
"- **推进状态****{state_name}**\n"
"- **推进时间**`{advanced_at}`\n"
"- **发送者**{sender}\n"
),
)
return template.format(
printing_order_id=str(printing_order_id),
printing_job_id=str(printing_job_id),
state_name=str(state_name),
advanced_at=str(advanced_at),
sender=str(sender_label or "系统自动发送"),
)
def send_printing_job_latest_state_wecom(
*,
printing_job_id: int,
sender_label: str,
key: str | None = None,
timeout_seconds: float = 10.0,
dry_run: bool = False,
) -> dict:
"""
Reusable entrypoint for command / future API.
"""
job = (
PrintingJob.objects.select_related("printing_order", "business_object")
.filter(id=int(printing_job_id))
.first()
)
if not job:
raise ValueError(f"PrintingJob 不存在id={printing_job_id}")
latest = get_printing_job_latest_state(job=job)
msg = render_printing_job_state_markdown(
printing_order_id=str(job.printing_order_id),
printing_job_id=str(job.id),
state_name=latest.state_name,
advanced_at=latest.advanced_at,
sender_label=sender_label,
)
if dry_run:
return {"dry_run": True, "message": msg, "state_name": latest.state_name, "advanced_at": latest.advanced_at}
resp = send_wecom_webhook_message(
content=msg,
msgtype="markdown",
key=key,
timeout_seconds=timeout_seconds,
)
return {
"dry_run": False,
"message": msg,
"state_name": latest.state_name,
"advanced_at": latest.advanced_at,
"wecom": resp.raw,
"ok": resp.ok,
"errcode": resp.errcode,
"errmsg": resp.errmsg,
}