Spaces:
Running
Running
| """用户建议反馈 - HF 仓库读写""" | |
| import json | |
| import os | |
| import tempfile | |
| import uuid | |
| from datetime import datetime | |
| from config import FEEDBACK_REPO_PREFIX | |
| from repo_util import download_repo_file, list_repo_files_safe, load_repo_json, upload_repo_file | |
| def _parse_created(value): | |
| if not value: | |
| return datetime.min | |
| try: | |
| return datetime.fromisoformat(str(value).replace("Z", "+00:00")) | |
| except ValueError: | |
| try: | |
| return datetime.strptime(str(value)[:19], "%Y-%m-%d %H:%M:%S") | |
| except ValueError: | |
| return datetime.min | |
| def _normalize_feedback(meta): | |
| meta = dict(meta or {}) | |
| meta.setdefault("feedback_id", "") | |
| meta.setdefault("username", "匿名") | |
| meta.setdefault("text", "") | |
| meta.setdefault("is_public", False) | |
| meta.setdefault("created", "") | |
| meta.setdefault("images", []) | |
| meta["image_count"] = len(meta.get("images") or []) | |
| meta["public_label"] = "公开" if meta.get("is_public") else "仅管理员可见" | |
| text = meta.get("text") or "" | |
| meta["text_preview"] = text if len(text) <= 80 else text[:80] + "..." | |
| return meta | |
| def fetch_all_feedback(): | |
| """获取全部用户反馈(本地管理端)""" | |
| files = list_repo_files_safe() | |
| prefix = FEEDBACK_REPO_PREFIX.rstrip("/") + "/" | |
| json_files = [f for f in files if f.startswith(prefix) and f.endswith(".json")] | |
| feedbacks = [] | |
| for json_path in json_files: | |
| try: | |
| meta = _normalize_feedback(load_repo_json(json_path)) | |
| feedbacks.append(meta) | |
| except Exception: | |
| continue | |
| feedbacks.sort(key=lambda item: _parse_created(item.get("created")), reverse=True) | |
| return feedbacks | |
| def fetch_public_feedback(): | |
| """获取允许公开展示的反馈(web 端)""" | |
| return [item for item in fetch_all_feedback() if item.get("is_public")] | |
| def submit_feedback(username, text, image_files=None, is_public=False): | |
| """提交用户反馈到 HF 仓库""" | |
| text = (text or "").strip() | |
| if not text: | |
| raise ValueError("请填写反馈内容") | |
| feedback_id = uuid.uuid4().hex | |
| image_paths = [] | |
| image_files = image_files or [] | |
| for index, image_path in enumerate(image_files): | |
| if not image_path or not os.path.exists(image_path): | |
| continue | |
| ext = os.path.splitext(image_path)[1].lower() or ".png" | |
| remote_path = f"{FEEDBACK_REPO_PREFIX}/{feedback_id}_{index}{ext}" | |
| upload_repo_file(image_path, remote_path) | |
| image_paths.append(remote_path) | |
| meta = { | |
| "feedback_id": feedback_id, | |
| "username": (username or "").strip() or "匿名", | |
| "text": text, | |
| "is_public": bool(is_public), | |
| "created": datetime.now().isoformat(), | |
| "images": image_paths, | |
| } | |
| with tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".json", delete=False) as fp: | |
| json.dump(meta, fp, ensure_ascii=False, indent=2) | |
| tmp_json = fp.name | |
| try: | |
| upload_repo_file(tmp_json, f"{FEEDBACK_REPO_PREFIX}/{feedback_id}.json") | |
| finally: | |
| os.remove(tmp_json) | |
| return meta | |
| def get_feedback(feedback_id): | |
| """按 ID 获取单条反馈""" | |
| json_path = f"{FEEDBACK_REPO_PREFIX}/{feedback_id}.json" | |
| return _normalize_feedback(load_repo_json(json_path)) | |
| def ensure_feedback_image_cached(feedback_id, image_index, cache_dir): | |
| """下载反馈图片到本地缓存并返回路径""" | |
| meta = get_feedback(feedback_id) | |
| images = meta.get("images") or [] | |
| if image_index < 0 or image_index >= len(images): | |
| raise FileNotFoundError("图片不存在") | |
| remote_path = images[image_index] | |
| filename = os.path.basename(remote_path) | |
| local_path = os.path.join(cache_dir, f"{feedback_id}_{filename}") | |
| if os.path.exists(local_path): | |
| return local_path | |
| return download_repo_file(remote_path, local_path) | |