zhoujiaangyao commited on
Commit
3985de6
·
1 Parent(s): f966728

fix(feishu): rpc_failed 自动重试 + 剔除抓不到的本地图片

Browse files
backend/app/services/feishu_service.py CHANGED
@@ -155,15 +155,29 @@ class FeishuService:
155
  content = prepared.encode("utf-8")
156
 
157
  folder_token = self.folder_token or self._root_folder_token()
158
- file_token = self._upload_media(safe_title, content, folder_token)
159
- ticket = self._create_import_task(safe_title, file_token, folder_token)
160
- result = self._poll_import_task(ticket)
161
 
162
- token = result.get("token") or ""
163
- doc_type = result.get("type") or "docx"
164
- url = result.get("url") or self._fallback_doc_url(doc_type, token)
165
- logger.info(f"飞书导入成功:{safe_title} -> {url}")
166
- return {"url": url, "token": token, "type": doc_type, "title": safe_title}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
 
168
  # ─── 导入流程内部步骤 ─────────────────────────────────────────────────────
169
  def _upload_media(self, title: str, content: bytes, folder_token: str) -> str:
@@ -275,19 +289,21 @@ class FeishuService:
275
 
276
  @staticmethod
277
  def _prepare_markdown(markdown: str, image_base_url: Optional[str]) -> str:
278
- """/static、/uploads 开头的相对图片链接补成绝对地址,便于飞书抓图。
279
 
280
- 飞书导入时按 http(s) 抓取图片,相对路径它无法解析;补成后端绝对地址
281
- 仅当后端对飞书服务端可达部署图片才会真正落进文档本机/内网下
282
- 飞书抓不到会自动跳过该,不影响正文导入
283
  """
284
- if not image_base_url:
285
- return markdown
286
- base = image_base_url.rstrip("/")
 
 
287
 
288
  def _repl(m: "re.Match[str]") -> str:
289
  alt, path = m.group(1), m.group(2)
290
- return f"![{alt}]({base}{path})"
291
 
292
- # 仅替换 ](/static...) 与 ](/uploads...) 这类站内相对图片
293
  return re.sub(r"!\[([^\]]*)\]\((/(?:static|uploads)/[^)]+)\)", _repl, markdown)
 
155
  content = prepared.encode("utf-8")
156
 
157
  folder_token = self.folder_token or self._root_folder_token()
 
 
 
158
 
159
+ # rpc_failed 多为飞书侧瞬时错误:整条导入流程自动重试几次。
160
+ last_exc: Optional[FeishuError] = None
161
+ for attempt in range(3):
162
+ try:
163
+ file_token = self._upload_media(safe_title, content, folder_token)
164
+ ticket = self._create_import_task(safe_title, file_token, folder_token)
165
+ result = self._poll_import_task(ticket)
166
+ token = result.get("token") or ""
167
+ doc_type = result.get("type") or "docx"
168
+ url = result.get("url") or self._fallback_doc_url(doc_type, token)
169
+ logger.info(f"飞书导入成功:{safe_title} -> {url}")
170
+ return {"url": url, "token": token, "type": doc_type, "title": safe_title}
171
+ except FeishuError as exc:
172
+ last_exc = exc
173
+ if "rpc_failed" in (exc.message or "") and attempt < 2:
174
+ logger.warning(f"飞书导入 rpc_failed,第 {attempt + 1} 次重试…")
175
+ time.sleep(2)
176
+ continue
177
+ raise
178
+ if last_exc:
179
+ raise last_exc
180
+ raise FeishuError("飞书导入失败:未知错误")
181
 
182
  # ─── 导入流程内部步骤 ─────────────────────────────────────────────────────
183
  def _upload_media(self, title: str, content: bytes, folder_token: str) -> str:
 
289
 
290
  @staticmethod
291
  def _prepare_markdown(markdown: str, image_base_url: Optional[str]) -> str:
292
+ """处理站内相对图片(/static、/uploads):
293
 
294
+ - 若 image_base_url 是飞书服务端能抓到的「公网 http(s)」地址 补成绝对地址,图片入文档。
295
+ - 若是本机/内网localhost / 127.* / 内或为空 → 飞书根本抓不到,直接**删掉**这些图片,
296
+ 只保留正文。避免飞书导入时为一张抓不到而整单失败(rpc_failed)
297
  """
298
+ base = (image_base_url or "").rstrip("/")
299
+ public = bool(re.match(r"^https?://", base)) and not re.search(
300
+ r"://(localhost|127\.|0\.0\.0\.0|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|\[?::1)",
301
+ base,
302
+ )
303
 
304
  def _repl(m: "re.Match[str]") -> str:
305
  alt, path = m.group(1), m.group(2)
306
+ return f"![{alt}]({base}{path})" if public else ""
307
 
308
+ # 仅处理 ](/static...) 与 ](/uploads...) 这类站内相对图片
309
  return re.sub(r"!\[([^\]]*)\]\((/(?:static|uploads)/[^)]+)\)", _repl, markdown)