From 26299c06ce9c89315f46199e62a414dbb42b8f32 Mon Sep 17 00:00:00 2001 From: lanterngamescn Date: Thu, 25 Jun 2026 07:09:17 +0800 Subject: [PATCH] =?UTF-8?q?M0:=20contracts=20=E5=A5=91=E7=BA=A6=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=20SSOT=EF=BC=88T-M0-04=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 落地《契约规范》§8/§9/§12,全工程唯一来源: - InboundHandlers 枚举:45 入站 handler 名,原样拼写(finsh/canclevibrator/opensaoma 等) - OutboundHandlers 枚举:21 出站 handler 名,含命名陷阱(getphoneinfo≠getphoneInfo、 getBattery≠getbattery、backgameData-末尾连字符) - PayloadType:入站/出站 raw|json 标注表 + 入站同步返回类型 + 查询函数 - DTO interface ×17(§12 字段同名同义):Sharetype/Maplocation/PhoneInfo/savephoto/ videoinfo/Othervideoinfo/GamePay/PayState/ShareLogin/ShareSuccess 等 - Result + Errors(ErrorKind/LocationErrorCode/AppError) - 经 entry 临时 smoke 全量引用,CompileArkTS 编译通过 Co-Authored-By: Claude Opus 4.8 (1M context) --- contracts/Index.ets | 23 ++- contracts/src/main/ets/dto/Dtos.ets | 174 ++++++++++++++++++ .../src/main/ets/handlers/InboundHandlers.ets | 122 ++++++++++++ .../main/ets/handlers/OutboundHandlers.ets | 57 ++++++ .../src/main/ets/handlers/PayloadType.ets | 112 +++++++++++ contracts/src/main/ets/result/Errors.ets | 47 +++++ contracts/src/main/ets/result/Result.ets | 35 ++++ 7 files changed, 567 insertions(+), 3 deletions(-) create mode 100644 contracts/src/main/ets/dto/Dtos.ets create mode 100644 contracts/src/main/ets/handlers/InboundHandlers.ets create mode 100644 contracts/src/main/ets/handlers/OutboundHandlers.ets create mode 100644 contracts/src/main/ets/handlers/PayloadType.ets create mode 100644 contracts/src/main/ets/result/Errors.ets create mode 100644 contracts/src/main/ets/result/Result.ets diff --git a/contracts/Index.ets b/contracts/Index.ets index c358aa8..b4e1d46 100644 --- a/contracts/Index.ets +++ b/contracts/Index.ets @@ -1,3 +1,20 @@ -// contracts HAR —— 契约类型 SSOT(导出入口 SSOT) -// 各子模块实现完成后在此统一 export。占位常量确保 HAR 可编译。 -export const contracts_MODULE_VERSION: string = '1.0.0'; +// contracts HAR —— 契约类型 SSOT(全工程唯一来源) +// 落地《TSGame_原生与H5接口契约总规范》§8/§9/§12 的 handler 名、DTO、错误与返回类型。 + +// —— Handler 名(原样拼写,不可纠正)—— +export { InboundHandlers } from './src/main/ets/handlers/InboundHandlers'; +export { OutboundHandlers } from './src/main/ets/handlers/OutboundHandlers'; + +// —— 载荷类型标注(raw / json)—— +export { PayloadType, InboundPayload, InboundSyncReturn, OutboundPayload, + inboundPayloadType, outboundPayloadType } from './src/main/ets/handlers/PayloadType'; + +// —— 数据结构 DTO(§12,字段同名同义)—— +export { SharetypeBean, MaplocationInfo, PhoneInfoBean, SavephotoURLBean, + VideoinfoBean, OthervideoinfoBean, GamePayReq, PayStateResp, ShareLoginResp, + ShareSuccessResp, OpenUrlTitleDataReq, OpenApplyDownloadReq, SwitchOverGameReq, + MediaTypeAudioReq, SrcIsLoopReq, GetAudioUrlResp, WifiLevelResp } from './src/main/ets/dto/Dtos'; + +// —— 错误与统一返回 —— +export { LocationErrorCode, ErrorKind, AppError, makeError } from './src/main/ets/result/Errors'; +export { Result } from './src/main/ets/result/Result'; diff --git a/contracts/src/main/ets/dto/Dtos.ets b/contracts/src/main/ets/dto/Dtos.ets new file mode 100644 index 0000000..5c6a345 --- /dev/null +++ b/contracts/src/main/ets/dto/Dtos.ets @@ -0,0 +1,174 @@ +/** + * 数据结构汇总(《契约规范》§12)。 + * + * 🔴 字段名严格对应原 Android 代码,序列化时必须【同名同语义】,不可改名/改大小写。 + * 多数字段为 String(H5 历史习惯),数值型按契约标注。 + */ + +/** 分享入参(friendsSharetypeUrlToptitleDescript 的 data),全 String。§12 sharetypeBean */ +export interface SharetypeBean { + /** "1" 好友(弹面板) / "2" 朋友圈(直发微信) */ + sharefriend: string; + /** "1" 网页/文本 / "2" Canvas截图 / "3" 图片链接 / "4" 视频(抖音) */ + type: string; + /** 分享子类型 */ + sharetype: string; + /** 分享链接 或 图片地址 */ + webpageUrl: string; + title: string; + description: string; +} + +/** 定位回传(getlocationinfo 的 data)。§12 MaplocationInfo */ +export interface MaplocationInfo { + latitude: number; // double + longitude: number; // double + address: string; + country: string; + province: string; + city: string; + district: string; + street: string; + cityCode: string; + streetNum: string; + adCode: string; + aoiName: string; + accuracy: number; // float + locationType: number; // int + /** 0 成功 / -1 未就绪 / -2 权限拒绝 / 其他=底层错误码 */ + errorCode: number; + errorMsg: string; +} + +/** 手机信息回传(getphoneinfo 出站 data),全 String。§12 phoneInfoBean */ +export interface PhoneInfoBean { + PhoneVersion: string; + PhoneAdresseMAC: string; + PhoneModel: string; + PhoneDeviceBrand: string; + PhoneProvidersName: string; + PhoneIMEI: string; + PhoneIMSI: string; +} + +/** 图片下载数组元素(getphoto 回传数组)。§12 savephotoURLBean */ +export interface SavephotoURLBean { + pid: string; + photourl: string; +} + +/** 加入/创建房间入参(createRoom 的 data),全 String。§12 videoinfobean */ +export interface VideoinfoBean { + playerid: string; + roomid: string; + agentid: string; + gameid: string; + left: string; + top: string; + pmw: string; + pmh: string; + width: string; + height: string; +} + +/** 请求远端视频窗口入参(getVideoinfo 入站的 data)。§12 Othervideoinfo */ +export interface OthervideoinfoBean { + playerid: string; + left: string; + top: string; + width: string; + height: string; + pmw: string; + pmh: string; +} + +/** App 内微信支付入参(getGameplay 的 data)。§12 支付相关 */ +export interface GamePayReq { + price: string; + body: string; + typeplay: number; // int + type: string; + user: number; // int +} + +/** App 内微信支付结果回传(PayuserPaytypePaystate 出站 data)。§12 支付相关 */ +export interface PayStateResp { + user: string; + state: number; // int:1 成功 / 0 取消 + typeplay: string; + type: string; + data: string; +} + +/** 微信登录回传(sharelogin 出站 data),全 String。§12 sharelogin */ +export interface ShareLoginResp { + openid: string; + headimgurl: string; + nickname: string; + sex: string; + city: string; + province: string; + unionid: string; +} + +/** 分享结果回传(sharesuccess 出站 data)。§12 sharesuccess */ +export interface ShareSuccessResp { + /** 2 成功 / 3 取消 */ + success: number; + /** 1 好友 / 2 朋友圈 */ + type: number; +} + +/** 打开通用网页容器入参(OpenurlTitleData 的 data)。§8.8 / §11.2 */ +export interface OpenUrlTitleDataReq { + url: string; + title: string; + data: string; +} + +/** 按包名启动 App 入参(openApplyDownloadpath 的 data)。§8.8 */ +export interface OpenApplyDownloadReq { + packagename: string; + downloadpath: string; +} + +/** 容器内切换子游戏入参(SwitchOverGameData 的 data)。§11.1 */ +export interface SwitchOverGameReq { + /** "2" 竖屏 / "3" 横屏 */ + webtype: string; + /** 目标游戏目录名 */ + Gamedirectory: string; + /** 游戏 id(用于判断/下载) */ + gamedownloadurl: string; + /** 交换数据 */ + data: string; +} + +/** 播放语音消息入参(mediaTypeAudio 的 data)。§8.7 */ +export interface MediaTypeAudioReq { + audiourl: string; + type: string; + user: string; +} + +/** 播放游戏音效/背景乐入参(srcIsloop 的 data)。§8.7 / §10.6 */ +export interface SrcIsLoopReq { + /** wav 文件名(非 URL),路径 <解压根>//assets/wav/ */ + src: string; + /** 0 播一次 / >0 循环 / <0 停止 */ + isloop: number; +} + +/** 录音上传完成回传(getaudiourl 出站 data)。§9 / §10.6 */ +export interface GetAudioUrlResp { + audiourl: string; + time: number; // float + /** 旧容器 webviewActivity 录音成功时额外携带,值同 audiourl */ + filepath?: string; +} + +/** WiFi 信号回传(getwifiLevel 出站 data)。§9 */ +export interface WifiLevelResp { + ssidname: string; + signalLevel: number; // int 0~4 +} diff --git a/contracts/src/main/ets/handlers/InboundHandlers.ets b/contracts/src/main/ets/handlers/InboundHandlers.ets new file mode 100644 index 0000000..15d6c11 --- /dev/null +++ b/contracts/src/main/ets/handlers/InboundHandlers.ets @@ -0,0 +1,122 @@ +/** + * 入站 Handler 全集(H5 → 原生)。 + * + * 来源:《TSGame_原生与H5接口契约总规范》§8。H5 通过 + * `bridge.callHandler('<名称>', '', responseCallback)` 调用,原生用 + * `registerHandler('<名称>', ...)` 接收。 + * + * 🔴 铁律:枚举【值】是 H5 实际发出的字符串,**必须与原 Android 代码逐字一致**, + * 包括拼写错误(`finsh`/`getcameraaAddress`/`canclevibrator` 等),不可"纠正"。 + * 枚举【成员名】仅供 ArkTS 侧引用,可用规范驼峰。 + */ +export enum InboundHandlers { + // —— §8.1 账号 / 社交 —— + /** 触发授权登录("1"=QQ 空实现 / 其他=微信)。异步回传 sharelogin */ + AccreditLogin = 'accreditlogin', + /** 分享(微信/QQ/抖音),data=sharetypeBean JSON。异步回传 sharesuccess(仅微信) */ + FriendsShare = 'friendsSharetypeUrlToptitleDescript', + /** 批量下载图片/头像到本地,data=图片下载 JSON 数组。异步回传 getphoto */ + GetPhoto = 'getphoto', + + // —— §8.2 支付 —— + /** 浏览器方式支付(当前生效),data=收银台 URL。无回传 */ + PayBrowser = 'paybrowser', + /** App 内微信支付(仅旧版容器),data=GamePay JSON。异步回传 PayuserPaytypePaystate */ + GetGameplay = 'getGameplay', + + // —— §8.3 设备 / 系统信息 —— + /** 获取系统时间,同步返回毫秒时间戳裸串 */ + GetTime = 'getTime', + /** 获取电话状态,同步返回 int 裸串(0 挂断/1 接起/2…) */ + GetPhoneState = 'getphonestate', + /** 主动获取电量,异步回传 getBattery */ + GetBattery = 'getbattery', + /** 主动获取 WiFi 信号,异步回传 getwifiLevel */ + GetWifiLevel = 'getwifiLevel', + /** 主动获取网络状态,同步返回 int 裸串(1 无网/2 WiFi/3 移动) */ + GetNetwork = 'getnetwork', + /** App 版本比较结果,同步返回 int 裸串(1=本地>网络,0=否) */ + GetCompareCode = 'getcompareCode', + /** 获取手机配置信息(注意大写 I,区别出站 getphoneinfo),异步回传 getphoneinfo */ + GetPhoneInfo = 'getphoneInfo', + /** 获取市场 ID,同步返回 market 值裸串 */ + GetMarketName = 'getmarketname', + /** 获取任意本地配置值,data=配置名,同步返回该配置值裸串 */ + GetOtherName = 'getothername', + /** 获取 other 配置值,同步返回裸串 */ + GetOther = 'getOther', + + // —— §8.4 交互 / 反馈 —— + /** 切换屏幕方向,data="1"横屏/其他竖屏 */ + Orientation = 'orientation', + /** 震动,data=long 毫秒裸串 */ + Vibrator = 'vibrator', + /** 重复震动,data=int(-1 否/1 重复) */ + RepeatVibrator = 'repeatvibrator', + /** 取消震动(拼写原样 cancle) */ + CancleVibrator = 'canclevibrator', + /** 写入剪贴板,data=文本 */ + GameCopyText = 'gameCopytext', + /** 读取剪贴板,同步返回剪贴板内容裸串 */ + GamePasteText = 'gamepastetext', + /** 发送通知(空实现,未落地),data=裸串 */ + Notification = 'notification', + + // —— §8.5 摇一摇 —— + /** 开始监听摇一摇,异步回传 shakeEnd */ + StartShake = 'startshake', + /** 摇一摇声音开关,data="1"开/其他关 */ + SwitchShake = 'SwitchShake', + /** 停止监听摇一摇 */ + StopShake = 'stopshake', + + // —— §8.6 定位 —— + /** 开启定位,data="1"连续/其他单次。异步回传 getlocationinfo */ + StartLocation = 'startlocation', + /** 主动取最近一次定位,同步返回 MaplocationInfo JSON(未就绪返回错误码 JSON) */ + GetLocationInfo = 'getlocationinfo', + + // —— §8.7 音频 —— + /** 准备/开始录音(长按)。异步回传 getaudiourl */ + PrepareAudio = 'prepareaudio', + /** 播放语音消息,data=audiourl/type/user JSON。异步回传 gameui_play_voice/gameui_stop_voice */ + MediaTypeAudio = 'mediaTypeAudio', + /** 播放游戏音效/背景乐,data=src/isloop JSON。无回传 */ + SrcIsLoop = 'srcIsloop', + /** 语音播放总开关,data="1"开/其他静音 */ + VoicePlaying = 'voicePlaying', + + // —— §8.8 扫码 / 相机 / 浏览器 / 网页 —— + /** 打开扫一扫(拼写原样 saoma),异步回传 getsaomaData */ + OpenSaoma = 'opensaoma', + /** 打开相机拍照,异步回传 getcameraaAddress */ + OpenCamera = 'opencamera', + /** 系统/QQ 浏览器打开链接,data=URL */ + Browser = 'browser', + /** 打开内置通用网页容器(子游戏跳转路径 B),data=url/title/data JSON。回传 getWebdata */ + OpenUrlTitleData = 'OpenurlTitleData', + /** 按包名启动 App,失败则浏览器打开下载地址,data=packagename/downloadpath JSON */ + OpenApplyDownloadPath = 'openApplyDownloadpath', + + // —— §8.9 子游戏切换 / 房间(音视频) —— + /** 容器内切换子游戏(路径 A),data=webtype/Gamedirectory/gamedownloadurl/data JSON */ + SwitchOverGameData = 'SwitchOverGameData', + /** 判断子游戏资源是否就绪,data=目录名,同步返回 "1"已装/"0"未装 */ + GetGameInstall = 'getGameinstall', + /** 加入/创建音视频房间(声网),data=videoinfobean JSON。【本期桩】 */ + CreateRoom = 'createRoom', + /** 退出房间,data=裸串。【本期桩】 */ + ExitRoom = 'exitRoom', + /** 请求/绑定远端视频窗口,data=Othervideoinfo JSON。异步回传 getVideoinfo。【本期桩】 */ + GetVideoInfo = 'getVideoinfo', + /** 视频悬浮框显隐,data="1"显示/其他隐藏。【本期桩】 */ + DragViewVideoIsShow = 'DragViewvideoIsshow', + + // —— §8.10 退出 / 返回 —— + /** 退出游戏(拼写原样 finsh,非 finish) */ + Finsh = 'finsh', + /** 子游戏带数据返回大厅(仅 NewwebviewActivity),data=裸串 */ + BackGameData = 'backgameData', + /** 获取通讯录(未实现/已注释,保留名以兜底) */ + GetAddressBook = 'getAddressBook', +} diff --git a/contracts/src/main/ets/handlers/OutboundHandlers.ets b/contracts/src/main/ets/handlers/OutboundHandlers.ets new file mode 100644 index 0000000..9cb626b --- /dev/null +++ b/contracts/src/main/ets/handlers/OutboundHandlers.ets @@ -0,0 +1,57 @@ +/** + * 出站 Handler 全集(原生 → H5)。 + * + * 来源:《TSGame_原生与H5接口契约总规范》§9。原生通过 + * `callHandler('<名称>', '', null)` 调用;H5 必须用 + * `registerHandler('<名称>', ...)` 注册才能收到。 + * + * 🔴 命名陷阱(与入站对照,逐字保留): + * - 出站 `getphoneinfo`(全小写) ≠ 入站 `getphoneInfo`(大写 I)。 + * - 出站 `getBattery`(大写 B) ≠ 入站 `getbattery`(小写 b)。 + * - 出站 `backgameData-`(**末尾连字符**) ≠ 入站 `backgameData`(无连字符)。 + * - getphoto/getwifiLevel/getnetwork/getlocationinfo/getVideoinfo 入站出站【同名】。 + */ +export enum OutboundHandlers { + /** 前后台状态,data="1"前台/"2"后台。首次加载 100% 及前后台切换时下发 */ + AppService = 'appservice', + /** 截图上传地址 http://<本机IP>:<端口>/testurl。页面加载完成时下发 */ + SetPostUrl = 'setPostUrl', + /** 网页回传 text / Intent 透传 data。页面首次加载完成;或通用网页关闭回传(101) */ + GetWebData = 'getWebdata', + /** 图片/头像下载完成,data=[{pid,photourl}] JSON 数组。与入站 getphoto 同名 */ + GetPhoto = 'getphoto', + /** 微信授权登录成功,data=sharelogin JSON(用户资料) */ + ShareLogin = 'sharelogin', + /** 分享结果,data={success,type} JSON(success 2 成功/3 取消)。仅微信完整 */ + ShareSuccess = 'sharesuccess', + /** 摇一摇触发后约 1 秒,data="" 空串 */ + ShakeEnd = 'shakeEnd', + /** 电量,data=float 裸串(0~1)。大写 B,区别入站 getbattery */ + GetBattery = 'getBattery', + /** WiFi 信号,data={ssidname,signalLevel(0~4)} JSON。与入站同名 */ + GetWifiLevel = 'getwifiLevel', + /** 网络状态变化,data=int 裸串(1 断网/2 WiFi/3 移动)。与入站同名 */ + GetNetwork = 'getnetwork', + /** 录音上传完成/取消,data={audiourl,time(,filepath)} JSON */ + GetAudioUrl = 'getaudiourl', + /** 语音开始播放,data=user 透传裸串 */ + GameUiPlayVoice = 'gameui_play_voice', + /** 语音播放完成/被打断,data=user 透传裸串 */ + GameUiStopVoice = 'gameui_stop_voice', + /** 手机配置信息,data=phoneInfoBean JSON。全小写,区别入站 getphoneInfo */ + GetPhoneInfo = 'getphoneinfo', + /** 房间其他用户加入,data=int 裸串(远端 uid)。【本期桩不推】 */ + GetVideoInfo = 'getVideoinfo', + /** App 内微信支付结果,data={user,state,typeplay,type,data} JSON。【本期桩不推】 */ + PayUserPayTypePayState = 'PayuserPaytypePaystate', + /** 定位结果,data=MaplocationInfo JSON(或错误码 JSON)。与入站同名 */ + GetLocationInfo = 'getlocationinfo', + /** 来电状态广播,data=int 裸串 */ + PhoneState = 'phonestate', + /** 扫码返回,data=扫码结果裸串 */ + GetSaomaData = 'getsaomaData', + /** 拍照返回(拼写原样双 a),data=照片路径裸串 */ + GetCameraAddress = 'getcameraaAddress', + /** 返回键确认对话框流程,data="" 空串。⚠ 末尾连字符,区别入站 backgameData */ + BackGameDataDash = 'backgameData-', +} diff --git a/contracts/src/main/ets/handlers/PayloadType.ets b/contracts/src/main/ets/handlers/PayloadType.ets new file mode 100644 index 0000000..01b617a --- /dev/null +++ b/contracts/src/main/ets/handlers/PayloadType.ets @@ -0,0 +1,112 @@ +/** + * 每个 handler 的载荷类型标注(《契约规范》§8/§9,框架 §5.4 "裸串 vs JSON 陷阱")。 + * + * `raw` —— data 是裸字符串,能力层直接使用,不做 JSON 解析/序列化。 + * `json` —— data 是 JSON 字符串,能力层需 JSON.parse(入站)/ JSON.stringify(出站)。 + * + * 用途:MessageCodec 不擅自 JSON 化 data;能力层据此逐 handler 决定如何处理载荷, + * 编译期可查、杜绝写错。查询用 `inboundPayloadType()` / `outboundPayloadType()`。 + */ +import { InboundHandlers } from './InboundHandlers'; +import { OutboundHandlers } from './OutboundHandlers'; + +export type PayloadType = 'raw' | 'json'; + +/** 入站 handler 的入参载荷类型(H5 → 原生)。 */ +export const InboundPayload: Map = new Map([ + [InboundHandlers.AccreditLogin, 'raw'], + [InboundHandlers.FriendsShare, 'json'], + [InboundHandlers.GetPhoto, 'json'], + [InboundHandlers.PayBrowser, 'raw'], + [InboundHandlers.GetGameplay, 'json'], + [InboundHandlers.GetTime, 'raw'], + [InboundHandlers.GetPhoneState, 'raw'], + [InboundHandlers.GetBattery, 'raw'], + [InboundHandlers.GetWifiLevel, 'raw'], + [InboundHandlers.GetNetwork, 'raw'], + [InboundHandlers.GetCompareCode, 'raw'], + [InboundHandlers.GetPhoneInfo, 'raw'], + [InboundHandlers.GetMarketName, 'raw'], + [InboundHandlers.GetOtherName, 'raw'], + [InboundHandlers.GetOther, 'raw'], + [InboundHandlers.Orientation, 'raw'], + [InboundHandlers.Vibrator, 'raw'], + [InboundHandlers.RepeatVibrator, 'raw'], + [InboundHandlers.CancleVibrator, 'raw'], + [InboundHandlers.GameCopyText, 'raw'], + [InboundHandlers.GamePasteText, 'raw'], + [InboundHandlers.Notification, 'raw'], + [InboundHandlers.StartShake, 'raw'], + [InboundHandlers.SwitchShake, 'raw'], + [InboundHandlers.StopShake, 'raw'], + [InboundHandlers.StartLocation, 'raw'], + [InboundHandlers.GetLocationInfo, 'raw'], + [InboundHandlers.PrepareAudio, 'raw'], + [InboundHandlers.MediaTypeAudio, 'json'], + [InboundHandlers.SrcIsLoop, 'json'], + [InboundHandlers.VoicePlaying, 'raw'], + [InboundHandlers.OpenSaoma, 'raw'], + [InboundHandlers.OpenCamera, 'raw'], + [InboundHandlers.Browser, 'raw'], + [InboundHandlers.OpenUrlTitleData, 'json'], + [InboundHandlers.OpenApplyDownloadPath, 'json'], + [InboundHandlers.SwitchOverGameData, 'json'], + [InboundHandlers.GetGameInstall, 'raw'], + [InboundHandlers.CreateRoom, 'json'], + [InboundHandlers.ExitRoom, 'raw'], + [InboundHandlers.GetVideoInfo, 'json'], + [InboundHandlers.DragViewVideoIsShow, 'raw'], + [InboundHandlers.Finsh, 'raw'], + [InboundHandlers.BackGameData, 'raw'], + [InboundHandlers.GetAddressBook, 'raw'], +]); + +/** + * 入站 handler 的【同步返回】载荷类型(仅对有同步返回的 handler 标注)。 + * 未列出者表示无同步返回(结果走出站异步推送或无返回)。 + */ +export const InboundSyncReturn: Map = new Map([ + [InboundHandlers.GetTime, 'raw'], + [InboundHandlers.GetPhoneState, 'raw'], + [InboundHandlers.GetNetwork, 'raw'], + [InboundHandlers.GetCompareCode, 'raw'], + [InboundHandlers.GetMarketName, 'raw'], + [InboundHandlers.GetOtherName, 'raw'], + [InboundHandlers.GetOther, 'raw'], + [InboundHandlers.GamePasteText, 'raw'], + [InboundHandlers.GetLocationInfo, 'json'], + [InboundHandlers.GetGameInstall, 'raw'], +]); + +/** 出站 handler 的下发载荷类型(原生 → H5)。 */ +export const OutboundPayload: Map = new Map([ + [OutboundHandlers.AppService, 'raw'], + [OutboundHandlers.SetPostUrl, 'raw'], + [OutboundHandlers.GetWebData, 'raw'], + [OutboundHandlers.GetPhoto, 'json'], + [OutboundHandlers.ShareLogin, 'json'], + [OutboundHandlers.ShareSuccess, 'json'], + [OutboundHandlers.ShakeEnd, 'raw'], + [OutboundHandlers.GetBattery, 'raw'], + [OutboundHandlers.GetWifiLevel, 'json'], + [OutboundHandlers.GetNetwork, 'raw'], + [OutboundHandlers.GetAudioUrl, 'json'], + [OutboundHandlers.GameUiPlayVoice, 'raw'], + [OutboundHandlers.GameUiStopVoice, 'raw'], + [OutboundHandlers.GetPhoneInfo, 'json'], + [OutboundHandlers.GetVideoInfo, 'raw'], + [OutboundHandlers.PayUserPayTypePayState, 'json'], + [OutboundHandlers.GetLocationInfo, 'json'], + [OutboundHandlers.PhoneState, 'raw'], + [OutboundHandlers.GetSaomaData, 'raw'], + [OutboundHandlers.GetCameraAddress, 'raw'], + [OutboundHandlers.BackGameDataDash, 'raw'], +]); + +export function inboundPayloadType(name: string): PayloadType { + return InboundPayload.get(name) ?? 'raw'; +} + +export function outboundPayloadType(name: string): PayloadType { + return OutboundPayload.get(name) ?? 'raw'; +} diff --git a/contracts/src/main/ets/result/Errors.ets b/contracts/src/main/ets/result/Errors.ets new file mode 100644 index 0000000..547d7d5 --- /dev/null +++ b/contracts/src/main/ets/result/Errors.ets @@ -0,0 +1,47 @@ +/** + * 错误码与错误类型定义。 + * + * 定位错误码来自《契约规范》§10.8 / §12 MaplocationInfo.errorCode; + * 其余为框架内部统一错误分类(框架 §10 ErrorCenter)。 + */ + +/** 定位错误码(MaplocationInfo.errorCode 取值约定)。 */ +export enum LocationErrorCode { + Success = 0, + /** 高德/底层原始错误码为正数时按原值透传 */ + NotReady = -1, + PermissionDenied = -2, +} + +/** 框架内部错误类别(用于 ErrorCenter 收敛与处置策略)。 */ +export enum ErrorKind { + /** 网络/请求失败 */ + Network = 'network', + /** 配置解析/缺失 */ + Config = 'config', + /** 资源下载/解压失败 */ + Resource = 'resource', + /** 权限被拒 */ + Permission = 'permission', + /** 桥消息编解码/分发异常 */ + Bridge = 'bridge', + /** 能力 Provider 内部异常 */ + Capability = 'capability', + /** 未分类 */ + Unknown = 'unknown', +} + +/** 统一错误对象。 */ +export interface AppError { + kind: ErrorKind; + /** 机器可读错误码(可选,如定位错误码、HTTP 状态码) */ + code?: number; + /** 人类可读信息 */ + message: string; + /** 原始底层错误(可选) */ + cause?: Object; +} + +export function makeError(kind: ErrorKind, message: string, code?: number, cause?: Object): AppError { + return { kind, message, code, cause }; +} diff --git a/contracts/src/main/ets/result/Result.ets b/contracts/src/main/ets/result/Result.ets new file mode 100644 index 0000000..43d7ac0 --- /dev/null +++ b/contracts/src/main/ets/result/Result.ets @@ -0,0 +1,35 @@ +/** + * 统一返回类型 Result(框架 §10 横切:错误中心)。 + * + * 成功携带 data,失败携带 AppError。用工厂方法构造,避免散落的 try/catch + * 直接抛到上层;调用方用 `isOk()` 收窄后取 `data`/`error`。 + */ +import { AppError, ErrorKind, makeError } from './Errors'; + +export class Result { + readonly success: boolean; + readonly data?: T; + readonly error?: AppError; + + private constructor(success: boolean, data?: T, error?: AppError) { + this.success = success; + this.data = data; + this.error = error; + } + + static ok(data: T): Result { + return new Result(true, data, undefined); + } + + static fail(error: AppError): Result { + return new Result(false, undefined, error); + } + + static failWith(kind: ErrorKind, message: string, code?: number): Result { + return new Result(false, undefined, makeError(kind, message, code)); + } + + isOk(): boolean { + return this.success; + } +}