关于 add-geolocation
用于通过 `@microsoft/power-apps-native-bglocation` 为原生应用添加持续或后台 GPS 跟踪的内部技能。它会验证 Dataverse 目标表及字段,再创建或更新 TypeScript 封装,不修改原生配置。
主要功能
- 01检查所需依赖与应用设置
- 02验证 Dataverse 表和映射字段
- 03创建或更新地理位置封装
- 04要求使用 MSAL 身份验证
- 05对变更执行类型检查
适用场景
- 01为原生应用添加后台 GPS 跟踪
- 02集成前验证位置上传目标
- 03复用或更新现有地理位置封装
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ microsoft/ power-platform-skills --skill add-geolocation安装后在 Claude Code 中输入/add-geolocation确认已加载。
详细介绍
共享说明:shared-instructions.md - 请先阅读。
工作目录: 在读取任何项目内容或执行任何命令之前,执行
native-artifact-compatibility.md Step 0。
继承 /add-native 解析出的绝对 working_dir;将每个 shell 调用和文件工具绑定到该目录,即使此辅助流程从其他目录启动也不例外。
添加地理位置
用于 /add-native geolocation、/add-native location-tracking、/add-native background-location、/add-native gps-tracking 和 /add-native @microsoft/power-apps-native-bglocation 的内部辅助流程。
仅将此流程用于通过原生持久化存储和内联 Dataverse 上传实现的连续/后台 GPS 跟踪。若只需读取一次前台坐标,请改用 /add-native location(expo-location)。
硬性规则:
- 认证只能使用 MSAL。不得公开 OneAuth 或认证方式选择器。
- 使用
geoService(dataSource, app_id)和BgLocationClient。 - 不得使用
GeolocationExtension、HostingSDK、PCF、Launch URI 或 CordovaV2 桥接路径。 - 不得安装软件包或修改原生配置。
- 只有在验证 Dataverse 目标表及映射列之后,才能使用此控件。
1. 验证应用和软件包
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
test -f app.config.js && test -f power.config.json && test -f package.json && test -d src || { echo "BLOCKED: working_dir is not an initialized app" >&2; exit 1; }
node -e "const p=require('./package.json'); const m='@microsoft/power-apps-native-bglocation'; if (!p.dependencies?.[m]) { console.error('MISSING: ' + m); process.exit(1); } console.log('OK: geolocation package present');"
如果任一检查失败,请停止。模板/应用必须已包含原生软件包。
2. 首先验证 Dataverse 目标表
此控件使用软件包默认的 Dataverse 实体集:
msdyn_locationrecords
不得询问用户表名,也不得自创自定义表。使用控件前,msdyn_locationrecords 必须已存在。
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
ENV_JSON=$(node "${PLUGIN_ROOT}/scripts/resolve-environment.js" "$(node -e "console.log(require('./power.config.json').environmentId)")")
ENV_URL=$(node -e "const j=JSON.parse(process.argv[1]); process.stdout.write(j.environmentUrl || '')" "$ENV_JSON")
TABLE="msdyn_locationrecords"
node "${PLUGIN_ROOT}/scripts/dataverse-request.js" "$ENV_URL" GET \
"EntityDefinitions?\$filter=EntitySetName eq '$TABLE'&\$select=LogicalName,EntitySetName"
所需结果:
- 返回一行:记录
LogicalName并验证列。 value: []为空:停止。标记为BLOCKED (target table missing)。不得创建表、不得转到/add-dataverse,也不得报告控件可用。请告知用户运行地理位置控件的表预配/设置机制,然后重新运行/add-native geolocation。- 认证/环境错误:停止。标记为
UNVERIFIED (target table not checked)。不得报告控件可用。
表存在时,验证每个映射列均存在:
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
node "${PLUGIN_ROOT}/scripts/dataverse-request.js" "$ENV_URL" GET \
"EntityDefinitions(LogicalName='<logicalName>')/Attributes?\$select=LogicalName,AttributeType"
软件包 README 中的默认 fieldMap 必需列:
msdyn_locationrecordid, msdyn_appid, msdyn_latitude, msdyn_longitude,
msdyn_altitude, msdyn_accuracy, msdyn_heading, msdyn_speed, msdyn_timestamp
如果任何启用的 fieldMap 列缺失,请停止。标记为 BLOCKED (target columns missing),并告知用户通过地理位置控件的表预配/设置机制修复控件表。
2a. 对照请求的制品
在执行任何写入或复用操作之前,针对地理位置对应项阅读并执行 native-artifact-compatibility.md
的步骤 1–3。继承 /add-native 当前的审批/模式;不得重复请求有效且范围明确的审批。
检查 src/native/geolocation.ts,确认其中包含所有请求的跟踪/状态/位置导出、GeoTrackingTarget、仅限 MSAL 的 geoService/BgLocationClient、必需的 connectionUrl/trackInBackground/persistAcrossRestarts、持久化存储和 Dataverse 同步。复用步骤 2 中已验证的 msdyn_locationrecords 列;单次定位或目标为其他表的封装器均不兼容。审批范围之外的不兼容制品应将 NEEDS_CONTEXT 返回给负责人,或在独立运行时询问;缺失表/列仍标记为 BLOCKED,绝不能报告控件可用。
3. 编写 src/native/geolocation.ts
根据步骤 2a 对 src/native/geolocation.ts 的决定执行:收到请求且文件缺失时创建;仅在兼容时原样复用;或只进行获批的范围内更新。保留自定义代码、导出和调用方;不得用示例覆盖。屏幕应导入此封装器,而不是直接导入软件包。
目标配置必须要求 README 中的两个跟踪标志:
trackInBackground: booleanpersistAcrossRestarts: boolean
// src/native/geolocation.ts
import {
geoService,
BgLocationClient,
AuthMethod,
ConnectionType,
} from '@microsoft/power-apps-native-bglocation';
import type {
DataverseDataSource,
LocationData,
PermissionStatus,
} from '@microsoft/power-apps-native-bglocation';
export type { LocationData, PermissionStatus } from '@microsoft/power-apps-native-bglocation';
export type GeoTrackingTarget = Omit<DataverseDataSource, 'authMethod' | 'connectionType'>;
export type GeoResult<T> =
| { ok: true; value: T }
| {
ok: false;
reason: 'NATIVE_MODULE_MISSING' | 'PERMISSION_DENIED' | 'TRACKING_FAILED';
message?: string;
};
let activeClient: BgLocationClient | null = null;
function client(): BgLocationClient {
return activeClient ?? new BgLocationClient();
}
function dataSource(target: GeoTrackingTarget): DataverseDataSource {
return {
...target,
authMethod: AuthMethod.MSAL,
connectionType: ConnectionType.Dataverse,
};
}
function fail(error: unknown): Extract<GeoResult<never>, { ok: false }> {
const message = error instanceof Error ? error.message : String(error);
if (/native|module|not.*link|undefined is not an object|null is not an object/i.test(message)) {
return { ok: false, reason: 'NATIVE_MODULE_MISSING', message };
}
if (/permission|denied|authoriz|not granted/i.test(message)) {
return { ok: false, reason: 'PERMISSION_DENIED', message };
}
return { ok: false, reason: 'TRACKING_FAILED', message };
}
export async function startTracking(
app_id: string,
target: GeoTrackingTarget,
): Promise<GeoResult<void>> {
try {
activeClient = geoService(dataSource(target), app_id);
await activeClient.startTracking();
return { ok: true, value: undefined };
} catch (error) {
activeClient = null;
return fail(error);
}
}
export async function stopTracking(): Promise<GeoResult<void>> {
try {
await client().stopTracking();
activeClient = null;
return { ok: true, value: undefined };
} catch (error) {
return fail(error);
}
}
export async function isTracking(): Promise<GeoResult<boolean>> {
try {
return { ok: true, value: await client().isTracking() };
} catch (error) {
return fail(error);
}
}
export async function getPermissionStatus(): Promise<GeoResult<PermissionStatus>> {
try {
return { ok: true, value: await client().getPermissionStatus() };
} catch (error) {
return fail(error);
}
}
export async function getCurrentLocation(): Promise<GeoResult<LocationData>> {
try {
return { ok: true, value: await client().getCurrentLocation() };
} catch (error) {
return fail(error);
}
}
4. 使用方式
面向所有者的集成指南;请勿在此辅助工具中编辑屏幕。
屏幕必须传入 connectionUrl、trackInBackground 和 persistAcrossRestarts。请勿传入 tableName 或 fieldMap;该控件使用默认的 msdyn_locationrecords 表和默认的 msdyn_* 字段映射。intervalMs、distanceFilterMeters 和 notification 为可选项。
const target: GeoTrackingTarget = {
connectionUrl: 'https://org.crm.dynamics.com',
trackInBackground: true,
persistAcrossRestarts: false,
};
await startTracking('my-wrap-app', target);
5. 类型检查和摘要
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
npx --no-install tsc --noEmit
完成类型检查后,执行共享兼容性契约的步骤 4。重新检查所有请求的跟踪 API/标志、MSAL 身份验证、持久化存储/同步和失败路径。仅类型检查成功并不足够;不要修复屏幕/生成的文件。只有在请求的工件契约、表 + 列以及 TypeScript 全部通过后,才返回共享兼容性结果,以及实际创建/更新/复用的路径,然后再报告:
Geolocation status : READY
Package present : @microsoft/power-apps-native-bglocation
Wrapper : src/native/geolocation.ts
Required config : connectionUrl, trackInBackground, persistAcrossRestarts
Auth : MSAL only
Target table : <TABLE> verified
Native config : not changed
如果表验证失败,请改为报告 BLOCKED 或 UNVERIFIED,不要将 memory-bank.md 更新为表示该控件已就绪。