feat: 初始化工程

This commit is contained in:
2026-09-09 18:42:37 +08:00
commit 31af6e07e2
29 changed files with 1208 additions and 0 deletions

42
service/crop_service.py Normal file
View File

@@ -0,0 +1,42 @@
from sqlalchemy import select, delete
from sqlalchemy.orm import Session
from models.patch import Crops, Records
from schemas.patch import CropCreate, CropUpdate
def create_crop(db: Session, data: CropCreate) -> bool:
crop = Crops(**data.model_dump())
db.add(crop)
db.commit()
db.refresh(crop)
return True
def update_crop(db: Session, crop_id: int, data: CropUpdate) -> bool:
result = db.execute(select(Crops).where(Crops.id == crop_id))
crop = result.scalar_one_or_none()
if not crop:
return False
for k, v in data.model_dump(exclude_unset=True).items():
setattr(crop, k, v)
db.commit()
db.refresh(crop)
return True
def delete_crop(db: Session, crop_id: int) -> bool:
result = db.execute(select(Crops).where(Crops.id == crop_id))
crop = result.scalar_one_or_none()
if not crop:
return False
db.execute(delete(Records).where(Records.crop_id == crop_id))
db.delete(crop)
db.commit()
return True

50
service/file_service.py Normal file
View File

@@ -0,0 +1,50 @@
import uuid
from fastapi import UploadFile, File, HTTPException
from config.rustfs import s3
ALLOWED_IMAGE_TYPES = [
# 图片
"image/jpeg",
"image/png",
"image/gif",
"image/webp",
"image/svg+xml",
# 视频
"video/mp4",
"video/mpeg",
"video/quicktime",
"video/x-msvideo",
"video/webm",
"video/x-matroska",
"video/ogg",
]
BUCKET = 'patch'
NGINX_PROXY = 'rustfs'
async def upload_file(file: UploadFile = File(...)) -> str:
# 校验文件类型
if file.content_type not in ALLOWED_IMAGE_TYPES:
raise HTTPException(
status_code=400,
detail="只允许上传图片或视频文件 (JPEG, PNG, GIF, WEBP, SVG, MP4, MOV, AVI, WEBM, MKV, OGV)"
)
file_ext = file.filename.split('.')[-1]
unique_filename = f"{uuid.uuid4().hex}.{file_ext}"
file_content = await file.read()
# 上传到S3
s3.put_object(
Bucket=BUCKET,
Key=unique_filename,
Body=file_content,
ContentType=file.content_type
)
# 返回文件url
return f"{NGINX_PROXY}/{BUCKET}/{unique_filename}"

79
service/patch_service.py Normal file
View File

@@ -0,0 +1,79 @@
from sqlalchemy import select, delete
from sqlalchemy.orm import Session, selectinload
from models.patch import Patches, Crops, Records
from schemas.patch import PatchCreate, PatchUpdate, PatchResponse, CropResponse
def create_patch(db: Session, data: PatchCreate) -> bool:
patch = Patches(**data.model_dump())
db.add(patch)
db.commit()
db.refresh(patch)
return True
def get_patches(db: Session) -> list[PatchResponse]:
patches = db.execute(select(Patches)).scalars().all()
result = []
for p in patches:
crop = db.execute(
select(Crops).where(Crops.patch_id == p.id)
).scalar_one_or_none()
data = PatchResponse.model_validate(p)
data.crop = CropResponse.from_orm(crop) if crop else None
result.append(data)
return result
def get_patch(db: Session, patch_id: int) -> PatchResponse | None:
patch = db.execute(
select(Patches).where(Patches.id == patch_id)
).scalar_one_or_none()
if not patch:
return None
crop = db.execute(
select(Crops).where(Crops.patch_id == patch_id)
).scalar_one_or_none()
data = PatchResponse.model_validate(patch)
data.crop = CropResponse.from_orm(crop) if crop else None
return data
def update_patch(db: Session, patch_id: int, data: PatchUpdate) -> bool:
result = db.execute(select(Patches).where(Patches.id == patch_id))
patch = result.scalar_one_or_none()
if not patch:
return False
for k, v in data.model_dump(exclude_unset=True).items():
setattr(patch, k, v)
db.commit()
db.refresh(patch)
return True
def delete_patch(db: Session, patch_id: int) -> bool:
result = db.execute(select(Patches).where(Patches.id == patch_id))
patch = result.scalar_one_or_none()
if not patch:
return False
# 先查作物
crop_result = db.execute(select(Crops).where(Crops.patch_id == patch_id))
crop = crop_result.scalar_one_or_none()
# 有作物就先删记录,再删作物
if crop:
db.execute(delete(Records).where(Records.crop_id == crop.id))
db.delete(crop)
# 最后删菜地
db.delete(patch)
db.commit()
return True

55
service/record_service.py Normal file
View File

@@ -0,0 +1,55 @@
from sqlalchemy import select
from sqlalchemy.orm import Session
from models.patch import Records
from schemas.patch import RecordCreate, RecordResponse, RecordUpdate
def create_record(db: Session, data: RecordCreate) -> bool:
record = Records(**data.model_dump())
db.add(record)
db.commit()
db.refresh(record)
return True
def get_record(db: Session, record_id: int) -> RecordResponse | None:
result = db.execute(select(Records).where(Records.id == record_id))
record = result.scalar_one_or_none()
return RecordResponse.from_orm(record) if record else None
def get_records_by_crop(db: Session, crop_id: int) -> list[RecordResponse]:
result = db.execute(
select(Records)
.where(Records.crop_id == crop_id)
.order_by(Records.date.desc())
)
return [RecordResponse.from_orm(r) for r in result.scalars().all()]
def update_record(db: Session, record_id: int, data: RecordUpdate) -> bool:
result = db.execute(select(Records).where(Records.id == record_id))
record = result.scalar_one_or_none()
if not record:
return False
for k, v in data.model_dump(exclude_unset=True).items():
setattr(record, k, v)
db.commit()
db.refresh(record)
return True
def delete_record(db: Session, record_id: int) -> bool:
result = db.execute(select(Records).where(Records.id == record_id))
record = result.scalar_one_or_none()
if record:
db.delete(record)
db.commit()
return True
return False