v0.1.0: CRM/ERP 系统内测版本 - 安全加固完成

- Docker bridge 网络隔离(8000 端口封死)
- Gunicorn 4 Worker 多进程
- Alembic 数据库迁移基线
- 日志轮转 20m×3
- JWT 密钥 + DB 密码 + CORS 收紧
- 3-2-1 备份链路(NAS + R740-B 冷备)
- 连接池 pool_pre_ping + pool_recycle=3600
This commit is contained in:
hankin
2026-03-16 07:31:37 +00:00
commit 423baff73b
2578 changed files with 824643 additions and 0 deletions
View File
+37
View File
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
"""
认证端点
处理用户登录,签发 JWT 令牌。
"""
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_db
from app.core.security import create_access_token
from app.crud.user import authenticate_user
from app.schemas.user import Token, UserLogin
router = APIRouter()
@router.post("/login", response_model=Token, summary="用户登录", tags=["认证"])
async def login(body: UserLogin, db: AsyncSession = Depends(get_db)):
"""
校验用户名密码,成功后签发 JWT access_token。
前端后续请求需在 Authorization 头携带 Bearer <token>。
"""
user = await authenticate_user(db, body.username, body.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="用户名或密码错误",
headers={"WWW-Authenticate": "Bearer"},
)
token = create_access_token(subject=user.username, role=user.role)
return Token(
access_token=token,
role=user.role,
username=user.username,
)
+31
View File
@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
"""
健康检查端点
用于 Nginx/LB 探活和数据库连接状态探测。
"""
from fastapi import APIRouter, Depends
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_db
router = APIRouter()
@router.get("/health", summary="健康检查", tags=["系统"])
async def health_check(db: AsyncSession = Depends(get_db)):
"""
探测服务与数据库连接是否存活。
- 数据库可达 → {"status": "healthy", "database": "connected"}
- 数据库不可达 → {"status": "degraded", "database": "disconnected", "detail": "..."}
"""
try:
await db.execute(text("SELECT 1"))
return {"status": "healthy", "database": "connected"}
except Exception as e:
return {
"status": "degraded",
"database": "disconnected",
"detail": str(e),
}
+81
View File
@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
"""
客户沟通日志 API
POST /api/v1/logs - 提交日志并触发后台 AI 标签提取
"""
import uuid
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status
from pydantic import BaseModel, Field
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import get_current_user
from app.core.database import get_db
from app.models.crm_business import CustomerLog
from app.services.ai_workflow import process_log_with_ai
router = APIRouter()
# ---- 请求/响应模型 ----
class LogCreate(BaseModel):
"""提交沟通日志请求"""
customer_id: uuid.UUID = Field(..., description="关联客户 ID")
content: str = Field(..., min_length=5, max_length=5000, description="沟通日志内容")
class LogResponse(BaseModel):
"""提交成功响应"""
id: uuid.UUID
message: str = "日志已提交,AI 正在后台分析标签和待办"
# ---- 路由 ----
@router.post(
"",
response_model=LogResponse,
status_code=status.HTTP_200_OK,
summary="提交客户沟通日志",
tags=["客户日志"],
)
async def create_customer_log(
body: LogCreate,
background_tasks: BackgroundTasks,
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(get_current_user),
):
"""
接收前端提交的客户沟通日志:
1. 立即存入 customer_logs 表
2. 将 AI 标签提取任务加入 BackgroundTasks 后台队列
3. 立即返回 200 OK(不等待 AI 处理完成)
AI 后台任务会:
- 调用 qwen3:14b 分析日志内容
- 自动提取最多 3 个客户标签 → customer_tags
- 自动生成 1 个跟进待办 → follow_up_todos
"""
# Step 1: 立即写入日志记录
log = CustomerLog(
customer_id=body.customer_id,
content=body.content,
)
db.add(log)
await db.flush()
await db.refresh(log)
# Step 2: 将 AI 处理加入后台队列
# *** 关键:传入 log.id / body.content / body.customer_id 三个值 ***
# process_log_with_ai 会创建独立的 DB Session,不与当前请求的 db 共享
background_tasks.add_task(
process_log_with_ai,
log_id=log.id,
content=body.content,
customer_id=body.customer_id,
)
# Step 3: 立即返回(不等待 AI)
return LogResponse(id=log.id)
+55
View File
@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
"""
销售复盘报告 API
GET /api/v1/reports/monthly - 获取当月销售复盘报告 (AI 生成)
"""
from fastapi import APIRouter, Depends
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import get_current_user
from app.core.database import get_db
from app.services.analytics import generate_monthly_report
router = APIRouter()
# ---- 响应模型 ----
class StageMetric(BaseModel):
"""单个阶段的统计指标"""
stage: str
count: int
total_amount: float
class MonthlyReportResponse(BaseModel):
"""月度复盘报告响应"""
metrics: list[StageMetric]
report: str
# ---- 路由 ----
@router.get(
"/monthly",
response_model=MonthlyReportResponse,
summary="获取当月销售复盘报告",
tags=["数据报告"],
)
async def get_monthly_report(
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(get_current_user),
):
"""
生成当月销售复盘报告:
1. SQL 预聚合统计各阶段的机会数量和金额
2. 将真实数据注入 Prompt,调用 qwen3:14b 生成分析报告
3. 同步返回结构化数据 + AI 报告文本
注意:此接口为同步等待模式(用户主动触发),
AI 生成可能需要 10-30 秒,前端应显示加载状态。
"""
result = await generate_monthly_report(db)
return MonthlyReportResponse(**result)
+22
View File
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
"""
API v1 路由汇总
所有 v1 版本的子路由在此注册,由 main.py 统一挂载到 /api/v1 前缀。
"""
from fastapi import APIRouter
from app.api.v1.endpoints import auth, health, logs, reports
api_v1_router = APIRouter()
# 挂载各业务模块路由
api_v1_router.include_router(health.router, prefix="", tags=["系统"])
api_v1_router.include_router(auth.router, prefix="/auth", tags=["认证"])
api_v1_router.include_router(logs.router, prefix="/logs", tags=["客户日志"])
api_v1_router.include_router(reports.router, prefix="/reports", tags=["数据报告"])
# 后续新增模块在此追加,例如:
# from app.api.v1.endpoints import clients, expenses
# api_v1_router.include_router(clients.router, prefix="/clients", tags=["客户管理"])
# api_v1_router.include_router(expenses.router, prefix="/expenses", tags=["报销管理"])