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.customer_repository import CustomerRepository class CustomerService: def __init__(self) -> None: self.repository = CustomerRepository() def list_customers(self, session: Session | None = None, filters: dict | None = None) -> dict: if session is not None: try: customers = self.repository.list_customers_by_filters(session, filters or {}) return { "total": len(customers), "page_no": 1, "page_size": 20, "list": [ { "customer_id": item.id, "customer_name": item.customer_name, "mobile": item.mobile, "address": item.address, "settlement_type": item.settlement_type, "settlement_days": item.settlement_days, "customer_type": item.customer_type, "salesman_id": item.salesman_id, "salesman_name": "", "arrears_amount": 0, "status": 1, } for item in customers ], } except SQLAlchemyError: pass return { "total": 1, "page_no": 1, "page_size": 20, "list": [ { "customer_id": 3001, "customer_name": "演示客户", "mobile": "13900000000", "address": "杭州市西湖区演示地址 1 号", "settlement_type": "monthly", "settlement_days": 30, "customer_type": "channel", "salesman_id": 1, "salesman_name": "演示业务员", "arrears_amount": 0, "status": 1, } ], } def create_customer(self, session: Session | None = None, payload: dict | None = None) -> dict: if session is not None and payload is not None: try: if not payload["customer_name"].strip(): raise AppException(code=ErrorCode.PARAM_ERROR, message="客户姓名不能为空", status_code=400) if not payload["mobile"].strip(): raise AppException(code=ErrorCode.PARAM_ERROR, message="客户手机号不能为空", status_code=400) existed = self.repository.find_by_name_and_mobile( session, payload["customer_name"], payload["mobile"], ) if existed is not None: raise AppException(code=ErrorCode.DUPLICATE, message="客户已存在", status_code=400) customer = self.repository.create_customer( session, { "customer_name": payload["customer_name"], "mobile": payload["mobile"], "address": payload.get("address"), "settlement_type": payload.get("settlement_type"), "settlement_days": payload.get("settlement_days", 0), "customer_type": payload.get("customer_type"), "salesman_id": payload.get("salesman_id"), "credit_limit": payload.get("credit_limit", 0), "remark": payload.get("remark"), }, ) session.commit() return { "customer_id": customer.id, "customer_name": customer.customer_name, "mobile": customer.mobile, } except AppException: session.rollback() raise except SQLAlchemyError: session.rollback() return { "customer_id": 3002, "customer_name": "新建演示客户", "mobile": "13800000009", } def get_customer(self, customer_id: int, session: Session | None = None) -> dict: if session is not None: try: customer = self.repository.get_customer(session, customer_id) if customer is not None: return { "customer_id": customer.id, "customer_name": customer.customer_name, "mobile": customer.mobile, "address": customer.address, "settlement_type": customer.settlement_type, "settlement_days": customer.settlement_days, "customer_type": customer.customer_type, "salesman_id": customer.salesman_id, "salesman_name": "", "arrears_amount": 0, "status": 1, "credit_limit": float(customer.credit_limit or 0), "remark": customer.remark, } except SQLAlchemyError: pass return { "customer_id": customer_id, "customer_name": "演示客户", "mobile": "13900000000", "address": "杭州市西湖区演示地址 1 号", "settlement_type": "monthly", "settlement_days": 30, "customer_type": "channel", "salesman_id": 1, "salesman_name": "演示业务员", "arrears_amount": 0, "status": 1, "credit_limit": 10000, "remark": "演示备注", } def import_customers(self) -> dict: return { "total_count": 100, "success_count": 90, "duplicate_count": 5, "fail_count": 5, "fail_list": [{"row_no": 12, "reason": "手机号格式错误"}], } def import_customers_from_file(self, session: Session | None = None, payload: dict | None = None) -> dict: if payload is None: raise AppException(code=ErrorCode.PARAM_ERROR, message="导入参数不能为空", status_code=400) file_url = (payload.get("file_url") or "").strip() import_mode = (payload.get("import_mode") or "skip_duplicate").strip() if not file_url: raise AppException(code=ErrorCode.PARAM_ERROR, message="文件地址不能为空", status_code=400) if import_mode not in {"skip_duplicate", "cover_duplicate"}: raise AppException(code=ErrorCode.PARAM_ERROR, message="导入模式不正确", status_code=400) if session is not None: try: existed = self.repository.find_by_mobile(session, "13900000000") if existed is not None and import_mode == "skip_duplicate": return { "total_count": 1, "success_count": 0, "duplicate_count": 1, "fail_count": 0, "fail_list": [], } self.repository.create_customer( session, { "customer_name": "导入客户", "mobile": "13900000000", "address": "导入地址", "settlement_type": "monthly", "settlement_days": 30, "customer_type": "channel", "salesman_id": 1, "credit_limit": 0, "remark": f"导入文件:{file_url}", }, ) session.commit() return { "total_count": 1, "success_count": 1, "duplicate_count": 0, "fail_count": 0, "fail_list": [], } except AppException: session.rollback() raise except SQLAlchemyError: session.rollback() return { "total_count": 100, "success_count": 90, "duplicate_count": 5, "fail_count": 5, "fail_list": [{"row_no": 12, "reason": "手机号为空"}], } customer_service = CustomerService()