import json as _json from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from starlette.middleware.base import BaseHTTPMiddleware from app.config import get_settings settings = get_settings() class DecryptMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): encrypt_key = request.headers.get("encrypt-key") if encrypt_key and request.method in ("POST", "PUT"): try: from app.utils.crypto import try_decrypt_request body = await request.body() decrypted = try_decrypt_request(encrypt_key, body.decode()) request._body = _json.dumps(decrypted).encode() except Exception: pass return await call_next(request) def create_app() -> FastAPI: app = FastAPI( title="CRM 后端 API", version="1.0.0", docs_url="/swagger-ui/index.html", openapi_url="/v3/api-docs", ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.add_middleware(DecryptMiddleware) from app.routers.auth import router as auth_router from app.routers.system import router as system_router from app.routers.crm.customer import ( router as crm_customer_router, follow_router as crm_follow_router, tag_router as crm_tag_router, pool_statistics_router as crm_pool_stats_router, pool_rule_router as crm_pool_rule_router, ) from app.routers.crm.clue import ( router as crm_clue_router, follow_router as crm_clue_follow_router, ) from app.routers.crm.contact import router as crm_contact_router from app.routers.crm.opportunity import ( router as crm_opportunity_router, follow_router as crm_opportunity_follow_router, product_router as crm_opportunity_product_router, stage_log_router as crm_stage_log_router, ) from app.routers.crm.product import ( router as crm_product_router, category_router as crm_product_category_router, tag_router as crm_product_tag_router, tag_relation_router as crm_product_tag_relation_router, attachment_router as crm_product_attachment_router, price_router as crm_product_price_router, price_history_router as crm_product_price_history_router, stock_router as crm_product_stock_router, ) from app.routers.crm.quote import ( router as crm_quote_router, product_router as crm_quote_product_router, approval_router as crm_quote_approval_router, ) from app.routers.crm.contract import router as crm_contract_router from app.routers.crm.payment import ( router as crm_payment_router, plan_router as crm_payment_plan_router, collection_router as crm_payment_collection_router, ) from app.routers.crm.invoice import ( router as crm_invoice_router, info_router as crm_invoice_info_router, item_router as crm_invoice_item_router, ) from app.routers.crm.visit import ( router as crm_visit_router, plan_router as crm_visit_plan_router, problem_router as crm_visit_problem_router, ) from app.routers.crm.supplier import router as crm_supplier_router app.include_router(auth_router, prefix="/auth", tags=["认证"]) app.include_router(system_router, prefix="/system", tags=["系统管理"]) app.include_router(crm_customer_router, prefix="/crm/customer", tags=["客户管理"]) app.include_router(crm_follow_router, prefix="/crm/customer/follow", tags=["客户跟进"]) app.include_router(crm_tag_router, prefix="/crm/customer/tag", tags=["客户标签"]) app.include_router(crm_pool_stats_router, prefix="/crm/customer/pool/statistics", tags=["公海池统计"]) app.include_router(crm_pool_rule_router, prefix="/crm/customer/pool/rule", tags=["公海池规则"]) app.include_router(crm_clue_router, prefix="/crm/clue", tags=["线索管理"]) app.include_router(crm_clue_follow_router, prefix="/crm/clue/follow", tags=["线索跟进"]) app.include_router(crm_contact_router, prefix="/crm/contact", tags=["联系人管理"]) app.include_router(crm_opportunity_router, prefix="/crm/opportunity", tags=["商机管理"]) app.include_router(crm_opportunity_follow_router, prefix="/crm/opportunity/follow", tags=["商机跟进"]) app.include_router(crm_opportunity_product_router, prefix="/crm/opportunity/product", tags=["商机产品"]) app.include_router(crm_stage_log_router, prefix="/crm/opportunity/stageLog", tags=["商机阶段日志"]) app.include_router(crm_product_router, prefix="/crm/product", tags=["产品管理"]) app.include_router(crm_product_category_router, prefix="/crm/product/category", tags=["产品分类"]) app.include_router(crm_product_tag_router, prefix="/crm/product/tag", tags=["产品标签"]) app.include_router(crm_product_tag_relation_router, prefix="/crm/product/tagRelation", tags=["产品标签关联"]) app.include_router(crm_product_attachment_router, prefix="/crm/product/attachment", tags=["产品附件"]) app.include_router(crm_product_price_router, prefix="/crm/product/price", tags=["产品价格"]) app.include_router(crm_product_price_history_router, prefix="/crm/product/price/history", tags=["产品价格历史"]) app.include_router(crm_product_stock_router, prefix="/crm/product/stock", tags=["产品库存"]) app.include_router(crm_quote_router, prefix="/crm/quote", tags=["报价管理"]) app.include_router(crm_quote_product_router, prefix="/crm/quote/product", tags=["报价产品"]) app.include_router(crm_quote_approval_router, prefix="/crm/quote/approval", tags=["报价审批"]) app.include_router(crm_contract_router, prefix="/crm/contract", tags=["合同管理"]) app.include_router(crm_payment_router, prefix="/crm/payment", tags=["回款管理"]) app.include_router(crm_payment_plan_router, prefix="/crm/payment/plan", tags=["回款计划"]) app.include_router(crm_payment_collection_router, prefix="/crm/payment/collection", tags=["催收管理"]) app.include_router(crm_invoice_router, prefix="/crm/invoice", tags=["发票管理"]) app.include_router(crm_invoice_info_router, prefix="/crm/invoiceInfo", tags=["开票信息"]) app.include_router(crm_invoice_item_router, prefix="/crm/invoice/item", tags=["发票明细"]) app.include_router(crm_visit_router, prefix="/crm/visit", tags=["回访管理"]) app.include_router(crm_visit_plan_router, prefix="/crm/visit/plan", tags=["回访计划"]) app.include_router(crm_visit_problem_router, prefix="/crm/visit/problem", tags=["回访问题"]) app.include_router(crm_supplier_router, prefix="/crm/supplierInfo", tags=["供应商管理"]) @app.get("/resource/sse") async def sse_endpoint(Authorization: str = "", clientid: str = ""): from starlette.responses import StreamingResponse async def event_generator(): yield "event: heartbeat\ndata: \n\n" return StreamingResponse(event_generator(), media_type="text/event-stream") @app.get("/resource/sse/close") def sse_close(): return {"code": 200, "msg": "ok"} @app.get("/resource/oss/listByIds/{oss_id}") def list_oss_by_ids(oss_id: str): from app.database import SessionLocal from app.models.system import SysOss db = SessionLocal() try: ids = [] for part in str(oss_id).split(","): part = part.strip() if part.isdigit(): ids.append(int(part)) if not ids: return {"code": 200, "msg": "操作成功", "data": []} rows = db.query(SysOss).filter(SysOss.oss_id.in_(ids)).all() return {"code": 200, "msg": "操作成功", "data": [{ "ossId": o.oss_id, "fileName": o.file_name, "originalName": o.original_name, "fileSuffix": o.file_suffix, "url": o.url, "size": getattr(o, "size", ""), } for o in rows]} finally: db.close() @app.get("/health") def health_check(): return {"code": 200, "msg": "ok"} @app.exception_handler(Exception) async def global_exception_handler(request, exc): return JSONResponse( status_code=200, content={"code": 500, "msg": str(exc), "data": None}, ) from app.scheduler import start_scheduler start_scheduler() return app app = create_app() if __name__ == "__main__": import uvicorn uvicorn.run( "app.main:app", host=settings.APP_HOST, port=settings.APP_PORT, reload=settings.DEBUG, )