From 6ba0824ea55156aa323b722950739979dae9cc16 Mon Sep 17 00:00:00 2001 From: XingQue Date: Mon, 10 Aug 2026 16:06:40 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BA=A4=E4=BB=98=E5=9B=BE=E4=B8=8E?= =?UTF-8?q?=E9=80=80=E6=AC=BE=E8=AF=81=E6=8D=AE=E5=9B=BE=E5=85=A5=E5=BA=93?= =?UTF-8?q?/=E8=AF=BB=E5=87=BA=E7=BB=9F=E4=B8=80=E4=B8=BA=E5=AE=8C?= =?UTF-8?q?=E6=95=B4=20COS=20URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- orders/views/admin.py | 5 +++-- orders/views/dashou.py | 36 +++++++++++++++++++++++++----------- orders/views/merchant.py | 20 +++++++++++++++----- users/views/kefu_orders.py | 12 ++++++++---- utils/oss_utils.py | 38 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 22 deletions(-) diff --git a/orders/views/admin.py b/orders/views/admin.py index 50c994c..c642263 100644 --- a/orders/views/admin.py +++ b/orders/views/admin.py @@ -227,13 +227,14 @@ class AdGetDingdanXiangQing(APIView): # 9. 查询打手提交的图片 try: + from utils.oss_utils import to_full_cos_url # 查询该订单的所有打手提交图片 dashou_tupian_list = PlayerDeliveryImage.query.filter( OrderID=dingdan_id ).values_list('ImageURL', flat=True) - # 转换为列表,过滤空值 - dashou_images = [img for img in dashou_tupian_list if img] + # 转换为列表,过滤空值;相对路径补全为完整 URL + dashou_images = [to_full_cos_url(img) for img in dashou_tupian_list if img] response_data['dashou_images'] = dashou_images except Exception as e: logger.warning(f"查询打手图片失败: {str(e)}") diff --git a/orders/views/dashou.py b/orders/views/dashou.py index 0a62212..55ebe1a 100644 --- a/orders/views/dashou.py +++ b/orders/views/dashou.py @@ -1357,12 +1357,14 @@ class DashouTijiaoView(APIView): }, status=status.HTTP_403_FORBIDDEN) # 5. 存储图片到Dashoutupian表(统一记在接单人/队长名下,组员可读) + # 小程序直传 COS 后常只带相对 Key;入库统一成完整 URL,后台/客服可不依赖拼接 + from utils.oss_utils import to_full_cos_url owner_uid = dingdan.PlayerID or yonghuid for tupian_url in jiaofu_tupian_urls: PlayerDeliveryImage.query.create( OrderID=dingdan_id, PlayerID=owner_uid, - ImageURL=tupian_url, + ImageURL=to_full_cos_url(tupian_url), CreateTime=timezone.now() ) @@ -1502,12 +1504,15 @@ class DashouDingdanXiangqingView(APIView): } try: from orders.models import RefundEvidenceImage - dingdan_data['tuikuan_tupian'] = list( - RefundEvidenceImage.query.filter(OrderID=dingdan.OrderID) + from utils.oss_utils import to_full_cos_url + dingdan_data['tuikuan_tupian'] = [ + to_full_cos_url(u) + for u in RefundEvidenceImage.query.filter(OrderID=dingdan.OrderID) .exclude(ImageURL__isnull=True) .exclude(ImageURL='') .values_list('ImageURL', flat=True) - ) + if u + ] except Exception: dingdan_data['tuikuan_tupian'] = [] try: @@ -1547,9 +1552,10 @@ class DashouDingdanXiangqingView(APIView): ).order_by('CreateTime') jiaofu_tupian_urls = [] + from utils.oss_utils import to_full_cos_url for tupian_record in dashou_tupian_list: if tupian_record.ImageURL: - jiaofu_tupian_urls.append(tupian_record.ImageURL) + jiaofu_tupian_urls.append(to_full_cos_url(tupian_record.ImageURL)) dingdan_data['dashou_jiaofu'] = jiaofu_tupian_urls @@ -1787,28 +1793,36 @@ class DashouXiugaiView(APIView): # ---------- 执行修改(使用事务保证原子性) ---------- try: + from utils.oss_utils import cos_url_variants, to_full_cos_url with transaction.atomic(): - # 1. 处理删除的图片:从 Dashoutupian 表中删除记录 + # 1. 处理删除的图片:相对/绝对 URL 都能匹配 if deleted_images: + match_urls = set() + for u in deleted_images: + match_urls.update(cos_url_variants(u)) delete_count, _ = PlayerDeliveryImage.query.filter( OrderID=dingdan_id, - ImageURL__in=deleted_images + ImageURL__in=list(match_urls), ).delete() logger.info(f"订单 {dingdan_id} 删除图片 {delete_count} 张") - # 2. 处理新增的图片:插入新记录 + # 2. 处理新增的图片:入库统一完整 URL if new_images: # 批量创建 new_records = [] for url in new_images: + full = to_full_cos_url(url) + if not full: + continue new_records.append(PlayerDeliveryImage( OrderID=dingdan_id, PlayerID=owner_uid, - ImageURL=url, + ImageURL=full, CreateTime=timezone.now() )) - PlayerDeliveryImage.objects.bulk_create(new_records) - logger.info(f"订单 {dingdan_id} 新增图片 {len(new_records)} 张") + if new_records: + PlayerDeliveryImage.objects.bulk_create(new_records) + logger.info(f"订单 {dingdan_id} 新增图片 {len(new_records)} 张") # 3. 更新订单留言(如果发生变化) # 注意:即使留言为空字符串,也允许更新,表示清空留言 diff --git a/orders/views/merchant.py b/orders/views/merchant.py index 9f857c0..b69e62c 100644 --- a/orders/views/merchant.py +++ b/orders/views/merchant.py @@ -660,10 +660,11 @@ class ShangjiaDingdanXiangqingView(APIView): order = shangjia_kuozhan.Order # 4. 交付图片列表 + from utils.oss_utils import to_full_cos_url dashou_tupian_list = PlayerDeliveryImage.query.filter( OrderID=dingdan_id ).order_by('CreateTime').values_list('ImageURL', flat=True) - dashou_jiaofu = list(dashou_tupian_list) + dashou_jiaofu = [to_full_cos_url(u) for u in dashou_tupian_list if u] # 5. 打手详细信息(头像、昵称、用户ID) dashou_yonghuid = order.PlayerID or '' @@ -818,12 +819,15 @@ class ShangjiaDingdanXiangqingView(APIView): } try: - response_data['tuikuan_tupian'] = list( - RefundEvidenceImage.query.filter(OrderID=dingdan_id) + from utils.oss_utils import to_full_cos_url as _full_cos + response_data['tuikuan_tupian'] = [ + _full_cos(u) + for u in RefundEvidenceImage.query.filter(OrderID=dingdan_id) .exclude(ImageURL__isnull=True) .exclude(ImageURL='') .values_list('ImageURL', flat=True) - ) + if u + ] except Exception: response_data['tuikuan_tupian'] = [] @@ -1375,7 +1379,13 @@ class ShangjiaTuikuanShenqingView(APIView): if not isinstance(tuikuan_tupian_urls, list): tuikuan_tupian_urls = [] - valid_urls = [str(u).strip() for u in tuikuan_tupian_urls if u and str(u).strip()] + from utils.oss_utils import to_full_cos_url + valid_urls = [ + to_full_cos_url(u) + for u in tuikuan_tupian_urls + if u and str(u).strip() + ] + valid_urls = [u for u in valid_urls if u] # 4. 参数验证(文字 + 至少1张证据图) if not dingdan_id or not tuikuan_liyou: diff --git a/users/views/kefu_orders.py b/users/views/kefu_orders.py index ad857bc..41b07ce 100644 --- a/users/views/kefu_orders.py +++ b/users/views/kefu_orders.py @@ -1674,8 +1674,9 @@ class KefuGetOrderDetailView(APIView): # 6. 打手图片 try: + from utils.oss_utils import to_full_cos_url dashou_images = PlayerDeliveryImage.query.filter(OrderID=dingdan_id).values_list('ImageURL', flat=True) - response_data['dashou_images'] = [img for img in dashou_images if img] + response_data['dashou_images'] = [to_full_cos_url(img) for img in dashou_images if img] except Exception as e: logger.exception(f"[kefuhqddxq] 打手图片查询异常 dingdan_id={dingdan_id}") response_data['dashou_images'] = [] @@ -1690,12 +1691,15 @@ class KefuGetOrderDetailView(APIView): response_data.setdefault('reject_reason', '') try: from orders.models import RefundEvidenceImage - response_data['tuikuan_tupian'] = list( - RefundEvidenceImage.query.filter(OrderID=dingdan_id) + from utils.oss_utils import to_full_cos_url + response_data['tuikuan_tupian'] = [ + to_full_cos_url(u) + for u in RefundEvidenceImage.query.filter(OrderID=dingdan_id) .exclude(ImageURL__isnull=True) .exclude(ImageURL='') .values_list('ImageURL', flat=True) - ) + if u + ] except Exception as e: logger.exception(f"[kefuhqddxq] 退款图片查询异常 dingdan_id={dingdan_id}") response_data['tuikuan_tupian'] = [] diff --git a/utils/oss_utils.py b/utils/oss_utils.py index c2db037..e8b37ea 100644 --- a/utils/oss_utils.py +++ b/utils/oss_utils.py @@ -184,6 +184,44 @@ def _upload_to_aliyun_oss(file_obj, file_path): return None +def _cos_public_domain() -> str: + """COS 公网域名(无尾斜杠)。""" + return (getattr(settings, 'COS_DOMAIN', '') or '').rstrip('/') + + +def to_full_cos_url(url_or_key) -> str: + """ + 相对 COS Key / 协议相对 URL → 完整 https URL。 + 已是 http(s) 则原样返回。用于交付图、退款证据图入库,避免后台当完整 URL 用时裂图。 + """ + s = str(url_or_key or '').strip() + if not s: + return '' + if s.startswith('//'): + return f'https:{s}' + if s.startswith('http://') or s.startswith('https://'): + return s + domain = _cos_public_domain() + key = s.lstrip('/') + if not domain: + return key + return f'{domain}/{key}' + + +def cos_url_variants(url_or_key) -> list: + """同一张图在库里可能存相对或绝对,删除匹配时两边都试。""" + s = str(url_or_key or '').strip() + if not s: + return [] + full = to_full_cos_url(s) + key = _url_to_oss_key(s) or s.lstrip('/') + out = [] + for x in (s, full, key, f'/{key}' if key else ''): + if x and x not in out: + out.append(x) + return out + + def _url_to_oss_key(file_url): """从完整 URL 或相对路径提取 OSS 对象 Key""" if not file_url: