feat: 技术支持工单/企微权限/开发版本管理/消息推送等迭代
This commit is contained in:
@@ -1,156 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ClientApp } from '@dukang/shared-types';
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import { TencentLbsProvider } from '../../integrations/map/tencent-lbs.provider';
|
||||
import { logWechatAuth, type WechatActorRef } from '../../integrations/wechat/wechat-log.util';
|
||||
import { AnalyticsService } from '../analytics/analytics.service';
|
||||
|
||||
export type ReportWechatLocationInput = {
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
sdk: 'jssdk' | 'geolocation';
|
||||
status: 'success' | 'fail';
|
||||
errMsg?: string;
|
||||
clientApp?: string;
|
||||
userId?: bigint;
|
||||
};
|
||||
|
||||
export type ReportWechatLocationResult = {
|
||||
province?: string;
|
||||
city?: string;
|
||||
district?: string;
|
||||
cityCode?: string;
|
||||
cityName?: string;
|
||||
openCity: boolean;
|
||||
thirdPartyLogIds: {
|
||||
location?: string;
|
||||
geocode?: string;
|
||||
};
|
||||
};
|
||||
|
||||
function normalizeCityName(name: string) {
|
||||
return name.replace(/市$/, '').trim();
|
||||
}
|
||||
|
||||
function matchOpenCity(
|
||||
cities: Array<{ code: string; name: string; province: string }>,
|
||||
province: string,
|
||||
city: string,
|
||||
) {
|
||||
const cityNorm = normalizeCityName(city);
|
||||
return cities.find((c) => {
|
||||
const nameNorm = normalizeCityName(c.name);
|
||||
if (nameNorm !== cityNorm && c.name !== city && c.name !== `${cityNorm}市`) return false;
|
||||
if (c.province && province && c.province !== province) return false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class WechatLocationService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly tencentLbs: TencentLbsProvider,
|
||||
private readonly analyticsService: AnalyticsService,
|
||||
) {}
|
||||
|
||||
async reportLocation(input: ReportWechatLocationInput): Promise<ReportWechatLocationResult> {
|
||||
const actorRef: WechatActorRef | undefined = input.userId
|
||||
? { refType: 'USER', refId: input.userId }
|
||||
: undefined;
|
||||
const clientApp = (input.clientApp as ClientApp) || ClientApp.USER_H5;
|
||||
const thirdPartyLogIds: ReportWechatLocationResult['thirdPartyLogIds'] = {};
|
||||
|
||||
const locationLogId = await logWechatAuth(this.prisma, {
|
||||
scene: 'GET_LOCATION',
|
||||
requestBody: {
|
||||
sdk: input.sdk,
|
||||
status: input.status,
|
||||
...(input.latitude != null && input.longitude != null
|
||||
? {
|
||||
latitude: Number(input.latitude.toFixed(3)),
|
||||
longitude: Number(input.longitude.toFixed(3)),
|
||||
}
|
||||
: {}),
|
||||
...(input.errMsg ? { errMsg: input.errMsg.slice(0, 200) } : {}),
|
||||
},
|
||||
responseBody: { reported: true },
|
||||
status: input.status === 'success' ? 'SUCCESS' : 'FAILED',
|
||||
errorMessage: input.status === 'fail' ? input.errMsg?.slice(0, 512) : undefined,
|
||||
actorRef,
|
||||
});
|
||||
thirdPartyLogIds.location = locationLogId.toString();
|
||||
|
||||
if (input.status !== 'success' || input.latitude == null || input.longitude == null) {
|
||||
return { openCity: false, thirdPartyLogIds };
|
||||
}
|
||||
|
||||
const geo = await this.tencentLbs.reverseGeocode(input.latitude, input.longitude, actorRef);
|
||||
if (geo) {
|
||||
thirdPartyLogIds.geocode = geo.logId.toString();
|
||||
}
|
||||
if (!geo) {
|
||||
return { openCity: false, thirdPartyLogIds };
|
||||
}
|
||||
|
||||
const openCities = await this.prisma.commonCity.findMany({
|
||||
where: { status: 'ACTIVE' },
|
||||
select: { code: true, name: true, province: true },
|
||||
});
|
||||
const matched = matchOpenCity(openCities, geo.province, geo.city);
|
||||
|
||||
const result: ReportWechatLocationResult = {
|
||||
province: geo.province,
|
||||
city: geo.city,
|
||||
district: geo.district,
|
||||
cityCode: matched?.code,
|
||||
cityName: matched?.name ?? `${geo.city}市`,
|
||||
openCity: !!matched,
|
||||
thirdPartyLogIds,
|
||||
};
|
||||
|
||||
if (input.userId) {
|
||||
const mapLogId = geo.logId;
|
||||
this.analyticsService.trackOneSafe(input.userId, clientApp, {
|
||||
eventName: 'wechat_location',
|
||||
refType: 'THIRD_PARTY_LOG',
|
||||
refId: mapLogId,
|
||||
extraJson: {
|
||||
sdk: input.sdk,
|
||||
province: geo.province,
|
||||
city: geo.city,
|
||||
district: geo.district,
|
||||
openCity: !!matched,
|
||||
cityCode: matched?.code,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async reportChooseImage(input: {
|
||||
status: 'success' | 'fail';
|
||||
errMsg?: string;
|
||||
sourceType?: string;
|
||||
stage?: string;
|
||||
pageUrl?: string;
|
||||
actorRef?: WechatActorRef;
|
||||
}) {
|
||||
const logId = await logWechatAuth(this.prisma, {
|
||||
scene: 'CHOOSE_IMAGE',
|
||||
requestUrl: input.pageUrl?.split('#')[0]?.slice(0, 512),
|
||||
requestBody: {
|
||||
status: input.status,
|
||||
...(input.sourceType ? { sourceType: input.sourceType } : {}),
|
||||
...(input.stage ? { stage: input.stage } : {}),
|
||||
...(input.errMsg ? { errMsg: input.errMsg.slice(0, 200) } : {}),
|
||||
},
|
||||
responseBody: { reported: true },
|
||||
status: input.status === 'success' ? 'SUCCESS' : 'FAILED',
|
||||
errorMessage: input.status === 'fail' ? input.errMsg?.slice(0, 512) : undefined,
|
||||
actorRef: input.actorRef,
|
||||
});
|
||||
return { ok: true, logId: logId.toString() };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user