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

279 lines
9.6 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 module business logic services
"""
from typing import Dict, Any, Tuple
from django.conf import settings
from django.db import transaction
from django.contrib.contenttypes.models import ContentType
from printing import models as printing_models
from printing.signals import printing_job_production_completed, printing_order_created
from stateflow import models as stateflow_models
from stateflow import services as stateflow_services
class PrintingOrderService:
"""印染订单业务逻辑服务"""
@staticmethod
def get_default_process():
"""获取默认流程"""
process_id = getattr(settings, 'PRINTING_DEFAULT_PROCESS_ID', None)
if not process_id:
return None
try:
return stateflow_models.Process.objects.get(id=process_id)
except stateflow_models.Process.DoesNotExist:
return None
@staticmethod
def create_printing_order(data: Dict[str, Any], user) -> printing_models.PrintingOrder:
"""
创建印染订单
Args:
data: 订单数据
user: 当前用户
Returns:
创建的订单实例
"""
# 如果未指定 process使用默认值
if 'process' not in data or data['process'] is None:
default_process = PrintingOrderService.get_default_process()
if default_process:
data['process'] = default_process
# 绑定创建人
data['created_by'] = user
# 绑定商户(从当前用户的 employee 获取)
if hasattr(user, 'employee') and user.employee and user.employee.merchant:
data['merchant'] = user.employee.merchant
order = printing_models.PrintingOrder.objects.create(**data)
# domain event: printing order created
# handler will ensure transaction.on_commit for external side effects (e.g. WeCom notify)
try:
printing_order_created.send(
sender=printing_models.PrintingOrder,
instance=order,
created_by=user,
)
except Exception:
# signal should never break core create flow
import logging
logging.getLogger(__name__).exception(
"[api_v1.views.printing.services] 触发 printing_order_created signal 失败(已忽略)"
)
return order
@staticmethod
def update_printing_order(
printing_order: printing_models.PrintingOrder,
data: Dict[str, Any],
user
) -> Tuple[bool, str, printing_models.PrintingOrder]:
"""
更新印染订单
Args:
printing_order: 订单实例
data: 更新数据
user: 当前用户
Returns:
(success, message, updated_order)
"""
# 如果要修改 process需要验证并同步 relink 所有 jobs 的 BusinessObject
if 'process' in data and data['process'] != printing_order.process:
new_process = data.get('process')
if new_process is None:
return False, '流程不能为空,无法修改流程', printing_order
# 只要存在任意 job 已开始(有未撤销进度)则拒绝
if not printing_order.can_change_process():
return False, '存在已开始的印染任务,无法修改流程', printing_order
from stateflow.services import relink_business_object_for_instance
try:
with transaction.atomic():
# relink 所有 jobsjobs 未开始,允许重建并重新绑定 BO
for job in printing_order.printing_jobs.select_related('business_object').all():
relinked = relink_business_object_for_instance(
instance=job,
new_process=new_process,
default_name=f"PrintingJob-{job.pk}",
default_description=f"印染任务 {job.pk} 的流程实例",
)
if relinked is None:
raise RuntimeError(f'印染任务 {job.pk} 已存在有效进度,无法修改流程')
# 最后更新订单流程
printing_order.process = new_process
printing_order.save(update_fields=['process'])
except RuntimeError as e:
return False, str(e), printing_order
# process 已处理完毕,避免后续通用字段更新再次覆盖
data = {k: v for k, v in data.items() if k != 'process'}
# 更新字段
for field, value in data.items():
setattr(printing_order, field, value)
printing_order.save()
return True, '更新成功', printing_order
@staticmethod
def can_change_process(printing_order: printing_models.PrintingOrder) -> bool:
"""
检查订单是否可以修改流程
Args:
printing_order: 订单实例
Returns:
是否可以修改
"""
return printing_order.can_change_process()
@staticmethod
def get_order_progress(printing_order: printing_models.PrintingOrder) -> int:
"""
计算订单完成进度
Args:
printing_order: 订单实例
Returns:
完成百分比 (0-100)
"""
return printing_order.progress
class PrintingJobService:
"""印染任务业务逻辑服务"""
@staticmethod
@transaction.atomic
def create_printing_job(data: Dict[str, Any], user) -> printing_models.PrintingJob:
"""
创建印染任务
自动创建关联的 BusinessObject 实例
Args:
data: 任务数据
user: 当前用户
Returns:
创建的任务实例
"""
data = dict(data)
data.pop('is_production_completed', None)
printing_order = data.get('printing_order')
# 绑定创建人
data['created_by'] = user
# 绑定商户(从当前用户的 employee 获取)
if hasattr(user, 'employee') and user.employee and user.employee.merchant:
data['merchant'] = user.employee.merchant
# 创建 PrintingJob
job = printing_models.PrintingJob.objects.create(**data)
# 如果 PrintingOrder 有关联的流程,创建 BusinessObject
if printing_order and printing_order.process:
# 将流程实例绑定到该 PrintingJob便于跨模块定位与审计
ct = ContentType.objects.get_for_model(printing_models.PrintingJob)
business_object = stateflow_models.BusinessObject.objects.create(
name=f"PrintingJob-{job.id}",
process=printing_order.process,
description=f"印染任务 {job.id} 的流程实例",
content_type=ct,
object_id=job.id,
)
job.business_object = business_object
job.save()
return job
@staticmethod
def update_printing_job(
printing_job: printing_models.PrintingJob,
data: Dict[str, Any],
user
) -> Tuple[bool, str, printing_models.PrintingJob]:
"""
更新印染任务
Args:
printing_job: 任务实例
data: 更新数据
user: 当前用户
Returns:
(success, message, updated_job)
"""
data = dict(data)
data.pop('is_production_completed', None)
# 更新字段
for field, value in data.items():
# 不允许直接修改 business_object
if field == 'business_object':
continue
setattr(printing_job, field, value)
printing_job.save()
return True, '更新成功', printing_job
@staticmethod
@transaction.atomic
def make_printing_job_production_completed(
printing_job: printing_models.PrintingJob,
triggered_by=None,
) -> printing_models.PrintingJob:
"""显式标记 PrintingJob 已完成生产,并触发领域信号。"""
if not printing_job.is_production_completed:
printing_job.is_production_completed = True
printing_job.save(update_fields=['is_production_completed', 'updated_at'])
try:
printing_job_production_completed.send(
sender=printing_models.PrintingJob,
instance=printing_job,
triggered_by=triggered_by,
)
except Exception:
import logging
logging.getLogger(__name__).exception(
"[api_v1.views.printing.services] 触发 printing_job_production_completed signal 失败(已忽略)"
)
return printing_job
@staticmethod
def get_job_status(printing_job: printing_models.PrintingJob) -> Dict[str, Any]:
"""
获取任务状态信息
Args:
printing_job: 任务实例
Returns:
状态信息字典
"""
return {
'status': printing_job.status,
'status_id': printing_job.status_id,
'is_completed': printing_job.is_completed,
'is_production_completed': printing_job.is_production_completed,
'has_started': printing_job.has_started,
}