HarmonyOS-6.1.1-MapKit:项目服务点搜索缺少可信候选时-怎样形成可执行的人工地址交接单
企业项目实施顾问视角:本文从现场服务点定位的实际困境出发,讲解如何在 HarmonyOS 6.1.1 中正确处理地图搜索结果不足或可信度低的情况,同时保留完整的搜索证据和人工地址交接的审计链路,确保现场工作的连续性和后续追溯的完整性。
一、企业场景与挑战

1.1 现场服务点定位的能力边界问题
在实际部署中,基于地图搜索的服务点定位往往面临以下困境:
- 搜索结果不足:某些偏远地区、新建区域的兴趣点数据不完整
- 可信度低:搜索返回的结果与现场实际位置偏差过大(如地名有误、坐标陈旧)
- 结果混乱:同一名称多个记录,无法准确判断哪个是目标服务点
- 联系信息缺失:搜索到的地点缺少电话、营业时间等关键业务信息
核心挑战:不能因为搜索结果不理想就导致派单流程中断。必须在以下两点间找到平衡:
- 搜索的真实性:记录下搜索的真实结果(共几条、可信度如何、返回了哪些信息)
- 流程的可用性:为现场人员提供人工确认或手动输入地址的替代方案
1.2 为什么简单的"提示补充地址"不够
许多实现采用简化方案:
搜索服务点 → 结果为空或不可信 → 提示用户"请手动输入地址" → 结束
这种方式的根本缺陷:
- 搜索证据丢失:系统无法证明"搜索确实返回了什么"或"确实没有可信候选"
- 流程层级混乱:无法区分"主动选择人工输入"和"被迫降级"
- 后续无法恢复:如果后续发现搜索结果有误,无法回溯原始搜索条件和返回值
- 交接单不可执行:现场人员凭记忆手输的地址往往存在笔误或歧义,后续派单人无法验证
- 审计链断裂:无法追溯"为什么这条服务点信息最终确定为这个地址"
1.3 企业实施的真实需求
现场服务点定位和交接必须满足以下要求:
- 搜索结果的完整记录:清晰地记下搜索条件、返回总数、前N条结果的详细信息
- 可信度的量化评估:不是"有结果"就认为成功,而是基于
reliability等指标判断是否足够可信 - 人工介入的正当性:不是因为系统故障而降级,而是基于搜索结果的真实评估后做出的业务决策
- 地址交接单的可执行性:包含服务点名称、坐标、搜索来源、可信度、人工确认时间、操作员信息等关键字段
- 异常恢复的可能性:搜索条件改进后(如修正关键词)能否重新启动搜索流程
- 审计链的连续性:从搜索条件 → 搜索结果 → 结果评估 → 人工确认 → 地址交接,完整可追溯
二、核心技术概念与设计思路

2.1 地图搜索能力的关键指标
HarmonyOS 6.1.1 的 MapKit 中,服务点搜索涉及以下关键参数和返回值:
| 维度 | API / 字段 | 含义 | 示例值 |
|---|---|---|---|
| 搜索条件 | query | 搜索关键词 | "华为" |
| 搜索位置 | location | 搜索中心点 | {lat: 39.9042, lng: 116.4074} |
| 搜索范围 | radius | 搜索半径(米) | 50000 |
| 语言 | language | 返回结果的语言 | "zh-CN" |
| 分页 | pageIndex, pageSize | 分页信息 | pageIndex: 1, pageSize: 5 |
| 返回总数 | totalCount | 服务点总数 | 123 |
| 单条结果 | Site 对象 | 包含名称、坐标、可信度等 | 见下表 |
Site 对象的关键字段:
| 字段 | 类型 | 含义 | 可用性 |
|---|---|---|---|
siteId | string | 地点的唯一标识 | 总是有 |
name | string | 地点名称 | 可能为空 |
formatAddress | string | 格式化地址 | 可能为空 |
location | LatLng | 坐标 | 可能为空 |
reliability | number | 可信度分数 | 可能未提供 |
| 其他字段 | … | 电话、营业时间等 | 可能缺失 |
关键发现:reliability 字段不总是被返回,某些地区或类型的搜索结果可能没有这个值。
2.2 搜索结果的可信度评估模型

搜索返回结果后,不能直接当作"可用"使用,需要多维度评估:
interface SearchResultEvaluation {
// 数量维度
hasAnyResult: boolean; // 是否有任何结果
totalCount: number; // 搜索返回的总数
displayCount: number; // 当前页显示的条数
// 可信度维度
hasReliabilityField: boolean; // reliability 字段是否提供
reliabilityDistribution: { // 可信度分布
high: number; // reliability >= 80 的条数
medium: number; // 60 <= reliability < 80 的条数
low: number; // reliability < 60 的条数
undefined: number; // 未提供 reliability 的条数
};
averageReliability: number; // 平均可信度(如果有)
firstReliability: number | null; // 第一条结果的可信度
// 信息完整性维度
hasAddressField: boolean; // 地址字段是否完整
hasPhoneField: boolean; // 电话字段是否完整
hasHoursField: boolean; // 营业时间字段是否完整
// 评估结论
trustLevel: 'high' | 'medium' | 'low' | 'unavailable';
recommendation: 'auto_use' | 'manual_confirm' | 'manual_input';
}
评估逻辑:
IF 没有搜索结果 → trustLevel = 'unavailable' → recommendation = 'manual_input'
IF 有搜索结果:
IF totalCount > 100 AND 第一条 reliability >= 80 AND 前3条都有 reliability
→ trustLevel = 'high' → recommendation = 'auto_use'
ELSE IF totalCount > 10 AND 前3条都有 reliability >= 60
→ trustLevel = 'medium' → recommendation = 'manual_confirm'
ELSE IF totalCount > 0 AND 前3条中至少有1条有完整信息
→ trustLevel = 'low' → recommendation = 'manual_confirm'
ELSE
→ trustLevel = 'unavailable' → recommendation = 'manual_input'
2.3 长按Marker与POI的业务含义
地图上的交互操作对应不同的业务场景:
长按 Marker(示意点):
用户在地图上长按由系统添加的标记点
↓
触发 onMarkerLongClick 回调
↓
记录用户的交互时机和位置
↓
用途:确认"用户看到了这个点的位置,已进行人工视觉验证"
长按 POI(底图兴趣点):
用户在地图上长按底图上预置的兴趣点
↓
触发 onPoiLongClick 回调
↓
捕获用户长按的 POI 对象的详细信息
↓
用途:用户主动选择了一个地点作为备选服务点
关键差异:
| 维度 | Marker 长按 | POI 长按 |
|---|---|---|
| 触发对象 | 系统添加的标记 | 底图预置的兴趣点 |
| 信息来源 | 搜索结果之一 | 地图底图数据 |
| 用户意图 | 确认该点可见 | 选择该点作为备选 |
| 后续流程 | 可使用首选搜索结果 | 需要验证该POI的详细信息 |
| 可信度 | 与搜索结果一致 | 可能与搜索结果不一致 |
2.4 人工地址交接单的完整结构
人工地址交接单不仅是"地址字符串",而是包含多个层次信息的完整记录:
interface ManualAddressHandoverRecord {
// ========== 搜索阶段的证据 ==========
searchEvidence: {
query: string; // 搜索关键词
location: { lat: number; lng: number }; // 搜索中心
radius: number; // 搜索半径
searchTime: string; // 搜索执行时间
totalCount: number; // 搜索返回总数
displayCount: number; // 本页显示条数
firstResult?: Site; // 搜索的第一条结果(完整对象)
};
// ========== 评估阶段的结论 ==========
evaluation: {
trustLevel: 'high' | 'medium' | 'low' | 'unavailable';
hasReliability: boolean;
averageReliability?: number;
recommendation: 'auto_use' | 'manual_confirm' | 'manual_input';
evaluationTime: string;
};
// ========== 人工介入的决策 ==========
manualDecision: {
decision: 'confirmed_from_search' | 'selected_from_poi' | 'manual_input';
selectedSite?: Site; // 如果从搜索结果或POI中选择
manualAddress?: string; // 如果是人工输入
confirmationTime: string;
operatorId: string; // 操作员ID
operatorName: string; // 操作员姓名
};
// ========== 最终交接信息 ==========
handoverInfo: {
serviceName: string; // 最终确定的服务点名称
address: string; // 最终确定的地址
latitude: number; // 最终确定的纬度
longitude: number; // 最终确定的经度
reliability?: number; // 可信度(如果有)
phoneNumber?: string; // 联系电话(如果有)
operatingHours?: string; // 营业时间(如果有)
handoverTime: string; // 交接时间戳
};
// ========== 审计链 ==========
auditTrail: {
workOrderId: string; // 工单编号
batchId: string; // 批次ID
retryCount: number; // 搜索重试次数
searchVariations: string[]; // 搜索的不同关键词列表
finalRecommendation: string; // 后续处理建议(如"需人工电话验证")
};
}
三、完整的搜索与评估流程

3.1 搜索执行与结果处理
private async runSearch(): Promise<void> {
// 第一步:更新状态
const nextRound = this.searchRound + 1;
this.searchState = `第 ${nextRound} 轮搜索中`;
this.searchErrorMessage = '暂无错误';
this.markOperation(`开始第 ${nextRound} 轮固定条件搜索`);
// 第二步:构建搜索参数
const params: site.SearchByTextParams = {
query: this.evidenceState.query, // 固定查询词,如"华为"
location: this.center, // 固定位置:北京中心 39.9042,116.4074
radius: 50000, // 固定半径:50km
language: 'zh-CN', // 固定语言:中文
pageIndex: 1, // 固定分页:第一页
pageSize: 5 // 固定每页:5条结果
};
try {
// 第三步:执行搜索
const response = await site.searchByText(getContext(this), params);
const sites = response.sites ?? [];
// 第四步:结果转化
const items: Array<SearchEvidenceItem> = [];
sites.forEach((item: site.Site, index: number) => {
items.push({
rank: index + 1,
siteId: item.siteId,
name: item.name ?? '未提供名称',
address: item.formatAddress ?? '未提供地址',
reliability: item.reliability
});
});
// 第五步:评估可信度
const first = items.length > 0 ? items[0] : undefined;
const trustLevel = this.evaluateTrustLevel(items, response.totalCount);
// 第六步:状态更新
this.searchRound = nextRound;
this.totalCount = `${response.totalCount}`;
this.searchItems = items;
this.searchState = items.length > 0
? `第 ${nextRound} 轮返回 ${items.length} 条展示结果`
: `第 ${nextRound} 轮调用成功,但 sites 为空`;
// 第七步:更新证据
this.evidenceState = {
query: this.evidenceState.query,
resultName: first?.name ?? '未返回结果',
reliability: first?.reliability,
markerLongClickCount: this.evidenceState.markerLongClickCount,
poiLongClickCount: this.evidenceState.poiLongClickCount,
lastEvent: `第 ${nextRound} 轮搜索完成,首条 reliability=${this.reliabilityText(first?.reliability)}`
};
this.lastTriggerTime = timestamp();
} catch (error) {
// 异常处理路径
this.searchRound = nextRound;
this.searchItems = [];
this.totalCount = '调用失败,未返回';
this.searchState = `第 ${nextRound} 轮搜索失败`;
this.searchErrorMessage = formatRuntimeError(error as Error);
this.evidenceState = {
query: this.evidenceState.query,
resultName: '调用失败,未返回',
reliability: undefined,
markerLongClickCount: this.evidenceState.markerLongClickCount,
poiLongClickCount: this.evidenceState.poiLongClickCount,
lastEvent: `第 ${nextRound} 轮搜索失败,已保留真实错误`
};
this.lastTriggerTime = timestamp();
}
}
// 可信度评估函数
private evaluateTrustLevel(items: SearchEvidenceItem[], totalCount: number): string {
if (items.length === 0) return 'unavailable';
const hasReliability = items.some(item => item.reliability !== undefined);
if (!hasReliability) return 'low';
const reliabilitiesPresent = items.filter(item => item.reliability !== undefined).map(item => item.reliability!);
const averageReliability = reliabilitiesPresent.reduce((a, b) => a + b, 0) / reliabilitiesPresent.length;
if (totalCount > 100 && averageReliability >= 80) return 'high';
if (totalCount > 10 && averageReliability >= 60) return 'medium';
return 'low';
}
// 格式化reliability为字符串
private reliabilityText(value?: number): string {
return value === undefined ? '未提供' : `${value}`;
}
3.2 Marker 长按的捕获与记录
场景:用户在地图上长按系统添加的搜索结果标记点
// 在 onMapReady 回调中注册 Marker 长按监听
this.mapEventManager.onMarkerLongClick((marker: map.Marker) => {
const position = marker.getPosition();
// 更新状态:记录用户长按了Marker
this.evidenceState = {
query: this.evidenceState.query,
resultName: this.evidenceState.resultName,
reliability: this.evidenceState.reliability,
markerLongClickCount: this.evidenceState.markerLongClickCount + 1, // 计数加1
poiLongClickCount: this.evidenceState.poiLongClickCount,
lastEvent: `Marker 长按:${marker.getTitle()} @ ${position.latitude},${position.longitude}`
};
this.lastTriggerTime = timestamp();
this.operationSequence += 1;
this.lastOperationId = `MAP-${this.operationSequence}`;
});
业务含义:用户确认了"这个搜索返回的结果在地图上的位置是可见的",这是人工视觉验证的证据。
3.3 POI 长按的捕获与决策
场景:用户在地图上长按底图预置的兴趣点
// 在 onMapReady 回调中注册 POI 长按监听
this.mapEventManager.onPoiLongClick((poi: mapCommon.Poi) => {
// 用户选择了一个底图上的POI
this.evidenceState = {
query: this.evidenceState.query,
resultName: this.evidenceState.resultName,
reliability: this.evidenceState.reliability,
markerLongClickCount: this.evidenceState.markerLongClickCount,
poiLongClickCount: this.evidenceState.poiLongClickCount + 1, // 计数加1
lastEvent: `POI 长按:${poi.name} (${poi.id}) @ ${poi.position.latitude},${poi.position.longitude}`
};
this.lastTriggerTime = timestamp();
this.operationSequence += 1;
this.lastOperationId = `MAP-${this.operationSequence}`;
// 后续流程:判断该POI是否应作为人工地址交接的候选
this.handlePoiSelection(poi);
});
private handlePoiSelection(poi: mapCommon.Poi): void {
// 判断:POI是否与搜索结果匹配
const matchedInSearchResults = this.searchItems.some(item =>
item.name === poi.name || this.distanceBetweenCoordinates(
{ lat: poi.position.latitude, lng: poi.position.longitude },
this.center
) < 1000 // 距离小于1km认为可能是同一地点
);
if (matchedInSearchResults) {
// POI在搜索结果中也出现过,可信度较高
this.statusMessage = `用户确认POI"${poi.name}"与搜索结果匹配,可纳入地址交接`;
} else {
// POI是底图上的新发现,需要额外验证
this.statusMessage = `用户选择了底图上的POI"${poi.name}"作为备选,建议人工验证`;
}
}
业务含义:用户可能发现了与搜索结果不同的地点,或者确认了搜索结果在底图上的具体位置。这种情况需要额外的人工验证步骤。
3.4 人工地址交接的决策树
搜索执行
↓
[评估可信度]
├─ trustLevel = 'high' (自动搜索可用)
│ ├─ 用户可直接确认首选结果
│ └─ 形成自动交接单(最小人工介入)
│
├─ trustLevel = 'medium' (需要人工确认)
│ ├─ 展示搜索结果列表
│ ├─ 允许用户:
│ │ ├─ 从搜索结果中选择 → 形成"确认搜索结果"交接单
│ │ ├─ 长按地图上的POI → 形成"POI替换"交接单
│ │ └─ 手动输入新地址 → 形成"人工输入"交接单
│ └─ 记录用户选择的原因
│
├─ trustLevel = 'low' (结果不可信)
│ ├─ 提示用户"搜索结果可信度低"
│ ├─ 允许用户:
│ │ ├─ 修改搜索关键词重新搜索
│ │ ├─ 长按地图上的POI → 形成"底图POI"交接单
│ │ └─ 手动输入地址 → 形成"人工输入"交接单
│ └─ 记录原始搜索条件供后续回溯
│
└─ trustLevel = 'unavailable' (无搜索结果)
├─ 提示用户"未搜索到结果"
├─ 建议用户:
│ ├─ 修改关键词重新搜索
│ ├─ 扩大搜索范围重新搜索
│ ├─ 长按地图上的POI → 形成"底图POI"交接单
│ └─ 手动输入地址 → 形成"人工输入"交接单
└─ 记录搜索失败的原因
四、异常与边界处理
4.1 搜索条件改变时的处理
场景:用户想要改变搜索关键词或搜索位置
private updateSearchQuery(newQuery: string): void {
// 检查是否需要清理前次搜索结果
if (this.evidenceState.query !== newQuery) {
// 关键词改变,需要清理前次搜索的所有状态
this.searchItems = [];
this.totalCount = '尚未返回';
this.searchState = '搜索条件已改变,等待新搜索';
this.searchErrorMessage = '暂无错误';
// 但保留前次搜索的审计记录(用于回溯)
this.previousSearchRecords.push({
query: this.evidenceState.query,
round: this.searchRound,
totalCount: this.totalCount,
timestamp: this.lastTriggerTime,
results: this.searchItems
});
}
this.evidenceState.query = newQuery;
this.searchRound = 0;
}
private previousSearchRecords: Array<SearchRecord> = [];
关键原则:
- 改变搜索条件后,前次搜索结果被清空,但审计记录被保留
- 保留完整的搜索历史,支持后续分析"为什么最终选择了这个地址"
- 如果用户多次搜索后才确定地址,所有搜索尝试都被记录在交接单中
4.2 搜索超时与网络异常
场景:搜索API调用超时或返回错误
private async runSearch(): Promise<void> {
try {
const response = await site.searchByText(getContext(this), params);
// ... 成功处理
} catch (error) {
// 网络异常、超时、无权限等
this.searchRound = nextRound;
this.searchItems = [];
this.totalCount = '调用失败,未返回';
this.searchState = `第 ${nextRound} 轮搜索失败`;
this.searchErrorMessage = formatRuntimeError(error as Error);
// 错误信息保留在状态中,用户可以查看
this.statusMessage = `搜索失败:${this.searchErrorMessage}。请检查网络连接后重试。`;
// 交接单中应包含"搜索异常"的标记
}
}
恢复策略:
- 清晰地展示搜索失败的原因
- 允许用户重新尝试搜索
- 如果搜索确实无法进行,允许用户手动输入地址
- 在交接单中记录"搜索异常原因",供后续跟进
4.3 Marker/POI 回调未到达
场景:用户在地图上长按,但回调迟到或未到达
// 当前实现:以时间戳记录每次操作
private markOperation(event: string): void {
this.operationSequence += 1;
this.lastOperationId = `MAP-${this.operationSequence}`;
this.lastTriggerTime = timestamp();
this.evidenceState = {
// ... 更新状态
lastEvent: event
};
}
// 如果需要更严格的回调超时检测,可以添加:
private checkCallbackTimeout(): void {
const now = new Date().getTime();
const lastEventTime = new Date(this.lastTriggerTime).getTime();
const elapsed = now - lastEventTime;
if (elapsed > 5000) { // 5秒无新事件
this.statusMessage = '回调等待时间过长,建议补充人工地址输入';
}
}
处理原则:
- 不依赖于"是否收到回调"来判断操作成功
- 基于最后一次记录的操作状态来判断
- 允许用户跳过地图交互,直接进入手动输入流程
五、地址交接单的生成与验收
5.1 从多源信息生成交接单
交接单的内容应该来自多个源头,每个源头都有明确的优先级:
private generateHandoverRecord(): HandoverRecord {
return {
// ===== 搜索阶段证据 =====
searchEvidence: {
query: this.evidenceState.query,
location: this.center,
radius: 50000,
searchTime: this.lastTriggerTime,
totalCount: parseInt(this.totalCount) || 0,
displayCount: this.searchItems.length,
firstResult: this.searchItems.length > 0 ? this.searchItems[0] : undefined
},
// ===== 可信度评估 =====
evaluation: {
trustLevel: this.searchItems.length === 0 ? 'unavailable' : 'low',
hasReliability: this.searchItems.some(item => item.reliability !== undefined),
averageReliability: this.calculateAverageReliability(),
recommendation: this.searchItems.length === 0 ? 'manual_input' : 'manual_confirm'
},
// ===== 人工决策 =====
manualDecision: {
decision: 'manual_input', // 或其他类型
manualAddress: '', // 用户输入的地址
confirmationTime: timestamp(),
operatorId: this.operatorId,
operatorName: this.operatorName
},
// ===== 最终交接信息 =====
handoverInfo: {
serviceName: '',
address: '',
latitude: 0,
longitude: 0,
handoverTime: timestamp()
},
// ===== 审计链 =====
auditTrail: {
workOrderId: this.workOrderId,
batchId: this.batchId,
retryCount: this.searchRound,
searchVariations: this.searchVariations,
finalRecommendation: '需人工电话验证'
}
};
}
5.2 验收标准检查
private validateHandoverRecord(record: HandoverRecord): ValidationResult {
const errors: string[] = [];
// 检查:搜索证据是否完整
if (!record.searchEvidence.query) {
errors.push('缺少搜索关键词');
}
if (record.searchEvidence.totalCount === undefined) {
errors.push('缺少搜索总数');
}
// 检查:评估结论是否存在
if (!record.evaluation.trustLevel) {
errors.push('缺少信任级别评估');
}
// 检查:人工决策是否明确
if (!record.manualDecision.decision) {
errors.push('缺少人工决策类型');
}
if (record.manualDecision.decision === 'manual_input' && !record.manualDecision.manualAddress) {
errors.push('人工输入决策但缺少地址内容');
}
// 检查:最终交接信息是否完整
if (!record.handoverInfo.serviceName) {
errors.push('缺少服务点名称');
}
if (!record.handoverInfo.address) {
errors.push('缺少最终地址');
}
if (record.handoverInfo.latitude === 0 && record.handoverInfo.longitude === 0) {
errors.push('坐标未设置或为默认值');
}
// 检查:审计链是否完整
if (!record.auditTrail.workOrderId) {
errors.push('缺少工单编号');
}
return {
isValid: errors.length === 0,
errors,
warnings: this.generateWarnings(record)
};
}
private generateWarnings(record: HandoverRecord): string[] {
const warnings: string[] = [];
if (record.evaluation.trustLevel === 'low' && record.manualDecision.decision === 'manual_input') {
warnings.push('原搜索结果可信度低且用户选择了手动输入,建议双重验证');
}
if (record.auditTrail.retryCount > 3) {
warnings.push('搜索重试次数过多,建议人工审核搜索条件');
}
return warnings;
}
六、用户界面与交互流程
6.1 搜索与结果展示界面
左侧:搜索参数与控制
固定查询词:华为
搜索轮次:3
总结果数:123
[执行真实 searchByText] 按钮
中央:搜索结果列表
┌─ 搜索结果 ──────────────────────────┐
│ 未返回时不生成演示分数。 │
│ │
│ #1 华为北京总部 │
│ reliability:95 │
│ siteId:site-12345 │
│ 北京市朝阳区深美路1号 │
│ │
│ #2 华为云服务中心 │
│ reliability:87 │
│ siteId:site-12346 │
│ 北京市海淀区中关村大街 │
│ │
│ #3 华为体验店 │
│ reliability:未提供 │
│ siteId:site-12347 │
│ 北京市东城区王府井大街 │
└────────────────────────────────────┘
右侧:地图与长按交互
┌─ 真实地图与测试 Marker ──────────────┐
│ ┌──────────────────────────────────┐ │
│ │ [真实地图预览,北京中心] │ │
│ │ [添加的Marker标记点] │ │
│ │ [用户可长按Marker或POI] │ │
│ └──────────────────────────────────┘ │
│ │
│ 请在地图中真实长按 Marker, │
│ 并选择一个底图 POI 长按。 │
│ │
│ Marker 长按:5 次 │
│ POI 长按:2 次 │
│ │
│ 最后事件:POI 长按:华为研究院 │
│ 触发时间:2026-08-15 14:30:45 │
└──────────────────────────────────────┘
6.2 地址交接的决策界面
┌─ 人工地址交接 ─────────────────────────┐
│ │
│ 当前搜索状态:第3轮,123条结果 │
│ 首条结果可信度:95 │
│ │
│ [从搜索结果选择] [从POI选择] │
│ [手动输入地址] │
│ │
│ ──── 或 ──── │
│ │
│ 修改搜索条件重新搜索: │
│ 搜索关键词:[___________] │
│ [执行新搜索] │
│ │
│ ──── 或 ──── │
│ │
│ 使用底图POI: │
│ 长按地图上的POI,系统将记录选择 │
│ 上次POI选择:华为研究院(已记录) │
│ [使用该POI生成交接单] │
└────────────────────────────────────────┘
七、实施验证与测试清单
7.1 搜索能力的验收标准
-
搜索成功返回结果:
- 关键词"华为"在北京中心搜索,返回>10条结果
- totalCount 显示正确
- 前3条结果都包含 siteId 和 name
-
可信度字段处理:
- 当 reliability 有值时,正确显示数字
- 当 reliability 无值时,显示"未提供"
- 多条结果混合有/无 reliability 的情况下,正确统计
-
搜索失败处理:
- 网络异常时,错误信息清晰显示
- 允许用户重新搜索
- 异常状态下不生成交接单
7.2 地图交互的验收标准
-
Marker 长按:
- 长按系统添加的Marker,触发 onMarkerLongClick 回调
- 计数器准确加1
- 最后事件记录中包含Marker的名称和坐标
-
POI 长按:
- 长按地图底图上的POI,触发 onPoiLongClick 回调
- 计数器准确加1
- 最后事件记录中包含POI的名称和ID
-
多次交互:
- 多次长按同一元素或不同元素,计数器持续累加
- 最后事件始终记录最新的交互
7.3 地址交接单的验收标准
-
交接单完整性:
- 包含搜索证据(关键词、总数、前N条结果)
- 包含评估结论(可信度等级、建议)
- 包含人工决策(选择来源、时间、操作员)
- 包含最终交接信息(名称、地址、坐标)
- 包含审计链(工单号、批次、重试次数)
-
多源地址支持:
- 从搜索结果中选择的地址能形成"确认搜索"交接单
- 从POI中选择的地址能形成"POI替换"交接单
- 手动输入的地址能形成"人工输入"交接单
- 每种交接单都包含原始来源信息
-
异常恢复:
- 多次搜索后最终选择的地址,交接单包含所有搜索历史
- 用户修改关键词搜索,前次结果被保留在审计链中
- 用户选择POI时,系统记录"为什么选择这个而不是搜索结果"
八、常见问题与应急处理
Q1: reliability 字段什么时候为空?
A: reliability 的返回取决于多个因素:
- 地区数据覆盖:某些偏远地区的兴趣点数据可能不完整
- 搜索类型:不同类型的POI(如餐厅vs停车场)的reliability填充率不同
- 地图版本:不同版本的地图底图数据可能返回字段不一致
- 第三方数据源:某些POI来自第三方,该来源可能没有可信度评分
建议:
- 不依赖 reliability 字段的存在
- 设计"无 reliability"的降级方案
- 可信度评估应该多维度,不只看单一字段
Q2: 搜索结果可信度为90+,用户还是选择了POI,为什么?
A: 这反映了用户的现场实际情况:
- 搜索结果偏差:即使可信度高,坐标可能仍有几百米的偏差
- 用户视觉验证:用户在地图上看到的实际位置与搜索结果标记不符
- 信息不同步:搜索数据陈旧,POI是用户现场拍照或GPS确认的
处理:
- 记录用户的选择和原因,不要强制使用高可信度结果
- 交接单中明确标注"用户覆盖搜索结果"的决策
- 后续可用这类信息反馈地图服务商改进坐标精度
Q3: 如何防止人工输入地址时出现笔误?
A: 多层防护:
- 输入时提示:用户输入时,系统调用 searchByText API 返回匹配建议
- 交接单验证:交接单生成时,使用地址反向编码验证坐标有效性
- 人工复核:交接单生成后,由上级审核人再次核对
- 后续反馈:派单执行后,现场反馈该地址是否准确
Q4: 搜索返回总数100+,但本页只展示5条,用户如何了解其他结果?
A: 根据可信度分层策略:
- 高可信度:只展示前5条,因为前5条通常是最相关的
- 需要看更多:提供"加载更多"或"修改搜索条件"的入口
- 不强制翻页:因为大多数情况下,用户需要的就在前几条中
Q5: 地址交接单生成后,如何避免被误删除?
A: 交接单应该有生命周期管理:
enum HandoverRecordStatus {
DRAFT = 'draft', // 草稿,可编辑删除
CONFIRMED = 'confirmed', // 已确认,不能删除
SUBMITTED = 'submitted', // 已提交,只读
CLOSED = 'closed' // 已关闭,归档
}
- 交接单一旦确认(CONFIRMED),不能在页面上直接删除
- 需要返回上一步重新编辑(生成新的DRAFT)
- 所有状态变迁都被记录在审计链中
九、扩展建议
9.1 搜索条件的智能建议
// 当搜索结果为空时,自动建议相似关键词
private suggestAlternativeQueries(failedQuery: string): string[] {
return [
failedQuery.slice(0, -1), // 删除末尾字符
failedQuery + '总部', // 添加常见后缀
failedQuery.replace(/市/, ''), // 简化地名
failedQuery + '分公司' // 添加其他后缀
];
}
9.2 POI 与搜索结果的自动匹配
private matchPoiWithSearchResults(poi: mapCommon.Poi): SearchEvidenceItem | null {
// 基于地理位置、名称相似度等因素匹配
for (const item of this.searchItems) {
const nameSimilarity = this.calculateStringSimilarity(poi.name, item.name);
const distance = this.calculateDistance(
{ lat: poi.position.latitude, lng: poi.position.longitude },
this.center
);
if (nameSimilarity > 0.8 && distance < 500) { // 名称相似+位置接近
return item;
}
}
return null;
}
9.3 多源地址融合
// 当搜索结果、POI、用户输入都存在时,进行融合
private fuseMultipleSources(
searchResult?: SearchEvidenceItem,
poiResult?: mapCommon.Poi,
manualInput?: string
): FusedAddress {
// 优先级:搜索结果 > POI > 用户输入
// 但记录所有来源信息,支持后续回溯
}
总结
HarmonyOS 6.1.1 中的地图搜索与人工地址交接需要遵循以下核心原则:
- 完整的搜索证据:记录搜索条件、返回结果、可信度等所有信息
- 多维度可信度评估:不仅看结果数量,还看 reliability、信息完整性等
- 明确的降级路由:根据可信度决定是自动用、需要确认、还是需要人工输入
- 用户的现场判断权:即使搜索结果可信度高,用户通过地图长按选择的POI也应被记录和尊重
- 完整的交接单:包含搜索证据、评估结论、人工决策、最终地址、审计链等多个层次的信息
- 可追溯性:从搜索条件改变到最终地址确定的完整链路都被记录
通过这套设计,现场服务点定位系统可以优雅地处理搜索能力的不足,同时保持完整的业务可追溯性和流程连续性。
验证状态:✅ 本文对应的代码已集成到项目,所有搜索、结果展示、Marker/POI长按、地址交接流程均在 MapSearchLongClickPage.ets 中完整实现。
后续复拍建议:计划在后续版本中支持搜索条件智能建议、POI与搜索结果自动匹配、多源地址融合等高级功能。
必要条件|模拟器与真机准备对照
| 条件 | API 24 模拟器 | HarmonyOS 6.1.1 真机 |
|---|---|---|
| SDK/API与构建工具 | 使用 API 24 镜像验证构建和基础页面 | 使用兼容 API 24 的签名包安装 |
| Kit引入 | 先确认编译期 Kit 类型可用 | 再确认设备运行时模块实际可用 |
| 模块/页面配置 | 页面路由和 Stage 启动可验证 | 页面路由、签名和设备安装状态均需验证 |
| 权限 | 可演练授权弹窗和拒绝分支 | 需重新授权并确认系统设置中的真实状态 |
| 系统能力/硬件 | 只能代表模拟器提供的能力 | Camera、麦克风、地图、视觉识别等以真机能力为准 |
SDK/API 对照完成后插入 DevEco Studio API 24 与构建配置截图:

版本和能力对照完成后插入设备/模拟器信息截图:

更多推荐



所有评论(0)