Files
dukang/server/dukang-api/src/integrations/map/tencent-lbs.provider.ts
T

120 lines
3.2 KiB
TypeScript

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;
}
}
}