fix: 会员/图标换图生成新路径并删除旧OSS,避免CDN缓存导致不更新
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -276,9 +276,13 @@ MEMBER_CARD_FOLDER = 'beijing/huiyuan'
|
|||||||
|
|
||||||
|
|
||||||
def upload_member_card_image(request, huiyuan_id, field, file_obj):
|
def upload_member_card_image(request, huiyuan_id, field, file_obj):
|
||||||
"""上传会员充值页卡片图,写入 club_huiyuan_price.card_image / card_bg。"""
|
"""上传会员充值页卡片图,写入 club_huiyuan_price.card_image / card_bg。
|
||||||
|
|
||||||
|
每次上传生成新相对路径并删除旧图,避免「同名覆盖但 CDN/浏览器仍显示旧图」。
|
||||||
|
"""
|
||||||
import os
|
import os
|
||||||
from utils.oss_utils import validate_image, upload_to_oss
|
import uuid
|
||||||
|
from utils.oss_utils import validate_image, upload_to_oss, delete_from_oss
|
||||||
|
|
||||||
if club_write_blocked(request):
|
if club_write_blocked(request):
|
||||||
return None, CLUB_WRITE_SCOPE_MSG
|
return None, CLUB_WRITE_SCOPE_MSG
|
||||||
@@ -289,18 +293,36 @@ def upload_member_card_image(request, huiyuan_id, field, file_obj):
|
|||||||
return None, msg
|
return None, msg
|
||||||
|
|
||||||
club_id = club_id_for_write(request)
|
club_id = club_id_for_write(request)
|
||||||
ext = os.path.splitext(getattr(file_obj, 'name', '') or '')[1].lower()
|
|
||||||
if ext not in ('.png', '.jpg', '.jpeg', '.webp', '.gif'):
|
|
||||||
ext = '.png'
|
|
||||||
rel_path = f'{MEMBER_CARD_FOLDER}/{club_id}/{huiyuan_id}_{field}{ext}'
|
|
||||||
if not upload_to_oss(file_obj, rel_path):
|
|
||||||
return None, '上传 OSS 失败'
|
|
||||||
|
|
||||||
row = ClubHuiyuanPrice.query.filter(club_id=club_id, huiyuan_id=huiyuan_id).first()
|
row = ClubHuiyuanPrice.query.filter(club_id=club_id, huiyuan_id=huiyuan_id).first()
|
||||||
if not row:
|
if not row:
|
||||||
return None, '请先在当前俱乐部上架该会员'
|
return None, '请先在当前俱乐部上架该会员'
|
||||||
|
|
||||||
|
ext = os.path.splitext(getattr(file_obj, 'name', '') or '')[1].lower()
|
||||||
|
if ext not in ('.png', '.jpg', '.jpeg', '.webp', '.gif'):
|
||||||
|
ext = '.png'
|
||||||
|
# 带时间戳+uuid,保证 URL 变化,前端/CDN 不会命中旧缓存
|
||||||
|
stamp = uuid.uuid4().hex[:12]
|
||||||
|
rel_path = f'{MEMBER_CARD_FOLDER}/{club_id}/{huiyuan_id}_{field}_{stamp}{ext}'
|
||||||
|
|
||||||
|
if hasattr(file_obj, 'seek'):
|
||||||
|
try:
|
||||||
|
file_obj.seek(0)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if not upload_to_oss(file_obj, rel_path):
|
||||||
|
return None, '上传 OSS 失败'
|
||||||
|
|
||||||
|
old_path = (getattr(row, field, None) or '').strip()
|
||||||
setattr(row, field, rel_path)
|
setattr(row, field, rel_path)
|
||||||
row.save(update_fields=[field, 'UpdateTime'])
|
row.save(update_fields=[field, 'UpdateTime'])
|
||||||
|
|
||||||
|
if old_path and old_path != rel_path:
|
||||||
|
try:
|
||||||
|
delete_from_oss(old_path)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning('删除旧会员卡片图失败 club=%s hy=%s field=%s path=%s err=%s',
|
||||||
|
club_id, huiyuan_id, field, old_path, e)
|
||||||
|
|
||||||
return {field: rel_path, 'huiyuan_id': huiyuan_id}, None
|
return {field: rel_path, 'huiyuan_id': huiyuan_id}, None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -129,12 +129,20 @@ def list_tupianpeizhi_by_type(club_id, image_type):
|
|||||||
|
|
||||||
|
|
||||||
def upsert_tupianpeizhi_single(club_id, image_type, image_url):
|
def upsert_tupianpeizhi_single(club_id, image_type, image_url):
|
||||||
"""ImageType 1/2 每俱乐部仅一条。"""
|
"""ImageType 1/2 每俱乐部仅一条;换图时删除旧 OSS 对象。"""
|
||||||
from config.models import Tupianpeizhi
|
from config.models import Tupianpeizhi
|
||||||
|
from utils.oss_utils import delete_from_oss
|
||||||
|
|
||||||
row = Tupianpeizhi.query.filter(club_id=club_id, ImageType=image_type).first()
|
row = Tupianpeizhi.query.filter(club_id=club_id, ImageType=image_type).first()
|
||||||
if row:
|
if row:
|
||||||
|
old = (row.ImageURL or '').strip()
|
||||||
row.ImageURL = image_url
|
row.ImageURL = image_url
|
||||||
row.save(update_fields=['ImageURL', 'UpdateTime'])
|
row.save(update_fields=['ImageURL', 'UpdateTime'])
|
||||||
|
if old and old != image_url:
|
||||||
|
try:
|
||||||
|
delete_from_oss(old)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
Tupianpeizhi.query.create(
|
Tupianpeizhi.query.create(
|
||||||
club_id=club_id, ImageType=image_type, ImageURL=image_url, AccessCount=0,
|
club_id=club_id, ImageType=image_type, ImageURL=image_url, AccessCount=0,
|
||||||
|
|||||||
@@ -138,15 +138,22 @@ class ClubMiniappIconManageView(APIView):
|
|||||||
ext = os.path.splitext(file_obj.name)[1].lower() or '.png'
|
ext = os.path.splitext(file_obj.name)[1].lower() or '.png'
|
||||||
if ext not in ('.png', '.jpg', '.jpeg', '.webp'):
|
if ext not in ('.png', '.jpg', '.jpeg', '.webp'):
|
||||||
ext = '.png'
|
ext = '.png'
|
||||||
|
import uuid
|
||||||
|
stamp = uuid.uuid4().hex[:12]
|
||||||
|
# 路径必须含 club_id + 唯一后缀:否则同名覆盖后 CDN 仍显示旧图,且会串俱乐部
|
||||||
if icon_key in POSTER_ICON_KEYS:
|
if icon_key in POSTER_ICON_KEYS:
|
||||||
oss_path = f'{POSTER_FOLDER}/{club_id}_{icon_key}{ext}'
|
oss_path = f'{POSTER_FOLDER}/{club_id}_{icon_key}_{stamp}{ext}'
|
||||||
else:
|
else:
|
||||||
oss_path = f'{ICON_FOLDER}/{icon_key}{ext}'
|
oss_path = f'{ICON_FOLDER}/{club_id}/{icon_key}_{stamp}{ext}'
|
||||||
|
|
||||||
row = ClubMiniappIcon.query.filter(club_id=club_id, icon_key=icon_key).first()
|
row = ClubMiniappIcon.query.filter(club_id=club_id, icon_key=icon_key).first()
|
||||||
if row and row.image_path and row.image_path != oss_path:
|
old_path = (row.image_path or '').strip() if row else ''
|
||||||
delete_from_oss(row.image_path)
|
|
||||||
|
|
||||||
|
if hasattr(file_obj, 'seek'):
|
||||||
|
try:
|
||||||
|
file_obj.seek(0)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
if not upload_to_oss(file_obj, oss_path):
|
if not upload_to_oss(file_obj, oss_path):
|
||||||
return Response({'code': 500, 'msg': '图片上传失败'})
|
return Response({'code': 500, 'msg': '图片上传失败'})
|
||||||
|
|
||||||
@@ -165,6 +172,11 @@ class ClubMiniappIconManageView(APIView):
|
|||||||
is_active=True,
|
is_active=True,
|
||||||
is_uploaded=True,
|
is_uploaded=True,
|
||||||
)
|
)
|
||||||
|
if old_path and old_path != oss_path:
|
||||||
|
try:
|
||||||
|
delete_from_oss(old_path)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
return Response({'code': 0, 'msg': '上传成功', 'data': {'image_path': oss_path}})
|
return Response({'code': 0, 'msg': '上传成功', 'data': {'image_path': oss_path}})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user