This commit is contained in:
2026-03-04 00:47:41 +08:00
parent 54f83cd76c
commit f3196f3e87
9 changed files with 2929 additions and 650 deletions
+193
View File
@@ -0,0 +1,193 @@
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.ext.asyncio import AsyncSession
from pydantic import BaseModel, EmailStr
from typing import Optional
from datetime import timedelta
from database.database import get_db_session
from services.auth_service import (
authenticate_user,
create_access_token,
create_user,
get_user_by_username,
get_user_by_email,
get_current_active_user,
get_current_admin_user
)
from models.database import User
from config.settings import settings
router = APIRouter(prefix="/api/auth", tags=["认证"])
class UserCreate(BaseModel):
username: str
email: EmailStr
password: str
full_name: Optional[str] = None
class UserResponse(BaseModel):
id: int
username: str
email: str
full_name: Optional[str]
is_active: bool
is_superuser: bool
class Config:
from_attributes = True
class Token(BaseModel):
access_token: str
token_type: str
user: UserResponse
class LoginRequest(BaseModel):
username: str
password: str
@router.post("/login", response_model=Token)
async def login(
form_data: OAuth2PasswordRequestForm = Depends(),
db_session: AsyncSession = Depends(get_db_session)
):
user = await authenticate_user(db_session, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="用户名或密码错误",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return Token(
access_token=access_token,
token_type="bearer",
user=UserResponse.from_orm(user)
)
@router.post("/login/json", response_model=Token)
async def login_json(
login_data: LoginRequest,
db_session: AsyncSession = Depends(get_db_session)
):
user = await authenticate_user(db_session, login_data.username, login_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="用户名或密码错误",
)
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return Token(
access_token=access_token,
token_type="bearer",
user=UserResponse.from_orm(user)
)
@router.post("/register", response_model=UserResponse, status_code=201)
async def register(
user_data: UserCreate,
db_session: AsyncSession = Depends(get_db_session)
):
existing_user = await get_user_by_username(db_session, user_data.username)
if existing_user:
raise HTTPException(status_code=400, detail="用户名已存在")
existing_email = await get_user_by_email(db_session, user_data.email)
if existing_email:
raise HTTPException(status_code=400, detail="邮箱已被注册")
user = await create_user(
db_session=db_session,
username=user_data.username,
email=user_data.email,
password=user_data.password,
full_name=user_data.full_name
)
return UserResponse.from_orm(user)
@router.get("/me", response_model=UserResponse)
async def get_current_user_info(
current_user: User = Depends(get_current_active_user)
):
return UserResponse.from_orm(current_user)
@router.post("/logout")
async def logout():
return {"message": "已登出"}
@router.get("/users", response_model=list[UserResponse])
async def list_users(
db_session: AsyncSession = Depends(get_db_session),
admin_user: User = Depends(get_current_admin_user)
):
from sqlalchemy import select
result = await db_session.execute(select(User))
users = result.scalars().all()
return [UserResponse.from_orm(u) for u in users]
@router.put("/users/{user_id}/toggle-active", response_model=UserResponse)
async def toggle_user_active(
user_id: int,
db_session: AsyncSession = Depends(get_db_session),
admin_user: User = Depends(get_current_admin_user)
):
from sqlalchemy import select
result = await db_session.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
if user.id == admin_user.id:
raise HTTPException(status_code=400, detail="不能禁用自己的账户")
user.is_active = not user.is_active
await db_session.commit()
await db_session.refresh(user)
return UserResponse.from_orm(user)
@router.put("/users/{user_id}/toggle-admin", response_model=UserResponse)
async def toggle_user_admin(
user_id: int,
db_session: AsyncSession = Depends(get_db_session),
admin_user: User = Depends(get_current_admin_user)
):
from sqlalchemy import select
result = await db_session.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
if user.id == admin_user.id:
raise HTTPException(status_code=400, detail="不能修改自己的管理员权限")
user.is_superuser = not user.is_superuser
await db_session.commit()
await db_session.refresh(user)
return UserResponse.from_orm(user)
+769
View File
@@ -0,0 +1,769 @@
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, and_, or_
from sqlalchemy.orm import selectinload
from pydantic import BaseModel
from typing import Optional, List
from datetime import datetime
import uuid
from database.database import get_db_session
from services.auth_service import get_current_active_user, get_current_admin_user
from models.database import (
User, Product, Supplier, Customer, Warehouse, Inventory,
StockMovement, PurchaseOrder, PurchaseOrderItem,
SalesOrder, SalesOrderItem
)
router = APIRouter(prefix="/api/inventory", tags=["进销存"])
def generate_order_no(prefix: str) -> str:
date_str = datetime.now().strftime("%Y%m%d%H%M%S")
random_str = uuid.uuid4().hex[:4].upper()
return f"{prefix}{date_str}{random_str}"
class ProductCreate(BaseModel):
sku: str
name: str
description: Optional[str] = None
category: Optional[str] = None
unit: str = "件"
cost_price: float = 0
sale_price: float = 0
min_stock: int = 0
max_stock: int = 1000
class ProductResponse(BaseModel):
id: int
sku: str
name: str
description: Optional[str]
category: Optional[str]
unit: str
cost_price: float
sale_price: float
min_stock: int
max_stock: int
is_active: bool
created_at: datetime
class Config:
from_attributes = True
class SupplierCreate(BaseModel):
code: Optional[str] = None
name: str
contact_person: Optional[str] = None
phone: Optional[str] = None
email: Optional[str] = None
address: Optional[str] = None
class SupplierResponse(BaseModel):
id: int
code: Optional[str]
name: str
contact_person: Optional[str]
phone: Optional[str]
email: Optional[str]
is_active: bool
class Config:
from_attributes = True
class CustomerCreate(BaseModel):
code: Optional[str] = None
name: str
contact_person: Optional[str] = None
phone: Optional[str] = None
email: Optional[str] = None
address: Optional[str] = None
class CustomerResponse(BaseModel):
id: int
code: Optional[str]
name: str
contact_person: Optional[str]
phone: Optional[str]
email: Optional[str]
is_active: bool
class Config:
from_attributes = True
class WarehouseCreate(BaseModel):
code: Optional[str] = None
name: str
address: Optional[str] = None
manager: Optional[str] = None
phone: Optional[str] = None
class WarehouseResponse(BaseModel):
id: int
code: Optional[str]
name: str
address: Optional[str]
manager: Optional[str]
is_active: bool
is_default: bool
class Config:
from_attributes = True
class InventoryResponse(BaseModel):
id: int
product_id: int
product_name: str
product_sku: str
warehouse_id: int
warehouse_name: str
quantity: int
locked_quantity: int
available_quantity: int
class Config:
from_attributes = True
class StockMovementCreate(BaseModel):
product_id: int
warehouse_id: int
movement_type: str
quantity: int
unit_price: Optional[float] = None
remark: Optional[str] = None
class StockMovementResponse(BaseModel):
id: int
product_name: str
movement_type: str
quantity: int
before_quantity: int
after_quantity: int
reference_no: Optional[str]
remark: Optional[str]
created_at: datetime
class Config:
from_attributes = True
class PurchaseOrderItemCreate(BaseModel):
product_id: int
quantity: int
unit_price: float
remark: Optional[str] = None
class PurchaseOrderCreate(BaseModel):
supplier_id: int
expected_date: Optional[datetime] = None
remark: Optional[str] = None
items: List[PurchaseOrderItemCreate]
class PurchaseOrderResponse(BaseModel):
id: int
order_no: str
supplier_name: str
order_date: datetime
expected_date: Optional[datetime]
status: str
total_amount: float
paid_amount: float
remark: Optional[str]
created_at: datetime
class Config:
from_attributes = True
class SalesOrderItemCreate(BaseModel):
product_id: int
quantity: int
unit_price: float
remark: Optional[str] = None
class SalesOrderCreate(BaseModel):
customer_id: int
delivery_date: Optional[datetime] = None
remark: Optional[str] = None
items: List[SalesOrderItemCreate]
class SalesOrderResponse(BaseModel):
id: int
order_no: str
customer_name: str
order_date: datetime
delivery_date: Optional[datetime]
status: str
total_amount: float
received_amount: float
remark: Optional[str]
created_at: datetime
class Config:
from_attributes = True
@router.get("/dashboard")
async def get_dashboard(
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
product_count = await db_session.scalar(select(func.count(Product.id)).where(Product.is_active == True))
supplier_count = await db_session.scalar(select(func.count(Supplier.id)).where(Supplier.is_active == True))
customer_count = await db_session.scalar(select(func.count(Customer.id)).where(Customer.is_active == True))
warehouse_count = await db_session.scalar(select(func.count(Warehouse.id)).where(Warehouse.is_active == True))
total_stock = await db_session.scalar(select(func.sum(Inventory.quantity))) or 0
total_value = await db_session.scalar(
select(func.sum(Inventory.quantity * Product.cost_price))
.join(Product, Inventory.product_id == Product.id)
) or 0
pending_purchase = await db_session.scalar(
select(func.count(PurchaseOrder.id)).where(PurchaseOrder.status == "pending")
)
pending_sales = await db_session.scalar(
select(func.count(SalesOrder.id)).where(SalesOrder.status == "pending")
)
low_stock_products = await db_session.execute(
select(Product, Inventory)
.join(Inventory, Product.id == Inventory.product_id)
.where(Inventory.quantity <= Product.min_stock)
.limit(10)
)
low_stock = [
{"id": p.id, "name": p.name, "sku": p.sku, "quantity": i.quantity, "min_stock": p.min_stock}
for p, i in low_stock_products.all()
]
return {
"product_count": product_count,
"supplier_count": supplier_count,
"customer_count": customer_count,
"warehouse_count": warehouse_count,
"total_stock": total_stock,
"total_value": round(total_value, 2),
"pending_purchase": pending_purchase,
"pending_sales": pending_sales,
"low_stock_products": low_stock
}
@router.get("/products", response_model=List[ProductResponse])
async def list_products(
skip: int = Query(0, ge=0),
limit: int = Query(20, ge=1, le=100),
search: Optional[str] = None,
category: Optional[str] = None,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
query = select(Product).where(Product.is_active == True)
if search:
query = query.where(or_(Product.name.ilike(f"%{search}%"), Product.sku.ilike(f"%{search}%")))
if category:
query = query.where(Product.category == category)
query = query.offset(skip).limit(limit).order_by(Product.created_at.desc())
result = await db_session.execute(query)
products = result.scalars().all()
return [ProductResponse.from_orm(p) for p in products]
@router.post("/products", response_model=ProductResponse, status_code=201)
async def create_product(
product_data: ProductCreate,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
existing = await db_session.execute(select(Product).where(Product.sku == product_data.sku))
if existing.scalar_one_or_none():
raise HTTPException(status_code=400, detail="SKU已存在")
product = Product(**product_data.dict())
db_session.add(product)
await db_session.commit()
await db_session.refresh(product)
return ProductResponse.from_orm(product)
@router.put("/products/{product_id}", response_model=ProductResponse)
async def update_product(
product_id: int,
product_data: ProductCreate,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
result = await db_session.execute(select(Product).where(Product.id == product_id))
product = result.scalar_one_or_none()
if not product:
raise HTTPException(status_code=404, detail="产品不存在")
for key, value in product_data.dict().items():
setattr(product, key, value)
await db_session.commit()
await db_session.refresh(product)
return ProductResponse.from_orm(product)
@router.delete("/products/{product_id}")
async def delete_product(
product_id: int,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_admin_user)
):
result = await db_session.execute(select(Product).where(Product.id == product_id))
product = result.scalar_one_or_none()
if not product:
raise HTTPException(status_code=404, detail="产品不存在")
product.is_active = False
await db_session.commit()
return {"message": "产品已删除"}
@router.get("/suppliers", response_model=List[SupplierResponse])
async def list_suppliers(
skip: int = Query(0, ge=0),
limit: int = Query(20, ge=1, le=100),
search: Optional[str] = None,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
query = select(Supplier).where(Supplier.is_active == True)
if search:
query = query.where(Supplier.name.ilike(f"%{search}%"))
query = query.offset(skip).limit(limit).order_by(Supplier.created_at.desc())
result = await db_session.execute(query)
return [SupplierResponse.from_orm(s) for s in result.scalars().all()]
@router.post("/suppliers", response_model=SupplierResponse, status_code=201)
async def create_supplier(
supplier_data: SupplierCreate,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
data = supplier_data.dict()
if not data.get("code"):
data["code"] = f"S{datetime.now().strftime('%Y%m%d%H%M%S')}"
supplier = Supplier(**data)
db_session.add(supplier)
await db_session.commit()
await db_session.refresh(supplier)
return SupplierResponse.from_orm(supplier)
@router.get("/customers", response_model=List[CustomerResponse])
async def list_customers(
skip: int = Query(0, ge=0),
limit: int = Query(20, ge=1, le=100),
search: Optional[str] = None,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
query = select(Customer).where(Customer.is_active == True)
if search:
query = query.where(Customer.name.ilike(f"%{search}%"))
query = query.offset(skip).limit(limit).order_by(Customer.created_at.desc())
result = await db_session.execute(query)
return [CustomerResponse.from_orm(c) for c in result.scalars().all()]
@router.post("/customers", response_model=CustomerResponse, status_code=201)
async def create_customer(
customer_data: CustomerCreate,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
data = customer_data.dict()
if not data.get("code"):
data["code"] = f"C{datetime.now().strftime('%Y%m%d%H%M%S')}"
customer = Customer(**data)
db_session.add(customer)
await db_session.commit()
await db_session.refresh(customer)
return CustomerResponse.from_orm(customer)
@router.get("/warehouses", response_model=List[WarehouseResponse])
async def list_warehouses(
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
result = await db_session.execute(
select(Warehouse).where(Warehouse.is_active == True).order_by(Warehouse.is_default.desc())
)
return [WarehouseResponse.from_orm(w) for w in result.scalars().all()]
@router.post("/warehouses", response_model=WarehouseResponse, status_code=201)
async def create_warehouse(
warehouse_data: WarehouseCreate,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
data = warehouse_data.dict()
if not data.get("code"):
data["code"] = f"W{datetime.now().strftime('%Y%m%d%H%M%S')}"
warehouse = Warehouse(**data)
db_session.add(warehouse)
await db_session.commit()
await db_session.refresh(warehouse)
return WarehouseResponse.from_orm(warehouse)
@router.get("/inventory", response_model=List[InventoryResponse])
async def list_inventory(
warehouse_id: Optional[int] = None,
product_id: Optional[int] = None,
low_stock: bool = False,
skip: int = Query(0, ge=0),
limit: int = Query(20, ge=1, le=100),
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
query = (
select(Inventory, Product, Warehouse)
.join(Product, Inventory.product_id == Product.id)
.join(Warehouse, Inventory.warehouse_id == Warehouse.id)
.where(Product.is_active == True)
.where(Warehouse.is_active == True)
)
if warehouse_id:
query = query.where(Inventory.warehouse_id == warehouse_id)
if product_id:
query = query.where(Inventory.product_id == product_id)
if low_stock:
query = query.where(Inventory.quantity <= Product.min_stock)
query = query.offset(skip).limit(limit)
result = await db_session.execute(query)
inventory_list = []
for inv, product, warehouse in result.all():
inventory_list.append(InventoryResponse(
id=inv.id,
product_id=inv.product_id,
product_name=product.name,
product_sku=product.sku,
warehouse_id=inv.warehouse_id,
warehouse_name=warehouse.name,
quantity=inv.quantity,
locked_quantity=inv.locked_quantity,
available_quantity=inv.available_quantity
))
return inventory_list
@router.post("/stock-movements", response_model=StockMovementResponse, status_code=201)
async def create_stock_movement(
movement_data: StockMovementCreate,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
if movement_data.movement_type not in ["in", "out", "adjust"]:
raise HTTPException(status_code=400, detail="无效的变动类型")
result = await db_session.execute(
select(Inventory)
.where(Inventory.product_id == movement_data.product_id)
.where(Inventory.warehouse_id == movement_data.warehouse_id)
)
inventory = result.scalar_one_or_none()
if not inventory:
if movement_data.movement_type == "out":
raise HTTPException(status_code=400, detail="库存不足")
inventory = Inventory(
product_id=movement_data.product_id,
warehouse_id=movement_data.warehouse_id,
quantity=0
)
db_session.add(inventory)
await db_session.flush()
before_qty = inventory.quantity
if movement_data.movement_type == "in":
inventory.quantity += movement_data.quantity
elif movement_data.movement_type == "out":
if inventory.quantity < movement_data.quantity:
raise HTTPException(status_code=400, detail="库存不足")
inventory.quantity -= movement_data.quantity
else:
inventory.quantity = movement_data.quantity
after_qty = inventory.quantity
movement = StockMovement(
product_id=movement_data.product_id,
warehouse_id=movement_data.warehouse_id,
movement_type=movement_data.movement_type,
quantity=movement_data.quantity,
before_quantity=before_qty,
after_quantity=after_qty,
reference_no=generate_order_no("SM"),
unit_price=movement_data.unit_price,
total_amount=movement_data.unit_price * movement_data.quantity if movement_data.unit_price else None,
remark=movement_data.remark,
operator_id=current_user.id
)
db_session.add(movement)
await db_session.commit()
product = await db_session.execute(select(Product).where(Product.id == movement_data.product_id))
product = product.scalar_one()
return StockMovementResponse(
id=movement.id,
product_name=product.name,
movement_type=movement.movement_type,
quantity=movement.quantity,
before_quantity=movement.before_quantity,
after_quantity=movement.after_quantity,
reference_no=movement.reference_no,
remark=movement.remark,
created_at=movement.created_at
)
@router.get("/stock-movements", response_model=List[StockMovementResponse])
async def list_stock_movements(
product_id: Optional[int] = None,
movement_type: Optional[str] = None,
skip: int = Query(0, ge=0),
limit: int = Query(20, ge=1, le=100),
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
query = (
select(StockMovement, Product)
.join(Product, StockMovement.product_id == Product.id)
.order_by(StockMovement.created_at.desc())
)
if product_id:
query = query.where(StockMovement.product_id == product_id)
if movement_type:
query = query.where(StockMovement.movement_type == movement_type)
query = query.offset(skip).limit(limit)
result = await db_session.execute(query)
movements = []
for movement, product in result.all():
movements.append(StockMovementResponse(
id=movement.id,
product_name=product.name,
movement_type=movement.movement_type,
quantity=movement.quantity,
before_quantity=movement.before_quantity,
after_quantity=movement.after_quantity,
reference_no=movement.reference_no,
remark=movement.remark,
created_at=movement.created_at
))
return movements
@router.get("/purchase-orders", response_model=List[PurchaseOrderResponse])
async def list_purchase_orders(
status: Optional[str] = None,
skip: int = Query(0, ge=0),
limit: int = Query(20, ge=1, le=100),
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
query = (
select(PurchaseOrder, Supplier)
.join(Supplier, PurchaseOrder.supplier_id == Supplier.id)
.order_by(PurchaseOrder.created_at.desc())
)
if status:
query = query.where(PurchaseOrder.status == status)
query = query.offset(skip).limit(limit)
result = await db_session.execute(query)
orders = []
for order, supplier in result.all():
orders.append(PurchaseOrderResponse(
id=order.id,
order_no=order.order_no,
supplier_name=supplier.name,
order_date=order.order_date,
expected_date=order.expected_date,
status=order.status,
total_amount=order.total_amount,
paid_amount=order.paid_amount,
remark=order.remark,
created_at=order.created_at
))
return orders
@router.post("/purchase-orders", response_model=PurchaseOrderResponse, status_code=201)
async def create_purchase_order(
order_data: PurchaseOrderCreate,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
order = PurchaseOrder(
order_no=generate_order_no("PO"),
supplier_id=order_data.supplier_id,
expected_date=order_data.expected_date,
remark=order_data.remark,
operator_id=current_user.id,
status="draft"
)
db_session.add(order)
await db_session.flush()
total_amount = 0
for item_data in order_data.items:
item = PurchaseOrderItem(
order_id=order.id,
product_id=item_data.product_id,
quantity=item_data.quantity,
unit_price=item_data.unit_price,
amount=item_data.quantity * item_data.unit_price,
remark=item_data.remark
)
db_session.add(item)
total_amount += item.amount
order.total_amount = total_amount
await db_session.commit()
await db_session.refresh(order)
supplier = await db_session.execute(select(Supplier).where(Supplier.id == order.supplier_id))
supplier = supplier.scalar_one()
return PurchaseOrderResponse(
id=order.id,
order_no=order.order_no,
supplier_name=supplier.name,
order_date=order.order_date,
expected_date=order.expected_date,
status=order.status,
total_amount=order.total_amount,
paid_amount=order.paid_amount,
remark=order.remark,
created_at=order.created_at
)
@router.get("/sales-orders", response_model=List[SalesOrderResponse])
async def list_sales_orders(
status: Optional[str] = None,
skip: int = Query(0, ge=0),
limit: int = Query(20, ge=1, le=100),
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
query = (
select(SalesOrder, Customer)
.join(Customer, SalesOrder.customer_id == Customer.id)
.order_by(SalesOrder.created_at.desc())
)
if status:
query = query.where(SalesOrder.status == status)
query = query.offset(skip).limit(limit)
result = await db_session.execute(query)
orders = []
for order, customer in result.all():
orders.append(SalesOrderResponse(
id=order.id,
order_no=order.order_no,
customer_name=customer.name,
order_date=order.order_date,
delivery_date=order.delivery_date,
status=order.status,
total_amount=order.total_amount,
received_amount=order.received_amount,
remark=order.remark,
created_at=order.created_at
))
return orders
@router.post("/sales-orders", response_model=SalesOrderResponse, status_code=201)
async def create_sales_order(
order_data: SalesOrderCreate,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
order = SalesOrder(
order_no=generate_order_no("SO"),
customer_id=order_data.customer_id,
delivery_date=order_data.delivery_date,
remark=order_data.remark,
operator_id=current_user.id,
status="draft"
)
db_session.add(order)
await db_session.flush()
total_amount = 0
for item_data in order_data.items:
item = SalesOrderItem(
order_id=order.id,
product_id=item_data.product_id,
quantity=item_data.quantity,
unit_price=item_data.unit_price,
amount=item_data.quantity * item_data.unit_price,
remark=item_data.remark
)
db_session.add(item)
total_amount += item.amount
order.total_amount = total_amount
await db_session.commit()
await db_session.refresh(order)
customer = await db_session.execute(select(Customer).where(Customer.id == order.customer_id))
customer = customer.scalar_one()
return SalesOrderResponse(
id=order.id,
order_no=order.order_no,
customer_name=customer.name,
order_date=order.order_date,
delivery_date=order.delivery_date,
status=order.status,
total_amount=order.total_amount,
received_amount=order.received_amount,
remark=order.remark,
created_at=order.created_at
)
+1 -20
View File
@@ -21,35 +21,16 @@ logger = get_logger(__name__)
router = APIRouter()
# 服务实例
stp_parser = STPParser()
geometry_analyzer = GeometryAnalyzer()
file_handler = FileHandler()
html_generator = HTMLGenerator()
# 初始化模具生成器(可配置不同材料的收缩率)
mold_generator = MoldCavityGenerator(shrinkage_rate=0.005) # ABS材料
mold_generator = MoldCavityGenerator(shrinkage_rate=0.005)
mesh_generator = MeshGenerator(quality="medium")
# 内存中的任务存储
tasks = {}
@router.get("/")
@router.post("/")
async def read_root(request: Request):
"""主页面"""
from fastapi.templating import Jinja2Templates
import os
# 简化路径配置,直接使用当前工作目录下的templates文件夹
templates_dir = os.path.join(os.getcwd(), "templates")
templates = Jinja2Templates(directory=templates_dir)
return templates.TemplateResponse("index.html", {
"request": request,
"pythonocc_available": True,
"version": "3.0.0"
})
@router.get("/health")
@router.post("/health")
async def health():