| 200 | |
| 201 | |
| 202 | class VersionService: |
| 203 | # NOTE: 版本信息获取受网络与服务端限流影响,需提供简易降级;此处先直接请求,后续可加缓存 |
| 204 | |
| 205 | RELEASES_URL = 'https://api.github.com/repos/WavesMan/Disable-automatic-Windows-update/releases' |
| 206 | RELEASE_PAGE = 'https://github.com/WavesMan/Disable-automatic-Windows-update/releases' |
| 207 | CURRENT_VERSION = PACKAGE_VERSION |
| 208 | |
| 209 | def get_version(self) -> Result: |
| 210 | start = time.perf_counter() |
| 211 | log_event(logging.INFO, "version_get_started", "开始获取版本信息", action="get_version", status="started") |
| 212 | try: |
| 213 | req = urllib.request.Request(self.RELEASES_URL) |
| 214 | req.add_header('User-Agent', 'Python/3.13') |
| 215 | with urllib.request.urlopen(req, timeout=10) as resp: |
| 216 | data = json.loads(resp.read().decode("utf-8")) |
| 217 | releases = [r for r in data if r.get("tag_name", "").startswith("EXE-")] |
| 218 | ver = releases[0]["tag_name"] if releases else "无版本" |
| 219 | duration_ms = int((time.perf_counter() - start) * 1000) |
| 220 | log_event(logging.INFO, "version_get_finished", "获取版本信息成功", action="get_version", status="ok", duration_ms=duration_ms) |
| 221 | return Result(True, data={"version": ver, "source": "github"}) |
| 222 | except Exception as e: |
| 223 | duration_ms = int((time.perf_counter() - start) * 1000) |
| 224 | log_event( |
| 225 | logging.ERROR, |
| 226 | "version_get_finished", |
| 227 | f"获取版本信息失败: {e}", |
| 228 | action="get_version", |
| 229 | status="failed", |
| 230 | duration_ms=duration_ms, |
| 231 | error_type=type(e).__name__, |
| 232 | error_message=str(e), |
| 233 | traceback=traceback.format_exc(), |
| 234 | exc_info=True, |
| 235 | ) |
| 236 | return Result(False, error={"code": "NETWORK_ERROR", "message": str(e)}) |
| 237 | |
| 238 | def _normalize(self, tag: str) -> str: |
| 239 | s = (tag or "").strip() |
| 240 | if s.startswith("EXE-"): |
| 241 | s = s[4:] |
| 242 | if s[:1].lower() == "v": |
| 243 | s = s[1:] |
| 244 | return s |
| 245 | |
| 246 | def _parse_nums(self, s: str) -> list[int]: |
| 247 | s = self._normalize(s) |
| 248 | nums = re.findall(r"\d+", s) |
| 249 | return [int(n) for n in nums] if nums else [] |
| 250 | |
| 251 | def _cmp(self, a: str, b: str) -> int: |
| 252 | na = self._parse_nums(a) |
| 253 | nb = self._parse_nums(b) |
| 254 | if na and nb: |
| 255 | L = max(len(na), len(nb)) |
| 256 | for i in range(L): |
| 257 | va = na[i] if i < len(na) else 0 |
| 258 | vb = nb[i] if i < len(nb) else 0 |
| 259 | if va < vb: |