import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; import IP2Region from 'ip2region'; import type { IpRegion } from './geo.types'; function cleanRegionName(value?: string | null): string | null { if (!value) return null; const trimmed = value.trim(); if (!trimmed || trimmed === '0' || trimmed === '内网IP') return null; return trimmed; } @Injectable() export class IpGeoService implements OnModuleInit { private readonly logger = new Logger(IpGeoService.name); private query: IP2Region | null = null; onModuleInit() { try { this.query = new IP2Region(); } catch (error) { this.logger.warn(`ip2region 初始化失败: ${error instanceof Error ? error.message : error}`); } } resolve(ip: string | null): IpRegion { if (!ip || !this.query) { return { province: null, city: null, district: null }; } if (ip === '127.0.0.1' || ip.startsWith('192.168.') || ip.startsWith('10.')) { return { province: null, city: null, district: null }; } try { const result = this.query.search(ip) as { province?: string; city?: string; region?: string; }; return { province: cleanRegionName(result?.province), city: cleanRegionName(result?.city), district: cleanRegionName(result?.region), }; } catch (error) { this.logger.debug(`IP 解析失败 ${ip}: ${error instanceof Error ? error.message : error}`); return { province: null, city: null, district: null }; } } }