diff --git a/api_v1/utils/tencentcloud_tiia.py b/api_v1/utils/tencentcloud_tiia.py index fe9f5d1..3b6c40b 100644 --- a/api_v1/utils/tencentcloud_tiia.py +++ b/api_v1/utils/tencentcloud_tiia.py @@ -24,6 +24,26 @@ def _truncate(s: str, max_len: int) -> str: return s return s[:max_len] +def _default_tiia_tags_for_image_url(image_url: str) -> str: + """ + TencentCloud TIIA `Tags` is a string field; in practice people often put JSON string inside. + We store the original accessible image URL there so SearchImage results can carry it back. + """ + payload = {'imageUrl': (image_url or '').strip()} + # Keep it compact and readable. + return json.dumps(payload, ensure_ascii=False, separators=(',', ':')) + +def _compute_tiia_pic_name(*, image_url: str, pic_prefix: str) -> str: + parsed = urlparse(image_url) + basename = (parsed.path.rsplit('/', 1)[-1] or '').strip() + if basename: + pic_name = f'{pic_prefix}_{basename}' + else: + # Fallback: keep old behavior (random) to avoid breaking existing uploaded naming. + uid = uuid.uuid4().hex + pic_name = f'{pic_prefix}_{uid}' + return _truncate(pic_name, 128) + class SimpleRateLimiter: """ @@ -154,7 +174,7 @@ def search_image_url_in_tencent_tiia( return json.loads(resp.to_json_string()) -def upload_image_url_to_tencent_tiia(*, image_url: str, entity_id: str) -> dict: +def upload_image_url_to_tencent_tiia(*, image_url: str, entity_id: str, tags: str | None = None, custom_content: str | None = None) -> dict: """ Upload an image by URL to Tencent Cloud TIIA gallery using CreateImage. @@ -202,12 +222,9 @@ def upload_image_url_to_tencent_tiia(*, image_url: str, entity_id: str) -> dict: pic_prefix = (getattr(settings, 'TENCENTCLOUD_TIIA_PIC_NAME_PREFIX', None) or 'plate_order').strip() or 'plate_order' # CreateImage requires PicName; we auto-generate it. - uid = uuid.uuid4().hex # NOTE: 云端对字段长度通常有限制,这里做一个保守截断,避免超长导致 400。 entity_id = _truncate(entity_id, 128) - basename = (parsed.path.rsplit('/', 1)[-1] or '').strip() - pic_name = f'{pic_prefix}_{basename}' if basename else f'{pic_prefix}_{uid}' - pic_name = _truncate(pic_name, 128) + pic_name = _compute_tiia_pic_name(image_url=image_url, pic_prefix=pic_prefix) try: from tencentcloud.common import credential @@ -229,10 +246,69 @@ def upload_image_url_to_tencent_tiia(*, image_url: str, entity_id: str) -> dict: req.EntityId = str(entity_id) req.PicName = str(pic_name) req.ImageUrl = str(image_url) + # Store accessible URL into tags so SearchImage result can carry it back. + # (UpdateImage only supports Tags, not CustomContent, so Tags is the primary field.) + req.Tags = str(tags) if tags is not None else _default_tiia_tags_for_image_url(image_url) + # Also store it in CustomContent for human readability (CreateImage supports it). + req.CustomContent = str(custom_content) if custom_content is not None else str(image_url) resp = client.CreateImage(req) return json.loads(resp.to_json_string()) + +def update_image_tags_in_tencent_tiia(*, entity_id: str, pic_name: str, tags: str) -> dict: + """ + Update an existing image metadata in TencentCloud TIIA gallery. + + Note: TencentCloud `UpdateImage` supports updating `Tags` only (no CustomContent). + """ + entity_id = (entity_id or '').strip() + pic_name = (pic_name or '').strip() + tags = (tags or '').strip() + if not entity_id: + raise ValueError('entity_id 不能为空') + if not pic_name: + raise ValueError('pic_name 不能为空') + if not tags: + raise ValueError('tags 不能为空') + + group_id = (getattr(settings, 'TENCENTCLOUD_TIIA_GROUP_ID', '') or '').strip() + if not group_id: + raise ValueError('缺少 settings: TENCENTCLOUD_TIIA_GROUP_ID') + + secret_id = (getattr(settings, 'TENCENTCLOUD_SECRET_ID', '') or '').strip() + secret_key = (getattr(settings, 'TENCENTCLOUD_SECRET_KEY', '') or '').strip() + token = (getattr(settings, 'TENCENTCLOUD_TOKEN', '') or '').strip() or None + if not secret_id or not secret_key: + raise ValueError('缺少 settings: TENCENTCLOUD_SECRET_ID / TENCENTCLOUD_SECRET_KEY') + + region = (getattr(settings, 'TENCENTCLOUD_TIIA_REGION', None) or 'ap-guangzhou').strip() + endpoint = (getattr(settings, 'TENCENTCLOUD_TIIA_ENDPOINT', None) or 'tiia.tencentcloudapi.com').strip() + + try: + from tencentcloud.common import credential + from tencentcloud.common.profile.client_profile import ClientProfile + from tencentcloud.common.profile.http_profile import HttpProfile + from tencentcloud.tiia.v20190529 import tiia_client, models + except Exception as e: # pragma: no cover + raise RuntimeError( + "TencentCloud SDK 未安装或不可用,请先安装依赖:tencentcloud-sdk-python" + ) from e + + cred = credential.Credential(secret_id, secret_key, token) + http_profile = HttpProfile(endpoint=endpoint, reqTimeout=30) + client_profile = ClientProfile(httpProfile=http_profile) + client = tiia_client.TiiaClient(cred, region, client_profile) + + req = models.UpdateImageRequest() + req.GroupId = str(group_id) + req.EntityId = _truncate(str(entity_id), 128) + req.PicName = _truncate(str(pic_name), 128) + req.Tags = str(tags) + + resp = client.UpdateImage(req) + return json.loads(resp.to_json_string()) + def _extract_urls_from_plate_image(plate_image) -> list[str]: """ 从 PlateOrder.plate_image(JSON) 中提取所有可能的图片 URL/Path。 @@ -349,5 +425,6 @@ __all__ = [ 'upload_image_url_to_tencent_tiia', 'search_image_url_in_tencent_tiia', 'upload_plate_order_images_to_tencent_tiia', + 'update_image_tags_in_tencent_tiia', ]