jiachenlong/backend/app/routers/collections.py

716 lines
29 KiB
Python
Raw Normal View History

# 藏品路由 - 使用字段编码
import os
import uuid
import re
from typing import Optional, List
from fastapi import APIRouter, Depends, HTTPException, status, Query, UploadFile, File
from sqlalchemy import or_, func, text
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.core.auth import get_current_user
from app.core.logging_config import logger
from app.models.models import User, Collection, CollectionImage, Operation
from app.schemas.schemas import (
CollectionCreate, CollectionUpdate, CollectionResponse,
CollectionListResponse, CollectionImageResponse
)
from app.services.oss import upload_to_oss, get_oss_path, delete_from_oss, get_public_url
router = APIRouter(prefix="/api/collections", tags=["藏品"])
def to_camel_case(data: dict) -> dict:
"""将字段编码转换为 camelCase 格式"""
if not data:
return data
mapping = {
'f99_90_id': 'id',
'f99_91_user_id': 'userId',
'f99_92_created_at': 'createdAt',
'f99_93_updated_at': 'updatedAt',
'f01_01_name': 'name',
'f01_02_code': 'code',
'f01_03_category': 'category',
'f01_04_status': 'status',
'f01_05_remark': 'remark',
'f02_10_prefix_serial': 'prefixSerial',
'f02_11_version': 'version',
'f02_12_packaging': 'packaging',
'f02_13_rarity': 'rarity',
'f03_20_is_graded': 'isGraded',
'f03_21_grading_company': 'gradingCompany',
'f03_22_grading_score': 'gradingScore',
'f03_23_three_star': 'threeStar',
'f04_30_special_mark': 'specialMark',
'f04_31_serial_feature': 'serialFeature',
'f04_32_issuer': 'issuer',
'f04_33_issue_year': 'issueYear',
'f04_34_material': 'material',
'f04_35_denomination': 'denomination',
'f04_36_issue_quantity': 'issueQuantity',
'f05_40_cost_price': 'costPrice',
'f05_41_target_price': 'targetPrice',
'f05_42_goal_price': 'goalPrice',
'f05_43_repair_fee': 'repairFee',
'f05_44_grading_fee': 'gradingFee',
'f06_50_purpose': 'purpose',
}
return {mapping.get(k, k): v for k, v in data.items()}
# 编码生成函数
def generate_code(version: str, user_id: str, db: Session) -> str:
"""自动生成藏品编号 - 按用户独立编码"""
import re
# 查询当前用户的非空编码(不与其他用户混算)
user_codes = db.query(Collection.f01_02_code).filter(
Collection.f01_02_code.isnot(None),
Collection.f99_91_user_id == user_id
).all()
max_num = 0
for (code,) in user_codes:
# 只处理纯数字或 4 位数字编码(忽略 TEST001 等特殊编码)
if re.match(r'^\d{4}$', code):
try:
num = int(code)
if num > max_num:
max_num = num
except (ValueError, TypeError):
pass
# 当前用户最大号 +1
next_num = max_num + 1
return str(next_num).zfill(4)
@router.get("/next-code")
def get_next_code(
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取下一个藏品编号"""
next_code = generate_code("2024 龙", current_user.f99_90_id, db)
return {"code": 200, "data": {"nextCode": next_code}}
@router.get("")
def get_collections(
category: Optional[str] = None,
status: Optional[str] = None,
user_id: Optional[str] = None,
search: Optional[str] = None,
page: int = Query(1, ge=1),
limit: int = Query(20, ge=1, le=100),
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取藏品列表"""
# admin 用户可以看到所有藏品,普通用户只能看到自己的
from sqlalchemy.orm import joinedload
if current_user.role == "admin":
# 联表查询获取用户名
query = db.query(Collection, User.f01_01_name.label('owner_name')).join(
User, Collection.f99_91_user_id == User.f99_90_id, isouter=True
)
else:
query = db.query(Collection).filter(or_(Collection.f99_91_user_id == current_user.f99_90_id, current_user.role == "admin"))
if category:
query = query.filter(Collection.f01_03_category == category)
if user_id:
query = query.filter(Collection.f99_91_user_id == user_id)
if status:
query = query.filter(Collection.f01_04_status == status)
if search:
query = query.filter(
(Collection.f01_01_name.contains(search)) |
(Collection.f01_05_remark.contains(search))
)
# 总数分开查询避免join影响count
if current_user.role == "admin":
total = db.query(Collection).count()
else:
total = query.count()
# 分页
data = query.order_by(Collection.f99_92_created_at.desc()) \
.offset((page - 1) * limit) \
.limit(limit) \
.all()
# 转换为字典列表并转为 camelCase
data_list = []
for item in data:
# 处理联表查询结果
if current_user.role == "admin":
collection_item, owner_name = item
else:
collection_item = item
owner_name = None
item_dict = {
'f99_90_id': collection_item.f99_90_id,
'f99_91_user_id': collection_item.f99_91_user_id,
'owner_name': owner_name, # 所属用户名(仅管理员可见)
'f01_01_name': collection_item.f01_01_name,
'f01_02_code': collection_item.f01_02_code,
'f01_03_category': collection_item.f01_03_category,
'f01_04_status': collection_item.f01_04_status,
'f01_05_remark': collection_item.f01_05_remark,
'f02_10_prefix_serial': collection_item.f02_10_prefix_serial,
'f02_11_version': collection_item.f02_11_version,
'f02_12_packaging': collection_item.f02_12_packaging,
'f02_13_rarity': collection_item.f02_13_rarity,
'f03_20_is_graded': collection_item.f03_20_is_graded,
'f03_21_grading_company': collection_item.f03_21_grading_company,
'f03_22_grading_score': collection_item.f03_22_grading_score,
'f03_23_three_star': collection_item.f03_23_three_star,
'f04_30_special_mark': collection_item.f04_30_special_mark,
'f04_31_serial_feature': collection_item.f04_31_serial_feature,
'f04_32_issuer': collection_item.f04_32_issuer,
'f04_33_issue_year': collection_item.f04_33_issue_year,
'f04_34_material': collection_item.f04_34_material,
'f04_35_denomination': collection_item.f04_35_denomination,
'f04_36_issue_quantity': collection_item.f04_36_issue_quantity,
'f05_40_cost_price': float(collection_item.f05_40_cost_price) if collection_item.f05_40_cost_price else None,
'f05_41_target_price': float(collection_item.f05_41_target_price) if collection_item.f05_41_target_price else None,
'f05_42_goal_price': float(collection_item.f05_42_goal_price) if collection_item.f05_42_goal_price else None,
'f05_43_repair_fee': float(collection_item.f05_43_repair_fee) if collection_item.f05_43_repair_fee else None,
'f05_44_grading_fee': float(collection_item.f05_44_grading_fee) if collection_item.f05_44_grading_fee else None,
'f06_50_purpose': collection_item.f06_50_purpose,
'f99_92_created_at': collection_item.f99_92_created_at.isoformat() if collection_item.f99_92_created_at else None,
'images': []
}
# 加载图片数据
from app.models.models import CollectionImage
images = db.query(CollectionImage).filter(
CollectionImage.collection_id == collection_item.f99_90_id
).all()
for img in images:
item_dict['images'].append({
'id': img.id,
'filename': img.filename,
'original_name': img.original_name,
'path': img.path,
'created_at': img.created_at.isoformat() if img.created_at else None
})
data_list.append(to_camel_case(item_dict))
return {
"data": data_list,
"pagination": {
"page": page,
"limit": limit,
"total": total,
"pages": (total + limit - 1) // limit
}
}
@router.get("/stats")
def get_stats(
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取藏品统计"""
# 获取所有藏品
if current_user.role == "admin":
all_collections = db.query(Collection).all()
else:
all_collections = db.query(Collection).filter(
or_(Collection.f99_91_user_id == current_user.f99_90_id, current_user.role == "admin")
).all()
# 总数
total_count = len(all_collections)
# 按分类统计
from collections import Counter
by_category = Counter(c.f01_03_category for c in all_collections).items()
# 按状态统计
by_status = Counter(c.f01_04_status for c in all_collections).items()
# 按是否评级统计
by_graded = Counter(c.f03_20_is_graded for c in all_collections).items()
# 新增8 个分布统计
by_packaging = Counter(c.f02_12_packaging for c in all_collections if c.f02_12_packaging).items()
by_rarity = Counter(c.f02_13_rarity for c in all_collections if c.f02_13_rarity).items()
by_version = Counter(c.f02_11_version for c in all_collections if c.f02_11_version).items()
by_grading_company = Counter(c.f03_21_grading_company for c in all_collections if c.f03_20_is_graded and c.f03_21_grading_company).items()
by_grading_score = Counter(c.f03_22_grading_score for c in all_collections if c.f03_20_is_graded and c.f03_22_grading_score).items()
by_special_mark = Counter(c.f04_30_special_mark for c in all_collections if c.f04_30_special_mark).items()
# 总成本: SUM(cost_price + repair_fee + grading_fee) for ALL collections
total_cost = sum(
(c.f05_40_cost_price or 0) + (c.f05_43_repair_fee or 0) + (c.f05_44_grading_fee or 0)
for c in all_collections
)
# 预期利润: SUM(target_price - cost_price) for collections with target_price > 0
expected_profit = sum(
(c.f05_41_target_price or 0) - (c.f05_40_cost_price or 0)
for c in all_collections
if c.f05_41_target_price and c.f05_41_target_price > 0
)
# 已售商品:状态为 sold 且出售价 > 0
sold_collections = [
c for c in all_collections
if c.f01_04_status == 'sold' and c.f05_42_goal_price and c.f05_42_goal_price > 0
]
# 总收入SUM(出售价) for 已售商品(售价>0
total_revenue = sum(
c.f05_42_goal_price or 0
for c in sold_collections
)
# 总利润已实现利润SUM(出售价 - 成本价 - 修复费 - 评级费) for 已售商品
# 单藏品总成本 = 成本价 + 修复费 + 评级费
total_profit = sum(
(c.f05_42_goal_price or 0) - (c.f05_40_cost_price or 0) - (c.f05_43_repair_fee or 0) - (c.f05_44_grading_fee or 0)
for c in sold_collections
)
return {
"totalCount": total_count,
"byCategory": [{"category": c, "count": n} for c, n in by_category],
"byStatus": [{"status": s, "count": n} for s, n in by_status],
"byGrading": [{"isGraded": g, "count": n} for g, n in by_graded],
"byPackaging": [{"packaging": p, "count": n} for p, n in by_packaging],
"byRarity": [{"rarity": r, "count": n} for r, n in by_rarity],
"byVersion": [{"version": v, "count": n} for v, n in by_version],
"byGradingCompany": [{"company": c, "count": n} for c, n in by_grading_company],
"byGradingScore": [{"score": s, "count": n} for s, n in by_grading_score],
"bySpecialMark": [{"mark": m, "count": n} for m, n in by_special_mark],
# 盈亏统计(只统计已售且有价格的藏品)
"byProfitLoss": [
{"type": "profit", "label": "盈利", "count": sum(1 for c in sold_collections if c.f05_42_goal_price and c.f05_40_cost_price and c.f05_42_goal_price > c.f05_40_cost_price)},
{"type": "loss", "label": "亏损", "count": sum(1 for c in sold_collections if c.f05_42_goal_price and c.f05_40_cost_price and c.f05_42_goal_price <= c.f05_40_cost_price)}
],
"totalCost": total_cost,
"totalTarget": sum(c.f05_41_target_price or 0 for c in all_collections),
"expectedProfit": expected_profit,
"totalRevenue": total_revenue,
"totalProfit": total_profit
}
@router.get("/by-user/{user_id}")
def get_collections_by_user(
user_id: str,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取指定用户的所有藏品(管理员专用)"""
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="E00050: 仅管理员可访问")
collections = db.query(Collection).filter(
Collection.f99_91_user_id == user_id
).all()
result = []
for c in collections:
result.append({
"f99_90_id": c.f99_90_id,
"name": c.f01_01_name,
"code": c.f01_02_code,
"prefixSerial": c.f02_10_prefix_serial,
"version": c.f02_11_version,
"category": c.f01_03_category,
"status": c.f01_04_status,
"packaging": c.f02_12_packaging,
"isGraded": c.f03_20_is_graded,
"costPrice": c.f05_40_cost_price,
"f01_01_name": c.f01_01_name,
"f02_10_prefix_serial": c.f02_10_prefix_serial,
"f02_11_version": c.f02_11_version,
"f01_04_status": c.f01_04_status,
"userId": c.f99_91_user_id,
"f05_40_cost_price": c.f05_40_cost_price,
"f99_92_created_at": c.f99_92_created_at.isoformat() if c.f99_92_created_at else None
})
return result
@router.get("/{collection_id}")
def get_collection(
collection_id: str,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取单个藏品详情"""
result = db.execute(
text("SELECT * FROM collections WHERE f99_90_id = :id"),
{"id": collection_id}
).fetchone()
if not result:
raise HTTPException(status_code=404, detail="E00033: 藏品不存在")
collection = dict(result._mapping)
# 非管理员只能查看自己的藏品
if current_user.role != "admin" and collection.get('f99_91_user_id') != current_user.f99_90_id:
raise HTTPException(status_code=403, detail="无权访问")
result_dict = {
'f99_90_id': collection.get('f99_90_id'),
'f99_91_user_id': collection.get('f99_91_user_id'),
'f01_01_name': collection.get('f01_01_name'),
'f01_02_code': collection.get('f01_02_code'),
'f01_03_category': collection.get('f01_03_category'),
'f01_04_status': collection.get('f01_04_status'),
'f01_05_remark': collection.get('f01_05_remark'),
'f02_10_prefix_serial': collection.get('f02_10_prefix_serial'),
'f02_11_version': collection.get('f02_11_version'),
'f02_12_packaging': collection.get('f02_12_packaging'),
'f02_13_rarity': collection.get('f02_13_rarity'),
'f03_20_is_graded': collection.get('f03_20_is_graded'),
'f03_21_grading_company': collection.get('f03_21_grading_company'),
'f03_22_grading_score': collection.get('f03_22_grading_score'),
'f03_23_three_star': collection.get('f03_23_three_star'),
'f04_30_special_mark': collection.get('f04_30_special_mark'),
'f04_31_serial_feature': collection.get('f04_31_serial_feature'),
'f04_32_issuer': collection.get('f04_32_issuer'),
'f04_33_issue_year': collection.get('f04_33_issue_year'),
'f04_34_material': collection.get('f04_34_material'),
'f04_35_denomination': collection.get('f04_35_denomination'),
'f04_36_issue_quantity': collection.get('f04_36_issue_quantity'),
'f05_40_cost_price': float(collection.get('f05_40_cost_price')) if collection.get('f05_40_cost_price') else None,
'f05_41_target_price': float(collection.get('f05_41_target_price')) if collection.get('f05_41_target_price') else None,
'f05_42_goal_price': float(collection.get('f05_42_goal_price')) if collection.get('f05_42_goal_price') else None,
'f05_43_repair_fee': float(collection.get('f05_43_repair_fee')) if collection.get('f05_43_repair_fee') else None,
'f05_44_grading_fee': float(collection.get('f05_44_grading_fee')) if collection.get('f05_44_grading_fee') else None,
'f06_50_purpose': collection.get('f06_50_purpose'),
'f99_92_created_at': collection.get('f99_92_created_at').isoformat() if collection.get('f99_92_created_at') else None,
'images': []
}
# 加载图片数据
images = db.query(CollectionImage).filter(
CollectionImage.collection_id == collection_id
).all()
for img in images:
result_dict['images'].append({
'id': img.id,
'filename': img.filename,
'original_name': img.original_name,
'path': img.path,
'created_at': img.created_at.isoformat() if img.created_at else None
})
return to_camel_case(result_dict)
@router.post("")
def create_collection(
collection_data: CollectionCreate,
force: bool = False, # 是否强制保存(忽略重复警告)
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""创建藏品 - 支持冠字号查重"""
from app.core.logging_config import logger
# 自动生成编码
final_code = collection_data.f01_02_code or generate_code(
collection_data.f02_11_version or '2024 龙',
current_user.f99_90_id,
db
)
2026-03-18 14:30:54 +08:00
# 编号查重(如果提供了编号且不是强制保存)
if not force and final_code:
existing_code = db.query(Collection).filter(
Collection.f01_02_code == final_code,
or_(Collection.f99_91_user_id == current_user.f99_90_id, current_user.role == "admin")
2026-03-18 14:30:54 +08:00
).first()
if existing_code:
logger.warning(f"发现重复编号:{final_code}, 已存在藏品 ID: {existing_code.f99_90_id}")
return {
"error": {
"code": "DUPLICATE_CODE",
"message": f"藏品编号 {final_code} 已存在,请使用其他编号"
}
}
# 冠字号查重(如果提供了冠字号且不是强制保存)
if not force and collection_data.f02_10_prefix_serial:
# 查询当前用户是否有相同冠字号的藏品
existing = db.query(Collection).filter(
Collection.f02_10_prefix_serial == collection_data.f02_10_prefix_serial,
or_(Collection.f99_91_user_id == current_user.f99_90_id, current_user.role == "admin")
).first()
if existing:
logger.warning(f"发现重复冠字号:{collection_data.f02_10_prefix_serial}, 已存在藏品 ID: {existing.f99_90_id}")
# 返回警告信息,让前端询问用户是否继续
return {
"warning": {
"code": "DUPLICATE_SERIAL",
"message": f"发现重复冠字号:{collection_data.f02_10_prefix_serial}",
"existing_collection": {
"id": existing.f99_90_id,
"name": existing.f01_01_name,
"code": existing.f01_02_code,
"prefix_serial": existing.f02_10_prefix_serial
}
},
"data": {
"ask_continue": True
}
}
collection = Collection(
f99_91_user_id=current_user.f99_90_id,
f01_01_name=collection_data.f01_01_name,
f01_02_code=final_code,
f01_03_category=collection_data.f01_03_category,
f01_04_status=collection_data.f01_04_status or "in_collection",
f01_05_remark=collection_data.f01_05_remark,
f02_10_prefix_serial=collection_data.f02_10_prefix_serial,
f02_11_version=collection_data.f02_11_version,
f02_12_packaging=collection_data.f02_12_packaging,
f02_13_rarity=collection_data.f02_13_rarity,
f03_20_is_graded=collection_data.f03_20_is_graded or False,
f03_21_grading_company=collection_data.f03_21_grading_company,
f03_22_grading_score=collection_data.f03_22_grading_score,
f03_23_three_star=collection_data.f03_23_three_star or False,
f04_30_special_mark=collection_data.f04_30_special_mark,
f04_31_serial_feature=collection_data.f04_31_serial_feature,
f04_32_issuer=collection_data.f04_32_issuer,
f04_33_issue_year=collection_data.f04_33_issue_year,
f04_34_material=collection_data.f04_34_material,
f04_35_denomination=collection_data.f04_35_denomination,
f04_36_issue_quantity=collection_data.f04_36_issue_quantity,
f05_40_cost_price=collection_data.f05_40_cost_price,
f05_41_target_price=collection_data.f05_41_target_price,
f05_42_goal_price=collection_data.f05_42_goal_price,
f05_43_repair_fee=collection_data.f05_43_repair_fee,
f05_44_grading_fee=collection_data.f05_44_grading_fee,
f06_50_purpose=collection_data.f06_50_purpose
)
db.add(collection)
db.commit()
db.refresh(collection)
return {
'f99_90_id': collection.f99_90_id,
'f99_91_user_id': collection.f99_91_user_id,
'f01_01_name': collection.f01_01_name,
'f01_02_code': collection.f01_02_code,
'f01_03_category': collection.f01_03_category,
'f01_04_status': collection.f01_04_status,
'f01_05_remark': collection.f01_05_remark,
'f99_92_created_at': collection.f99_92_created_at.isoformat() if collection.f99_92_created_at else None,
'message': '创建成功'
}
@router.put("/{collection_id}")
def update_collection(
collection_id: str,
collection_data: CollectionUpdate,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""更新藏品"""
collection = db.query(Collection).filter(
Collection.f99_90_id == collection_id,
or_(Collection.f99_91_user_id == current_user.f99_90_id, current_user.role == "admin")
).first()
if not collection:
raise HTTPException(status_code=404, detail="E00033: 藏品不存在")
# 更新字段 - 使用 model_fields_set 检查哪些字段被设置
for field_name in collection_data.model_fields_set:
value = getattr(collection_data, field_name)
if value is not None:
setattr(collection, field_name, value)
db.commit()
db.refresh(collection)
return {
"f99_90_id": collection.f99_90_id,
"message": "更新成功"
}
@router.delete("/{collection_id}")
def delete_collection(
collection_id: str,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""删除藏品"""
# 验证权限并检查是否存在
collection = db.query(Collection).filter(
Collection.f99_90_id == collection_id,
or_(Collection.f99_91_user_id == current_user.f99_90_id, current_user.role == "admin")
).first()
if not collection:
raise HTTPException(status_code=404, detail="E00033: 藏品不存在")
# 使用原生 SQL 删除(避免 ORM 级联查询字段不匹配问题)
from sqlalchemy import text
# 1. 删除关联的 operationsf99_91_user_id 关联到 collections.f99_90_id
db.execute(text("DELETE FROM operations WHERE f99_91_user_id = :id"), {"id": collection_id})
# 2. 删除关联的图片
db.execute(text("DELETE FROM collection_images WHERE collection_id = :id"), {"id": collection_id})
# 3. 删除藏品本身
db.execute(text("DELETE FROM collections WHERE f99_90_id = :id"), {"id": collection_id})
db.commit()
return {"message": "删除成功"}
@router.post("/upload-image")
async def upload_image(
collection_id: str = None,
file: UploadFile = File(...),
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""上传藏品图片 - 文件名格式:用户名 - 藏品编号 - 冠字号"""
try:
# 验证藏品是否存在
collection = db.query(Collection).filter(
Collection.f99_90_id == collection_id
).first()
if not collection:
raise HTTPException(status_code=404, detail="E00033: 藏品不存在")
# 获取用户信息(用于文件名)
owner = db.query(User).filter(User.f99_90_id == collection.f99_91_user_id).first()
username = owner.f01_01_name if owner else "unknown"
# 获取藏品信息(用于文件名)
code = collection.f01_02_code or "0000"
prefix_serial = collection.f02_10_prefix_serial or ""
# 检查文件类型
if not file.content_type.startswith('image/'):
raise HTTPException(status_code=400, detail="E00038: 只能上传图片文件")
# 检查文件大小(限制 10MB
file_size = 0
content = await file.read()
file_size = len(content)
if file_size > 10 * 1024 * 1024: # 10MB
raise HTTPException(status_code=400, detail=f"E00039: 图片大小不能超过 10MB当前{file_size // 1024 // 1024}MB")
# 生成OSS存储路径
user_id = collection.f99_91_user_id
file_extension = file.filename.split('.')[-1] if '.' in file.filename else 'jpg'
# 清理特殊字符
clean_username = re.sub(r'[^\w\u4e00-\u9fff\-]', '', username)
clean_serial = re.sub(r'[^\w\u4e00-\u9fff\-]', '', prefix_serial)
# 文件名格式:用户名-藏品编号-冠字号.jpg
if clean_serial:
filename = f"{clean_username}-{code}-{clean_serial}.{file_extension}"
else:
filename = f"{clean_username}-{code}.{file_extension}"
# 生成OSS key
oss_key, unique_name = get_oss_path("collections", user_id=user_id, filename=filename)
# 上传到OSS
image_url = upload_to_oss(content, oss_key)
# 创建图片记录保存OSS URL
image = CollectionImage(
id=str(uuid.uuid4()),
collection_id=collection_id,
filename=unique_name,
original_name=file.filename,
path=image_url # 保存OSS URL
)
db.add(image)
db.commit()
db.refresh(image)
logger.info(f"图片上传成功:{image_url}, collection_id={collection_id}")
return {
"message": "上传成功",
"image_id": image.id,
"filename": unique_name,
"url": image_url
}
except HTTPException:
raise
except Exception as e:
logger.error(f"图片上传失败:{str(e)}")
raise HTTPException(status_code=500, detail="上传失败")
@router.delete("/images/{image_id}")
async def delete_image(
image_id: str,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""删除藏品图片"""
try:
# 查找图片记录
image = db.query(CollectionImage).filter(
CollectionImage.id == image_id
).first()
if not image:
raise HTTPException(status_code=404, detail="E00033: 图片不存在")
# 检查权限
collection = db.query(Collection).filter(
Collection.f99_90_id == image.collection_id
).first()
if collection and current_user.role != "admin" and collection.f99_91_user_id != current_user.f99_90_id:
raise HTTPException(status_code=403, detail="E00014: 无权删除此图片")
# 删除OSS文件如果path是OSS URL
if image.path and image.path.startswith("https://"):
# 从OSS URL提取key
try:
oss_key = image.path.replace("https://jiachenlong-oss.oss-cn-hangzhou.aliyuncs.com/", "")
delete_from_oss(oss_key)
except Exception as e:
logger.warning(f"OSS文件删除失败: {e}")
elif image.path and os.path.exists(image.path):
# 兼容旧的本地上传
os.remove(image.path)
# 删除数据库记录
db.delete(image)
db.commit()
return {"message": "删除成功"}
except HTTPException:
raise
except Exception as e:
logger.error(f"图片删除失败:{str(e)}")
raise HTTPException(status_code=500, detail="删除失败")