203 lines
8.4 KiB
Python
203 lines
8.4 KiB
Python
from datetime import datetime, timedelta
|
|
|
|
from sqlalchemy.exc import SQLAlchemyError
|
|
from sqlalchemy.orm import Session
|
|
|
|
from backend.app.core.error_codes import ErrorCode
|
|
from backend.app.core.exceptions import AppException
|
|
from backend.app.repositories.config_repository import ConfigRepository
|
|
from backend.app.repositories.reminder_repository import ReminderRepository
|
|
|
|
|
|
class ReminderService:
|
|
def __init__(self) -> None:
|
|
self.repository = ReminderRepository()
|
|
self.config_repository = ConfigRepository()
|
|
|
|
def list_reminders(self, session: Session | None = None, filters: dict | None = None) -> dict:
|
|
filters = filters or {}
|
|
if session is not None:
|
|
try:
|
|
reminders = self.repository.list_reminders(session, filters)
|
|
page_no = filters.get("page_no", 1)
|
|
page_size = filters.get("page_size", 20)
|
|
start = max(page_no - 1, 0) * page_size
|
|
page_list = reminders[start : start + page_size]
|
|
return {
|
|
"total": len(reminders),
|
|
"page_no": page_no,
|
|
"page_size": page_size,
|
|
"list": [self._map_reminder(item) for item in page_list],
|
|
}
|
|
except SQLAlchemyError:
|
|
pass
|
|
|
|
return {
|
|
"total": 1,
|
|
"page_no": 1,
|
|
"page_size": 20,
|
|
"list": [
|
|
{
|
|
"reminder_id": 6001,
|
|
"title": "订单待审批",
|
|
"type": "order_approve",
|
|
"biz_type": "sales_order",
|
|
"biz_id": 9001,
|
|
"receiver_user_id": 1,
|
|
"status": "pending",
|
|
"sent_at": "2026-05-14 11:00:00",
|
|
"created_at": "2026-05-14 11:00:00",
|
|
}
|
|
],
|
|
}
|
|
|
|
def read_reminder(self, reminder_id: int, session: Session | None = None) -> dict:
|
|
if session is not None:
|
|
try:
|
|
reminder = self.repository.get_reminder(session, reminder_id)
|
|
if reminder is None:
|
|
raise AppException(code=ErrorCode.NOT_FOUND, message="提醒不存在", status_code=404)
|
|
reminder.status = "read"
|
|
session.add(reminder)
|
|
session.commit()
|
|
return {"reminder_id": reminder.id, "status": reminder.status}
|
|
except AppException:
|
|
session.rollback()
|
|
raise
|
|
except SQLAlchemyError:
|
|
session.rollback()
|
|
|
|
return {"reminder_id": reminder_id, "status": "read"}
|
|
|
|
def check_arrears(self, session: Session | None = None) -> dict:
|
|
if session is not None:
|
|
try:
|
|
rows = self.repository.list_overdue_arrears(session)
|
|
created_count = 0
|
|
for arrears, customer, order in rows:
|
|
if arrears.due_date is None or arrears.due_date >= datetime.now().date():
|
|
continue
|
|
|
|
receiver_user_id = (customer.salesman_id if customer and customer.salesman_id else 0) or 0
|
|
if receiver_user_id <= 0:
|
|
continue
|
|
|
|
existed = self.repository.find_active_reminder(
|
|
session,
|
|
"arrears",
|
|
"customer_arrears",
|
|
arrears.id,
|
|
receiver_user_id,
|
|
)
|
|
if existed is not None:
|
|
continue
|
|
|
|
title = f"客户欠款逾期提醒 - {customer.customer_name if customer else '未知客户'}"
|
|
content = (
|
|
f"客户存在逾期欠款,欠款金额 {float(arrears.arrears_amount or 0):.2f} 元,"
|
|
f"订单编号 {order.order_no if order else '-'},请及时跟进。"
|
|
)
|
|
self.repository.create_reminder(
|
|
session,
|
|
{
|
|
"reminder_type": "arrears",
|
|
"biz_type": "customer_arrears",
|
|
"biz_id": arrears.id,
|
|
"receiver_user_id": receiver_user_id,
|
|
"reminder_title": title,
|
|
"reminder_content": content,
|
|
"status": "pending",
|
|
"sent_at": datetime.now(),
|
|
},
|
|
)
|
|
arrears.reminded_at = datetime.now()
|
|
arrears.status = "overdue"
|
|
session.add(arrears)
|
|
created_count += 1
|
|
|
|
session.commit()
|
|
return {"checked": True, "created_count": created_count}
|
|
except AppException:
|
|
session.rollback()
|
|
raise
|
|
except SQLAlchemyError:
|
|
session.rollback()
|
|
|
|
return {"checked": True, "created_count": 0}
|
|
|
|
def check_inactive_customers(self, session: Session | None = None) -> dict:
|
|
if session is not None:
|
|
try:
|
|
inactive_days = int(self._get_config_value(session, "inactive_customer_days", "90"))
|
|
amount_threshold = float(self._get_config_value(session, "inactive_customer_amount_threshold", "1000"))
|
|
inactive_before = datetime.now() - timedelta(days=inactive_days)
|
|
rows = self.repository.list_inactive_customers(session, inactive_before)
|
|
|
|
created_count = 0
|
|
for customer, latest_order in rows:
|
|
if float(customer.credit_limit or 0) < amount_threshold:
|
|
continue
|
|
receiver_user_id = (customer.salesman_id or 0)
|
|
if receiver_user_id <= 0:
|
|
continue
|
|
|
|
existed = self.repository.find_active_reminder(
|
|
session,
|
|
"inactive_customer",
|
|
"customer",
|
|
customer.id,
|
|
receiver_user_id,
|
|
)
|
|
if existed is not None:
|
|
continue
|
|
|
|
last_order_time = (
|
|
latest_order.created_at.strftime("%Y-%m-%d %H:%M:%S")
|
|
if latest_order and latest_order.created_at
|
|
else "暂无订单"
|
|
)
|
|
self.repository.create_reminder(
|
|
session,
|
|
{
|
|
"reminder_type": "inactive_customer",
|
|
"biz_type": "customer",
|
|
"biz_id": customer.id,
|
|
"receiver_user_id": receiver_user_id,
|
|
"reminder_title": f"沉默客户提醒 - {customer.customer_name}",
|
|
"reminder_content": f"客户超过 {inactive_days} 天未下单,最近订单时间:{last_order_time}。",
|
|
"status": "pending",
|
|
"sent_at": datetime.now(),
|
|
},
|
|
)
|
|
created_count += 1
|
|
|
|
session.commit()
|
|
return {"checked": True, "created_count": created_count}
|
|
except AppException:
|
|
session.rollback()
|
|
raise
|
|
except SQLAlchemyError:
|
|
session.rollback()
|
|
|
|
return {"checked": True, "created_count": 0}
|
|
|
|
def _get_config_value(self, session: Session, config_key: str, default: str) -> str:
|
|
config = self.config_repository.get_by_key(session, config_key)
|
|
return config.config_value if config is not None and config.config_value else default
|
|
|
|
def _map_reminder(self, reminder) -> dict:
|
|
return {
|
|
"reminder_id": reminder.id,
|
|
"title": reminder.reminder_title,
|
|
"type": reminder.reminder_type,
|
|
"biz_type": reminder.biz_type,
|
|
"biz_id": reminder.biz_id,
|
|
"receiver_user_id": reminder.receiver_user_id,
|
|
"status": reminder.status,
|
|
"sent_at": reminder.sent_at.strftime("%Y-%m-%d %H:%M:%S") if reminder.sent_at else "",
|
|
"created_at": reminder.created_at.strftime("%Y-%m-%d %H:%M:%S") if reminder.created_at else "",
|
|
}
|
|
|
|
|
|
reminder_service = ReminderService()
|