Commit 47ef36f7 by Ooh-Ao

noti

parent b4e64e7e
# myproject/controllers/notification_controller.py
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update
from fastapi import HTTPException
from uuid import UUID
from ..models.notification import Notification
# ดึง Notification ทั้งหมด (หรือกรอง project_id)
async def get_notifications(db: AsyncSession, project_id: UUID = None):
q = select(Notification)
if project_id:
q = q.where(Notification.project_id == project_id)
res = await db.execute(q.order_by(Notification.created_at.desc()))
return res.scalars().all()
# นับจำนวน notification ยังไม่อ่าน (หรือกรอง project_id)
async def count_unread_notifications(db: AsyncSession, project_id: UUID = None):
q = select(func.count()).select_from(Notification).where(Notification.is_read == False)
if project_id:
q = q.where(Notification.project_id == project_id)
return await db.scalar(q)
# ทำเครื่องหมายว่าอ่านแล้ว
async def mark_notification_read(db: AsyncSession, notif_id: UUID):
res = await db.execute(select(Notification).where(Notification.notif_id == notif_id))
notif = res.scalar_one_or_none()
if not notif:
raise HTTPException(404, "Notification not found")
notif.is_read = True
await db.commit()
await db.refresh(notif)
return notif
......@@ -13,6 +13,8 @@ from .routes.borrow_routes import router as borrow_router
from .routes.admin_dashboard_routes import router as admin_dashboard_router
from .routes.inventory_lot_router import router as inventory_lot_router
from .routes.equipment_category_router import router as equipment_category_router
from .routes.notification_routes import router as notification_router
from .services.notification_service import start_notification_scheduler
from fastapi.middleware.cors import CORSMiddleware
logging.basicConfig(level=logging.DEBUG)
app = FastAPI()
......@@ -29,6 +31,7 @@ app.add_middleware(
async def startup():
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
start_notification_scheduler()
# โฟลเดอร์สำหรับเก็บไฟล์รูปภาพ
UPLOAD_DIR = "./uploaded_images"
......@@ -49,6 +52,7 @@ app.include_router(pm_router, prefix="/project-members", tags=["ProjectMembers"]
app.include_router(pe_router, prefix="/project-equipments", tags=["ProjectEquipments"])
app.include_router(borrow_router, prefix="/borrow-transactions", tags=["BorrowTransactions"])
app.include_router(admin_dashboard_router, prefix="/admin", tags=["AdminDashboard"])
app.include_router(notification_router, prefix="/notifications", tags=["Notifications"])
app.include_router(file_upload_router.router, prefix="/api", tags=["File Upload"])
@app.get("/")
async def root():
......
# myproject/models/notification.py
import uuid
from datetime import datetime
from sqlalchemy import Column, String, Boolean, DateTime
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy import ForeignKey
from ..config.database import Base
class Notification(Base):
__tablename__ = 'notifications'
notif_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
project_id = Column(UUID(as_uuid=True), ForeignKey('project.projectId'), nullable=False)
equipment_id = Column(UUID(as_uuid=True), ForeignKey('equipment.equipmentId'), nullable=False)
message = Column(String, nullable=False)
is_read = Column(Boolean, default=False, nullable=False)
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
......@@ -26,6 +26,7 @@ class ProjectEquipment(Base):
)
quantity_in_project = Column(Integer, nullable=False, default=0)
low_stock_threshold = Column(Integer, nullable=False, default=5)
# Relationship กลับไปยัง Project และ Equipment
project = relationship("Project", back_populates="project_equipment", lazy="joined")
......
# myproject/routes/notification_routes.py
from fastapi import APIRouter, Depends, Query
from typing import List
from uuid import UUID
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import func
from ..config.database import get_db
from ..controllers.notification_controller import (
get_notifications,
count_unread_notifications,
mark_notification_read
)
from ..schemas.notification_schema import NotificationResponse
router = APIRouter(prefix="/notifications", tags=["Notifications"])
@router.get("/", response_model=List[NotificationResponse])
async def list_notifications(
project_id: UUID = Query(None),
db: AsyncSession = Depends(get_db)
):
return await get_notifications(db, project_id)
@router.get("/unread/count")
async def unread_count(
project_id: UUID = Query(None),
db: AsyncSession = Depends(get_db)
):
count = await count_unread_notifications(db, project_id)
return {"unread_count": count}
@router.put("/{notif_id}/read", response_model=NotificationResponse)
async def read_notification(
notif_id: UUID,
db: AsyncSession = Depends(get_db)
):
return await mark_notification_read(db, notif_id)
# myproject/schemas/notification_schema.py
from pydantic import BaseModel
from uuid import UUID
from datetime import datetime
class NotificationResponse(BaseModel):
notif_id: UUID
project_id: UUID
equipment_id: UUID
message: str
is_read: bool
created_at: datetime
class Config:
orm_mode = True
# myproject/services/notification_service.py
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_
from ..models.project_equipment import ProjectEquipment
from ..models.notification import Notification
from ..config.database import AsyncSession
async def check_low_stock_and_notify():
async with AsyncSession() as db: # หรือ Depends(get_db)
# 1) หาอุปกรณ์ใกล้หมดในทุกโปรเจ็กต์
stmt = select(ProjectEquipment).where(
ProjectEquipment.quantity_in_project <= ProjectEquipment.low_stock_threshold
)
res = await db.execute(stmt)
lows = res.scalars().all()
for pe in lows:
# 2) ตรวจสอบว่าเคยมี Notification ไม่อ่าน (ล่าสุด) อยู่ไหม
dup = await db.execute(
select(Notification).where(
and_(
Notification.project_id == pe.projectId,
Notification.equipment_id == pe.equipmentId,
Notification.is_read == False
)
)
)
if dup.scalar_one_or_none():
continue # ข้ามถ้ามีแล้ว
# 3) สร้าง Notification ใหม่
notif = Notification(
projectId=pe.projectId,
equipmentId=pe.equipmentId,
message=f"อุปกรณ์ {pe.equipmentId} ในโครงการ {pe.projectId} เหลือน้อยกว่า {pe.low_stock_threshold}"
)
db.add(notif)
await db.commit()
def start_notification_scheduler():
scheduler = AsyncIOScheduler()
scheduler.add_job(check_low_stock_and_notify, "interval", hours=24) # ทุก 24 ชม.
scheduler.start()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment