定位获取城市功能,需要微信地图的key?
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { loadAppConfig } from '@dukang/shared-types';
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import type { WechatActorRef } from '../wechat/wechat-log.util';
|
||||
|
||||
export type ReverseGeocodeResult = {
|
||||
province: string;
|
||||
city: string;
|
||||
district: string;
|
||||
logId: bigint;
|
||||
};
|
||||
|
||||
function normalizeCityName(name: string) {
|
||||
return name.replace(/市$/, '').trim();
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class TencentLbsProvider {
|
||||
private readonly logger = new Logger(TencentLbsProvider.name);
|
||||
private readonly config = loadAppConfig();
|
||||
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
isEnabled() {
|
||||
return !!this.config.tencentLbsKey;
|
||||
}
|
||||
|
||||
async reverseGeocode(
|
||||
latitude: number,
|
||||
longitude: number,
|
||||
actorRef?: WechatActorRef,
|
||||
): Promise<ReverseGeocodeResult | null> {
|
||||
const baseLog = {
|
||||
provider: 'WECHAT_MAP' as const,
|
||||
scene: 'REVERSE_GEOCODE',
|
||||
refType: actorRef?.refType,
|
||||
refId: actorRef?.refId,
|
||||
requestUrl: 'https://apis.map.qq.com/ws/geocoder/v1/',
|
||||
requestBody: {
|
||||
latitude: Number(latitude.toFixed(6)),
|
||||
longitude: Number(longitude.toFixed(6)),
|
||||
},
|
||||
};
|
||||
|
||||
if (!this.isEnabled()) {
|
||||
const log = await this.prisma.logThirdParty.create({
|
||||
data: {
|
||||
...baseLog,
|
||||
status: 'FAILED',
|
||||
errorMessage: 'TENCENT_LBS_KEY 未配置',
|
||||
},
|
||||
});
|
||||
this.logger.warn('Tencent LBS key missing, skip reverse geocode');
|
||||
return null;
|
||||
}
|
||||
|
||||
const location = `${latitude},${longitude}`;
|
||||
const url = new URL('https://apis.map.qq.com/ws/geocoder/v1/');
|
||||
url.searchParams.set('location', location);
|
||||
url.searchParams.set('key', this.config.tencentLbsKey);
|
||||
url.searchParams.set('get_poi', '0');
|
||||
|
||||
try {
|
||||
const res = await fetch(url.toString());
|
||||
const data = (await res.json()) as {
|
||||
status?: number;
|
||||
message?: string;
|
||||
result?: {
|
||||
ad_info?: {
|
||||
province?: string;
|
||||
city?: string;
|
||||
district?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const ad = data.result?.ad_info;
|
||||
const ok = data.status === 0 && !!ad?.city;
|
||||
const responseBody = {
|
||||
status: data.status,
|
||||
message: data.message,
|
||||
province: ad?.province,
|
||||
city: ad?.city,
|
||||
district: ad?.district,
|
||||
};
|
||||
|
||||
const log = await this.prisma.logThirdParty.create({
|
||||
data: {
|
||||
...baseLog,
|
||||
responseBody,
|
||||
status: ok ? 'SUCCESS' : 'FAILED',
|
||||
errorMessage: ok ? undefined : data.message ?? '逆地理编码失败',
|
||||
},
|
||||
});
|
||||
|
||||
if (!ok || !ad?.province || !ad?.city) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
province: ad.province,
|
||||
city: normalizeCityName(ad.city),
|
||||
district: ad.district ?? '',
|
||||
logId: log.id,
|
||||
};
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
this.logger.error(`Tencent LBS reverse geocode failed: ${message}`);
|
||||
await this.prisma.logThirdParty.create({
|
||||
data: {
|
||||
...baseLog,
|
||||
status: 'FAILED',
|
||||
errorMessage: message.slice(0, 512),
|
||||
},
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user