import { getWechatLocationDetailed } from '@dukang/weixin-sdk'; import { apiBase } from './api'; import { weixinSdk } from './weixin'; import { regionFromGeo, type RegionSelection } from './region-data'; export const GPS_CITY_STORAGE_KEY = 'dukang_gps_city'; export const CITY_STORAGE_KEY = 'dukang_selected_city'; export const FALLBACK_CITY_CODE = '410100'; export type ResolvedUserCity = { province: string; city: string; district: string; cityCode?: string; cityName?: string; openCity: boolean; region: RegionSelection; displayCity: string; }; type GpsCityCache = ResolvedUserCity & { timestamp: number }; function readCache(): GpsCityCache | null { try { const raw = sessionStorage.getItem(GPS_CITY_STORAGE_KEY); if (!raw) return null; const parsed = JSON.parse(raw) as GpsCityCache; if (Date.now() - parsed.timestamp > 30 * 60 * 1000) return null; return parsed; } catch { return null; } } function writeCache(data: ResolvedUserCity) { sessionStorage.setItem( GPS_CITY_STORAGE_KEY, JSON.stringify({ ...data, timestamp: Date.now() } satisfies GpsCityCache), ); } async function reportLocationToServer(payload: { latitude?: number; longitude?: number; sdk: 'jssdk' | 'geolocation'; status: 'success' | 'fail'; errMsg?: string; }) { const token = localStorage.getItem('accessToken'); const headers: Record = { 'Content-Type': 'application/json', 'X-Client-App': 'USER_H5', }; if (token) headers.Authorization = `Bearer ${token}`; const res = await fetch(`${apiBase}/common/wechat/location`, { method: 'POST', headers, body: JSON.stringify(payload), }); const json = await res.json(); if (json.code !== 0) { throw new Error(json.message || '定位上报失败'); } return json.data as { province?: string; city?: string; district?: string; cityCode?: string; cityName?: string; openCity?: boolean; }; } function toResolved(data: { province?: string; city?: string; district?: string; cityCode?: string; cityName?: string; openCity?: boolean; }): ResolvedUserCity | null { if (!data.province || !data.city) return null; const region = regionFromGeo(data.province, data.city, data.district); const displayCity = data.cityName ?? (data.city.endsWith('市') ? data.city : `${data.city}市`); return { province: data.province, city: data.city, district: data.district ?? '', cityCode: data.cityCode, cityName: data.cityName, openCity: !!data.openCity, region, displayCity, }; } /** 获取并解析用户当前城市(微信 JSSDK 优先),失败返回 null */ export async function resolveUserCity(force = false): Promise { if (!force) { const cached = readCache(); if (cached) return cached; } const outcome = await getWechatLocationDetailed({ apiBase, clientApp: 'USER_H5', getAccessToken: () => localStorage.getItem('accessToken'), }); if (!outcome.location) { await reportLocationToServer({ sdk: outcome.sdk, status: 'fail', errMsg: outcome.errMsg, }).catch(() => {}); return null; } try { const data = await reportLocationToServer({ latitude: outcome.location.latitude, longitude: outcome.location.longitude, sdk: outcome.sdk, status: 'success', }); const resolved = toResolved(data); if (resolved) { writeCache(resolved); if (resolved.openCity && resolved.cityCode) { localStorage.setItem(CITY_STORAGE_KEY, resolved.cityCode); } } return resolved; } catch { return null; } } export function syncCityCodeFromGps(resolved: ResolvedUserCity) { if (resolved.openCity && resolved.cityCode) { localStorage.setItem(CITY_STORAGE_KEY, resolved.cityCode); } }