200 lines
5.4 KiB
TypeScript
200 lines
5.4 KiB
TypeScript
import type { FirmInfo, Lawyer } from '../types/card';
|
|
import { request } from '../utils/http';
|
|
|
|
interface OpenFirmResponse {
|
|
name: string;
|
|
logo: string;
|
|
heroImage: string;
|
|
intro: string;
|
|
hotlinePhone: string;
|
|
hqAddress: string;
|
|
hqLatitude: number | string | null;
|
|
hqLongitude: number | string | null;
|
|
officeCount: number;
|
|
lawyerCount: number;
|
|
officeList: string[];
|
|
practiceAreas: string[];
|
|
}
|
|
|
|
interface OpenCardListItem {
|
|
id: number;
|
|
name: string;
|
|
title: string;
|
|
office: string;
|
|
phone: string;
|
|
email: string;
|
|
avatar: string;
|
|
specialties: string[];
|
|
}
|
|
|
|
interface OpenCardDetailResponse {
|
|
id: number;
|
|
name: string;
|
|
title: string;
|
|
office: string;
|
|
phone: string;
|
|
email: string;
|
|
address: string;
|
|
avatar: string;
|
|
coverImage: string;
|
|
wechatQrImage: string;
|
|
bio: string;
|
|
specialties: string[];
|
|
firmName: string;
|
|
firmAddress: string;
|
|
firmLatitude: number | string | null;
|
|
firmLongitude: number | string | null;
|
|
}
|
|
|
|
function parseCoordinate(value: number | string | null | undefined): number | null {
|
|
if (typeof value === 'number') {
|
|
return Number.isFinite(value) ? value : null;
|
|
}
|
|
if (typeof value !== 'string') {
|
|
return null;
|
|
}
|
|
const normalized = value.trim();
|
|
if (!normalized) {
|
|
return null;
|
|
}
|
|
const parsed = Number(normalized);
|
|
return Number.isFinite(parsed) ? parsed : null;
|
|
}
|
|
|
|
function isLatitude(value: number | null): value is number {
|
|
return value !== null && value >= -90 && value <= 90;
|
|
}
|
|
|
|
function isLongitude(value: number | null): value is number {
|
|
return value !== null && value >= -180 && value <= 180;
|
|
}
|
|
|
|
function normalizeCoordinates(latitude: number | string | null | undefined, longitude: number | string | null | undefined): {
|
|
latitude: number;
|
|
longitude: number;
|
|
} {
|
|
const parsedLatitude = parseCoordinate(latitude);
|
|
const parsedLongitude = parseCoordinate(longitude);
|
|
|
|
if (isLatitude(parsedLatitude) && isLongitude(parsedLongitude)) {
|
|
return {
|
|
latitude: parsedLatitude,
|
|
longitude: parsedLongitude,
|
|
};
|
|
}
|
|
|
|
if (isLatitude(parsedLongitude) && isLongitude(parsedLatitude)) {
|
|
return {
|
|
latitude: parsedLongitude,
|
|
longitude: parsedLatitude,
|
|
};
|
|
}
|
|
|
|
return {
|
|
latitude: 0,
|
|
longitude: 0,
|
|
};
|
|
}
|
|
|
|
function toFirmInfo(payload: OpenFirmResponse): FirmInfo {
|
|
const coordinates = normalizeCoordinates(payload.hqLatitude, payload.hqLongitude);
|
|
return {
|
|
id: payload.name || 'firm',
|
|
name: payload.name || '',
|
|
logo: payload.logo || '',
|
|
intro: payload.intro || '',
|
|
hotlinePhone: payload.hotlinePhone || '',
|
|
hqAddress: payload.hqAddress || '',
|
|
hqLatitude: coordinates.latitude,
|
|
hqLongitude: coordinates.longitude,
|
|
officeCount: payload.officeCount || 0,
|
|
lawyerCount: payload.lawyerCount || 0,
|
|
heroImage: payload.heroImage || '',
|
|
officeList: payload.officeList || [],
|
|
practiceAreas: payload.practiceAreas || [],
|
|
};
|
|
}
|
|
|
|
function toLawyer(payload: OpenCardListItem | OpenCardDetailResponse): Lawyer {
|
|
return {
|
|
id: String(payload.id),
|
|
name: payload.name || '',
|
|
title: payload.title || '',
|
|
office: payload.office || '',
|
|
phone: payload.phone || '',
|
|
email: payload.email || '',
|
|
address: 'address' in payload ? payload.address || '' : '',
|
|
avatar: payload.avatar || '',
|
|
coverImage: 'coverImage' in payload ? payload.coverImage || '' : '',
|
|
specialties: payload.specialties || [],
|
|
bio: 'bio' in payload ? payload.bio || '' : '',
|
|
wechatQrImage: 'wechatQrImage' in payload ? payload.wechatQrImage || '' : '',
|
|
};
|
|
}
|
|
|
|
export async function getFirmProfile(): Promise<FirmInfo> {
|
|
const payload = await request<OpenFirmResponse>({
|
|
url: '/api/open/firm/profile',
|
|
});
|
|
return toFirmInfo(payload);
|
|
}
|
|
|
|
export async function listLawyers(params: {
|
|
keyword?: string;
|
|
office?: string;
|
|
practiceArea?: string;
|
|
}): Promise<Lawyer[]> {
|
|
const query = Object.entries(params)
|
|
.filter(([, value]) => Boolean(value))
|
|
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value as string)}`)
|
|
.join('&');
|
|
const payload = await request<OpenCardListItem[]>({
|
|
url: `/api/open/cards${query ? `?${query}` : ''}`,
|
|
});
|
|
return payload.map((item) => toLawyer(item));
|
|
}
|
|
|
|
export async function getLawyerDetail(cardId: string, sourceType = 'DIRECT', shareFromCardId = ''): Promise<{
|
|
firm: FirmInfo;
|
|
lawyer: Lawyer;
|
|
}> {
|
|
const queryParts = [`sourceType=${encodeURIComponent(sourceType)}`];
|
|
if (shareFromCardId) {
|
|
queryParts.push(`shareFromCardId=${encodeURIComponent(shareFromCardId)}`);
|
|
}
|
|
const payload = await request<OpenCardDetailResponse>({
|
|
url: `/api/open/cards/${encodeURIComponent(cardId)}?${queryParts.join('&')}`,
|
|
});
|
|
const coordinates = normalizeCoordinates(payload.firmLatitude, payload.firmLongitude);
|
|
|
|
return {
|
|
firm: {
|
|
id: payload.firmName || 'firm',
|
|
name: payload.firmName || '',
|
|
logo: '',
|
|
intro: '',
|
|
hotlinePhone: '',
|
|
hqAddress: payload.firmAddress || '',
|
|
hqLatitude: coordinates.latitude,
|
|
hqLongitude: coordinates.longitude,
|
|
officeCount: 0,
|
|
lawyerCount: 0,
|
|
heroImage: '',
|
|
officeList: [],
|
|
practiceAreas: [],
|
|
},
|
|
lawyer: toLawyer(payload),
|
|
};
|
|
}
|
|
|
|
export async function recordCardShare(cardId: string, sharePath: string): Promise<void> {
|
|
await request<void>({
|
|
url: `/api/open/cards/${encodeURIComponent(cardId)}/share`,
|
|
method: 'POST',
|
|
data: {
|
|
shareChannel: 'WECHAT_FRIEND',
|
|
sharePath,
|
|
},
|
|
});
|
|
}
|