鸿蒙开发-HMS Kit能力集(应用内支付、推送服务)
1 应用内支付
开发步骤
步骤一:判断当前登录的华为账号所在服务地是否支持应用内支付
在使用应用内支付之前,您的应用需要向IAP Kit发送queryEnvironmentStatus请求,以此判断用户当前登录的华为帐号所在的服务地是否在IAP Kit支持结算的国家/地区中。
// pages/Index.ets
/*** @description 应用内支付服务示例-消耗型商品* @author 白晓明* @organization 坚果派* @website: nutpi.com.cn* @date 2024-06-01*/
import { common } from '@kit.AbilityKit'
import { iap } from '@kit.IAPKit';
import { JSON } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit';
import { promptAction } from '@kit.ArkUI';@Entry
@Component
struct Index {private context: common.UIAbilityContext = {} as common.UIAbilityContext;@State querying: boolean = true;@State queryingFailed: boolean = false;@State productInfoArray: iap.Product[] = [];@State queryFailedText: string = "查询失败!";showLoadingPage() {this.queryingFailed = false;this.querying = true;}showFailedPage(failedText?: string) {if (failedText) {this.queryFailedText = failedText;}this.queryingFailed = true;this.querying = false;}showNormalPage() {this.queryingFailed = false;this.querying = false;}aboutToAppear(): void {this.showLoadingPage();this.context = getContext(this) as common.UIAbilityContext;this.onCase();}async onCase() {this.showLoadingPage();const queryEnvCode = await this.queryEnv();if (queryEnvCode !== 0) {let queryEnvFailedText = "当前应用不支持IAP Kit服务!";if (queryEnvCode === iap.IAPErrorCode.ACCOUNT_NOT_LOGGED_IN) {queryEnvFailedText = "请通过桌面设置入口登录华为账号后再次尝试!";}this.showFailedPage(queryEnvFailedText);return;}}// 判断当前登录的华为账号所在服务地是否支持应用内支付。async queryEnv(): Promise<number> {try {console.log("IAPKitDemo queryEnvironmentStatus begin.");await iap.queryEnvironmentStatus(this.context);return 0;} catch (error) {promptAction.showToast({message: "IAPKitDemo queryEnvironmentStatus failed. Cause: " + JSON.stringify(error)})return error.code;}}build() {...}
}
步骤二:确保权益发放
用户购买商品后,开发者需要及时发放相关权益。但实际应用场景中,若出现异常(网络错误、进程被中止等)将导致应用无法知道用户实际是否支付成功,从而无法及时发放权益,即出现掉单情况。为了确保权益发放,您需要在以下场景检查用户是否存在已购未发货的商品:
- 应用启动时。
- 购买请求返回1001860001时。
- 购买请求返回1001860051时。
如果存在已购未发货商品,则发放相关权益,然后向IAP Kit确认发货,完成购买。
// pages/Index.ets
/*** @description 应用内支付服务示例-消耗型商品* @author 白晓明* @organization 坚果派* @website: nutpi.com.cn* @date 2024-06-01*/
import { common } from '@kit.AbilityKit'
import { iap } from '@kit.IAPKit';
import { JSON } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit';
import { promptAction } from '@kit.ArkUI';@Entry
@Component
struct Index {private context: common.UIAbilityContext = {} as common.UIAbilityContext;@State querying: boolean = true;@State queryingFailed: boolean = false;@State productInfoArray: iap.Product[] = [];@State queryFailedText: string = "查询失败!";showLoadingPage() {...}showFailedPage(failedText?: string) {...}showNormalPage() {...}aboutToAppear(): void {...}async onCase() {...await this.queryPurchase();}// 判断当前登录的华为账号所在服务地是否支持应用内支付。async queryEnv(): Promise<number> {...}async queryPurchase() {console.log("IAPKitDemo queryPurchase begin.");const queryPurchaseParam: iap.QueryPurchasesParameter = {productType: iap.ProductType.CONSUMABLE,queryType: iap.PurchaseQueryType.UNFINISHED};const result: iap.QueryPurchaseResult = await iap.queryPurchases(this.context, queryPurchaseParam);// 处理订单信息if (result) {const purchaseDataList: string[] = result.purchaseDataList;if (purchaseDataList === undefined || purchaseDataList.length <= 0) {console.log("IAPKitDemo queryPurchase, list empty.");return;}for (let i = 0; i < purchaseDataList.length; i++) {const purchaseData = purchaseDataList[i];const jwsPurchaseOrder = (JSON.parse(purchaseData) as PurchaseData).jwsPurchaseOrder;if (!jwsPurchaseOrder) {console.log("IAPKitDemo queryPurchase, jwsPurchaseOrder invalid.");continue;}const purchaseStr = JWTUtil.decodeJwtObj(jwsPurchaseOrder);const purchaseOrderPayload = JSON.parse(purchaseStr) as PurchaseOrderPayload;}}}build() {...}
}
步骤三:查询商品信息
通过queryProducts来获取在AppGallery Connect上配置的商品信息。发起请求时,开发者需在请求参数QueryProductsParameter中携带相关的商品ID,并根据实际配置指定其productType。
当接口请求成功时,IAP Kit将返回商品信息Product的列表。 您可以使用Product包含的商品价格、名称和描述等信息,向用户展示可供购买的商品列表。
// pages/Index.ets
/*** @description 应用内支付服务示例-消耗型商品* @author 白晓明* @organization 坚果派* @website: nutpi.com.cn* @date 2024-06-01*/
import { common } from '@kit.AbilityKit'
import { iap } from '@kit.IAPKit';
import { JSON } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit';
import { promptAction } from '@kit.ArkUI';@Entry
@Component
struct Index {private context: common.UIAbilityContext = {} as common.UIAbilityContext;@State querying: boolean = true;@State queryingFailed: boolean = false;@State productInfoArray: iap.Product[] = [];@State queryFailedText: string = "查询失败!";showLoadingPage() {...}showFailedPage(failedText?: string) {...}showNormalPage() {...}aboutToAppear(): void {...}async onCase() {...await this.queryProducts();}// 判断当前登录的华为账号所在服务地是否支持应用内支付。async queryEnv(): Promise<number> {...}// 查询商品信息async queryProducts() {try {console.log("IAPKitDemo queryProducts begin.");const queryProductParam: iap.QueryProductsParameter = {productType: iap.ProductType.CONSUMABLE,productIds: ['nutpi_course_1']};const result: iap.Product[] = await iap.queryProducts(this.context, queryProductParam);this.productInfoArray = result;this.showNormalPage();} catch (error) {this.showFailedPage();}}async queryPurchase() {...}build() {...}
}
步骤四:构建商品列表UI
// pages/Index.ets
/*** @description 应用内支付服务示例-消耗型商品* @author 白晓明* @organization 坚果派* @website: nutpi.com.cn* @date 2024-06-01*/
import { common } from '@kit.AbilityKit'
import { iap } from '@kit.IAPKit';
import { JSON } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit';
import { promptAction } from '@kit.ArkUI';@Entry
@Component
struct Index {private context: common.UIAbilityContext = {} as common.UIAbilityContext;@State querying: boolean = true;@State queryingFailed: boolean = false;@State productInfoArray: iap.Product[] = [];@State queryFailedText: string = "查询失败!";showLoadingPage() {...}showFailedPage(failedText?: string) {...}showNormalPage() {...}aboutToAppear(): void {...}async onCase() {...}// 判断当前登录的华为账号所在服务地是否支持应用内支付。async queryEnv(): Promise<number> {...}// 查询商品信息async queryProducts() {...}async queryPurchase() {...}build() {Column() {Column() {Text('应用内支付服务示例-消耗型').fontSize(18).fontWeight(FontWeight.Bolder)}.width('100%').height(54).justifyContent(FlexAlign.Center).backgroundColor(Color.White)Column() {Column() {Row() {Text('Consumables').fontSize(28).fontWeight(FontWeight.Bold).margin({ left: 24, right: 24 })}.margin({ top: 16, bottom: 12 }).height(48).justifyContent(FlexAlign.Start).width('100%')// 商品列表信息List({ space: 0, initialIndex: 0 }) {ForEach(this.productInfoArray, (item: iap.Product) => {ListItem() {Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) {Image($r('app.media.app_icon')).height(48).width(48).objectFit(ImageFit.Contain)Text(item.name).width('100%').height(48).fontSize(16).textAlign(TextAlign.Start).padding({ left: 12, right: 12 })Button(item.localPrice).width(200).fontSize(16).height(30).onClick(() => {this.createPurchase(item.id, item.type)}).stateEffect(true)}.borderRadius(16).backgroundColor('#FFFFFF').alignSelf(ItemAlign.Auto)}})}.divider({ strokeWidth: 1, startMargin: 2, endMargin: 2 }).padding({ left: 12, right: 12 }).margin({ left: 12, right: 12 }).borderRadius(16).backgroundColor('#FFFFFF').alignSelf(ItemAlign.Auto)}.backgroundColor('#F1F3F5').width('100%').height('100%').visibility(this.querying || this.queryingFailed ? Visibility.None : Visibility.Visible)// 加载进度组件Stack() {LoadingProgress().width(96).height(96)}.backgroundColor('#F1F3F5').width('100%').height('100%').visibility(this.querying ? Visibility.Visible : Visibility.None)// 异常文本提示Stack({ alignContent: Alignment.Center }) {Text(this.queryFailedText).fontSize(28).fontWeight(FontWeight.Bold).margin({ left: 24, right: 24 })}.backgroundColor('#F1F3F5').width('100%').height('100%').visibility(this.queryingFailed ? Visibility.Visible : Visibility.None).onClick(() => {this.onCase();})}.width('100%').layoutWeight(1)}.width('100%').height('100%').backgroundColor(0xF1F3F5)}
}
步骤五:发起购买
用户发起购买时,开发者的应用可通过向IAP Kit发送createPurchase请求来拉起IAP Kit收银台。发起请求时,需在请求参数PurchaseParameter中携带开发者此前已在华为AppGallery Connect网站上配置并生效的商品ID,并根据实际配置指定其productType。
// pages/Index.ets
/*** @description 应用内支付服务示例-消耗型商品* @author 白晓明* @organization 坚果派* @website: nutpi.com.cn* @date 2024-06-01*/
import { common } from '@kit.AbilityKit'
import { iap } from '@kit.IAPKit';
import { JSON } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit';
import { promptAction } from '@kit.ArkUI';@Entry
@Component
struct Index {private context: common.UIAbilityContext = {} as common.UIAbilityContext;@State querying: boolean = true;@State queryingFailed: boolean = false;@State productInfoArray: iap.Product[] = [];@State queryFailedText: string = "查询失败!";showLoadingPage() {...}showFailedPage(failedText?: string) {...}showNormalPage() {...}aboutToAppear(): void {...}async onCase() {...}// 判断当前登录的华为账号所在服务地是否支持应用内支付。async queryEnv(): Promise<number> {...}// 查询商品信息async queryProducts() {...}async queryPurchase() {...}/*** 发起购买* @param id AppGallery Connect控制台配置的商品ID* @param type 商品类型*/createPurchase(id: string, type: iap.ProductType) {console.log("IAPKitDemo createPurchase begin.");try {const createPurchaseParam: iap.PurchaseParameter = {productId: id,productType: type};iap.createPurchase(this.context, createPurchaseParam).then(async (result) => {console.log("IAPKitDemo createPurchase success. Data: " + JSON.stringify(result));// 获取PurchaseOrderPayload的JSON字符串const purchaseData: PurchaseData = JSON.parse(result.purchaseData) as PurchaseData;const jwsPurchaseOrder: string = purchaseData.jwsPurchaseOrder;// 解码 JWTUtil为自定义类,可参见Sample Code工程const purchaseStr = JWTUtil.decodeJwtObj(jwsPurchaseOrder);const purchaseOrderPayload = JSON.parse(purchaseStr) as PurchaseOrderPayload;// 处理发货}).catch((error: BusinessError) => {promptAction.showToast({message: "IAPKitDemo createPurchase failed. Cause: " + JSON.stringify(error)})if (error.code === iap.IAPErrorCode.PRODUCT_OWNED || error.code === iap.IAPErrorCode.SYSTEM_ERROR) {// 参考权益发放检查是否需要补发货,确保权益发放this.queryPurchase();}})} catch (err) {promptAction.showToast({message: "IAPKitDemo createPurchase failed. Error: " + JSON.stringify(err)})}}build() {...}
}
步骤六:完成购买
对PurchaseData.jwsPurchaseOrder解码验签成功后,如果PurchaseOrderPayload.purchaseOrderRevocationReasonCode为空,则代表购买成功,即可发放相关权益。
发货成功后,开发者需在应用中发送finishPurchase请求确认发货,以此通知IAP服务器更新商品的发货状态,完成购买流程。发送finishPurchase请求时,需在请求参数FinishPurchaseParameter中携带PurchaseOrderPayload中的productType、purchaseToken、purchaseOrderId。请求成功后,IAP服务器会将相应商品标记为已发货。
对于消耗型商品,应用成功执行finishPurchase之后,IAP服务器会将相应商品重新设置为可购买状态,用户即可再次购买该商品。
// pages/Index.ets
/*** @description 应用内支付服务示例-消耗型商品* @author 白晓明* @organization 坚果派* @website: nutpi.com.cn* @date 2024-06-01*/
import { common } from '@kit.AbilityKit'
import { iap } from '@kit.IAPKit';
import { JSON } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit';
import { promptAction } from '@kit.ArkUI';@Entry
@Component
struct Index {private context: common.UIAbilityContext = {} as common.UIAbilityContext;@State querying: boolean = true;@State queryingFailed: boolean = false;@State productInfoArray: iap.Product[] = [];@State queryFailedText: string = "查询失败!";showLoadingPage() {...}showFailedPage(failedText?: string) {...}showNormalPage() {...}aboutToAppear(): void {...}async onCase() {...}// 判断当前登录的华为账号所在服务地是否支持应用内支付。async queryEnv(): Promise<number> {...}// 查询商品信息async queryProducts() {...}async queryPurchase() {...}/*** 发起购买* @param id AppGallery Connect控制台配置的商品ID* @param type 商品类型*/createPurchase(id: string, type: iap.ProductType) {console.log("IAPKitDemo createPurchase begin.");try {const createPurchaseParam: iap.PurchaseParameter = {productId: id,productType: type};iap.createPurchase(this.context, createPurchaseParam).then(async (result) => {console.log("IAPKitDemo createPurchase success. Data: " + JSON.stringify(result));// 获取PurchaseOrderPayload的JSON字符串const purchaseData: PurchaseData = JSON.parse(result.purchaseData) as PurchaseData;const jwsPurchaseOrder: string = purchaseData.jwsPurchaseOrder;// 解码 JWTUtil为自定义类,可参见Sample Code工程const purchaseStr = JWTUtil.decodeJwtObj(jwsPurchaseOrder);const purchaseOrderPayload = JSON.parse(purchaseStr) as PurchaseOrderPayload;// 处理发货this.finishPurchase(purchaseOrderPayload);}).catch((error: BusinessError) => {promptAction.showToast({message: "IAPKitDemo createPurchase failed. Cause: " + JSON.stringify(error)})if (error.code === iap.IAPErrorCode.PRODUCT_OWNED || error.code === iap.IAPErrorCode.SYSTEM_ERROR) {// 参考权益发放检查是否需要补发货,确保权益发放this.queryPurchase();}})} catch (err) {promptAction.showToast({message: "IAPKitDemo createPurchase failed. Error: " + JSON.stringify(err)})}}finishPurchase(purchaseOrder: PurchaseOrderPayload) {console.log("IAPKitDemo finishPurchase begin.");const finishPurchaseParam: iap.FinishPurchaseParameter = {productType: purchaseOrder.productType,purchaseToken: purchaseOrder.purchaseToken,purchaseOrderId: purchaseOrder.purchaseOrderId};iap.finishPurchase(this.context, finishPurchaseParam).then((result) => {console.log("IAPKitDemo finishPurchase success");}).catch((error: BusinessError) => {promptAction.showToast({message: "IAPKitDemo finishPurchase failed. Cause: " + JSON.stringify(error)})})}build() {...}
}
2 推送服务
开发步骤
步骤一:请求通知授权
为确保应用可正常收到消息,建议应用发送通知前调用requestEnableNotification()方法弹出提醒,告知用户需要允许接收通知消息。
// entryability/EntryAbility.ets
/*** @description 应用入口* @author 白晓明* @organization 坚果派* @website: nutpi.com.cn* @date 2024-06-13*/
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
import { JSON } from '@kit.ArkTS';
import { notificationManager } from '@kit.NotificationKit';export default class EntryAbility extends UIAbility {async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');// 请求通知授权await this.requestNotification();}async requestNotification() {try {console.info("requestNotification: 请求通知授权开始。");// 查询通知是否授权const notificationEnabled: boolean = await notificationManager.isNotificationEnabled();console.info("requestNotification: " + (notificationEnabled ? '已' : '未') + "授权");if (!notificationEnabled) {// 请求通知授权await notificationManager.requestEnableNotification();}} catch (error) {const e: BusinessError = error as BusinessError;console.error("requestNotification failed. Cause: " + JSON.stringify(e));}}
}
步骤二:获取Push Token
导入pushService模块。建议在应用的UIAbility(例如EntryAbility)的onCreate()方法中调用getToken()接口获取Push Token并上报到开发者的服务端,方便开发者的服务端向终端推送消息。本示例便于应用端测试发送通知消息请求,将Push Token获取放置在Index.ets页面。
// pages/Index.ets
import { pushService } from '@kit.PushKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { JSON } from '@kit.ArkTS';
import { http } from '@kit.NetworkKit';
import { promptAction } from '@kit.ArkUI';
/*** @description 推送服务示例* @author 白晓明* @organization 坚果派* @website: nutpi.com.cn* @date 2024-06-13*/
@Entry
@Component
struct Index {@State pushToken: string = "";async aboutToAppear(): Promise<void> {try {// 获取Push Tokenconst pushToken: string = await pushService.getToken();console.log("getToken succeed. Token: " + pushToken);const now = new Date();const timestamp = now.getTime();console.log("getToken succeed. Time: " + Math.floor(timestamp / 1000));console.log("getToken succeed. Time: " + (Math.floor(timestamp / 1000) + 3600));this.pushToken = pushToken;// 此处需要上报Push Token到应用服务端} catch (error) {const e: BusinessError = error as BusinessError;console.error("getToken failed. Cause: " + JSON.stringify(e));}}build() {...}
}
步骤三:获取项目ID
登录AppGallery Connect控制台,选择“我的项目”,在项目列表中选择对应的项目,左侧导航栏选择“项目设置”,拷贝项目ID。
步骤四:创建服务账号密钥文件
- 开发者需要在华为开发者联盟的API Console上创建并下载推送服务API的服务账号密钥文件。点击“管理中心 > API服务 > API库”,在API库页面选择“项目名称”,在展开的App Services列表中点击“推送服务”。
- 点击推送服务页面中的“启用”,完成API添加。
- 点击“管理中心 > API服务 > 凭证”,在凭证页面点击“服务账号密钥”卡片中的“创建凭证”按钮。
- 在“创建服务账号密钥”页面输入信息并点击“生成公私钥”,点击“创建并下载JSON”,完成“服务账号密钥”凭证创建,需要开发者保存“支付公钥”,用于后期生成JWT鉴权令牌。
步骤五:生成JWT Token
开发者在正式开发前调试功能,可使用在线生成工具获取JWT Token,需要注意生成JWT Token时Algorithm请选择RS256或PS256。若用于正式环境,为了方便开发者生成服务账号鉴权令牌,华为提供了JWT开源组件,可根据开发者使用的开发语言选择进行开发。
- HEADER中的kid指下载的服务账号密钥文件中key_id字段。
- PAYLOAD数据中iss指下载的的服务账号密钥文件中sub_account字段。
- VERIFY SIGNATURE中复制粘贴公钥和私钥。
步骤六:调用推送服务REST API
该模块需要开发者在应用服务端自行开发,需要结合用户信息留存设备Token,本课程中该功能位于应用端仅用于学习,不推荐该方法。应用服务端调用Push Kit服务端的REST API推送通知消息,需要传递的参数说明如下所示:
- [projectId]:项目ID。
- Authorization:JWT格式字符串,JWT Token前加“Bearer ”,需注意“Bearer”和JWT格式字符串中间的空格不能丢。
- push-type:0表示Alert消息,此处为通知消息场景。
- category:表示通知消息自分类的类别,MARKETING为资讯营销类消息。
- actionType:0表示点击消息打开应用首页。
- token:Push Token。
- testMessage:测试消息标识,true标识测试消息。
- notifyId:(选填)自定义消息标识字段,仅支持数字,范围[0, 2147483647],若要用于消息撤回则必填。

在应用端按钮组件Button的点击事件onClick中通过数据请求API实现发送通知消息。
// pages/Index.ets
import { pushService } from '@kit.PushKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { JSON } from '@kit.ArkTS';
import { http } from '@kit.NetworkKit';
import { promptAction } from '@kit.ArkUI';
/*** @description 推送服务示例* @author 白晓明* @organization 坚果派* @website: nutpi.com.cn* @date 2024-06-13*/
@Entry
@Component
struct Index {@State pushToken: string = "";@State isLoading: boolean = false;// 步骤五生成的JWT Tokenauthorization: string = "Bearer ****";async aboutToAppear(): Promise<void> {try {// 获取Push Tokenconst pushToken: string = await pushService.getToken();console.log("getToken succeed. Token: " + pushToken);const now = new Date();const timestamp = now.getTime();console.log("getToken succeed. Time: " + Math.floor(timestamp / 1000));console.log("getToken succeed. Time: " + (Math.floor(timestamp / 1000) + 3600));this.pushToken = pushToken;// 上报Push Token} catch (error) {const e: BusinessError = error as BusinessError;console.error("getToken failed. Cause: " + JSON.stringify(e));}}async deletePushTokenFunc() {try {await pushService.deleteToken();} catch (error) {const e: BusinessError = error as BusinessError;console.error("deleteToken failed. Cause: " + JSON.stringify(e));}}build() {Column() {Row() {Text('推送服务示例').fontSize(18).fontWeight(FontWeight.Bolder)}.width('100%').height(54).justifyContent(FlexAlign.Center).alignItems(VerticalAlign.Center)Column({ space: 16 }) {Row() {LoadingProgress()Text('等待通知发送完成').fontSize(16)}.width('100%').height(64).justifyContent(FlexAlign.Center).visibility(this.isLoading ? Visibility.Visible : Visibility.Hidden)Button('发送通知消息').type(ButtonType.Normal).borderRadius(8).enabled(!this.isLoading).onClick(async () => {try {this.isLoading = true;const url = "https://push-api.cloud.huawei.com/v3/388421841222199046/messages:send";const httpRequest = http.createHttp();const response: http.HttpResponse = await httpRequest.request(url, {header: {"Content-Type": "application/json","Authorization": this.authorization,"push-type": 0},method: http.RequestMethod.POST,extraData: {"payload": {"notification": {"category": "MARKETING","title": "普通通知标题","body": "普通通知内容","clickAction": {"actionType": 0},"notifyId": 12345}},"target": {"token": [this.pushToken]},"pushOptions": {"testMessage": true}}})if (response.responseCode === 200) {const result = response.result as string;const data = JSON.parse(result) as ResultData;promptAction.showToast({message: data.msg})}} catch (error) {const e: BusinessError = error as BusinessError;console.error("getToken failed. Cause: " + JSON.stringify(e));} finally {this.isLoading = false;}})}.width('100%').layoutWeight(1)}.height('100%').width('100%')}
}// 接口返回数据类
interface ResultData {code: string;msg: string;requestId: string;
}
相关文章:

鸿蒙开发-HMS Kit能力集(应用内支付、推送服务)
1 应用内支付 开发步骤 步骤一:判断当前登录的华为账号所在服务地是否支持应用内支付 在使用应用内支付之前,您的应用需要向IAP Kit发送queryEnvironmentStatus请求,以此判断用户当前登录的华为帐号所在的服务地是否在IAP Kit支持结算的国…...

TYUT设计模式大题
对比简单工厂,工厂方法,抽象工厂模式 比较安全组合模式和透明组合模式 安全组合模式容器节点有管理子部件的方法,而叶子节点没有,防止在用户在叶子节点上调用不适当的方法,保证了的安全性,防止叶子节点暴露…...

Webman中实现定时任务
文章目录 Webman中实现定时任务一、引言二、安装与配置1、安装Crontab组件2、创建进程文件3、配置进程文件随Webman启动4、重启Webman5、Cron表达式(补充)例子 三、使用示例四、总结 Webman中实现定时任务 一、引言 在现代的后端开发中,定时…...

《以 C++破局:人工智能系统可解释性的探索与实现》
在当今科技飞速发展的时代,人工智能已深度融入我们的生活,从医疗诊断到金融决策,从交通管控到司法审判,其影响力无处不在。然而,在这些涉及重大利益和社会影响的关键领域,人工智能系统却面临着严峻的信任危…...

C++:QTableWidget删除选中行(单行,多行即可)
转自博客: Qt C -在QTableWidget中删除行 - 腾讯云开发者社区 - 腾讯云 我的界面: 采集机器人位置和姿态信息并写入QTableWidget控件中 删除代码: 1.获取要删除行的索引 2.删除行 QList<QTableWidgetItem*> items ui->tableW…...

C++类中多线程的编码方式
问题 在C++代码中,一般的代码是需要封装在类里面,比如对象,方法等。否则就不能很好的利用C++面向对象的能力了。 但是这个方式在处理线程时会碰到一个问题。 考虑下面一个简单的场景: class demoC { public:std::thread t;int x;void threadFunc(){std::cout<<x&…...

数据湖的概念(包含数据中台、数据湖、数据仓库、数据集市的区别)--了解数据湖,这一篇就够了
文章目录 一、数据湖概念1、企业对数据的困扰2、什么是数据湖3、数据中台、数据湖、数据仓库、数据集市的区别 网上看了好多有关数据湖的帖子,还有数据中台、数据湖、数据仓库、数据集市的区别的帖子,发现帖子写的都很多,而且专业名词很多&am…...

EDKII之安全启动详细介绍
文章目录 安全启动简介安全启动流程介绍签名过程BIOS实现小结 安全启动简介 安全启动(Secure Boot)是一种计算机系统的安全功能,旨在确保系统启动过程中只能加载经过数字签名的受信任的操作系统和启动加载程序。通过使用安全启动,…...

原生js上传图片
无样式上传图片 创建一个 FormData 对象:这个对象可以用于存储数据。 将文件添加到 FormData 对象:通过 append() 方法,将用户选择的文件添加到 formData 对象中。 使用 fetch 发送请求:使用 fetch API 或者其他方法将 FormDat…...

使用torch==2.5.1版本用的清华源遇到的坑
解决安装torch后,torch.cuda.is_available()结果为false的问题 清华源下载到的torch2.5.1版本的Lib\site-packages\torch\version.py 其中,清华源指的是: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorchhttps://mirrors.tuna.tsinghua.…...

泷羽Sec-星河飞雪-BurpSuite之解码、日志、对比模块基础使用
免责声明 学习视频来自 B 站up主泷羽sec,如涉及侵权马上删除文章。 笔记的只是方便各位师傅学习知识,以下代码、网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负。 泷羽sec官网:http…...

对拍详细使用方法
对拍的作用 对于我们在学校OJ,cf,牛客…各种只提供少量测试数据的题目,常常交上代码常常超时,能写出正确的暴力代码而题目要求的时间复杂度更低。然而这时你写出了能通过样例且时间复杂度更低的代码,但交上去就是错误…...

Python面向对象编程与模块化设计练习
需求: 编写一个BankAccount类,模拟银行账户功能: 属性:账户名、余额 方法:存款、取款、查询余额 使用模块将类和测试代码分离。 模块文件:bank_account.py 该模块包含 BankAccount 类。 class BankAccoun…...

Linux系统硬件老化测试脚本:自动化负载与监控
简介: 这篇文章介绍了一款用于Linux系统的自动化硬件老化测试脚本。该脚本能够通过对CPU、内存、硬盘和GPU进行高强度负载测试,持续运行设定的时长(如1小时),以模拟长时间高负荷运行的环境,从而验证硬件的稳…...

搭建一个基于Web的文档管理系统,用于存储、共享和协作编辑文档
搭建一个基于Web的文档管理系统,用于存储、共享和协作编辑文档 本项目采用以下架构: NFS服务器: 负责存储文档资料。Web服务器: 负责提供文档访问和编辑功能。SELinux: 负责权限控制,确保文档安全。Git服务器: 负责存储文档版本历史&#x…...

排序学习整理(1)
1.排序的概念及运用 1.1概念 排序:所谓排序,就是使⼀串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作,以便更容易查找、组织或分析数据。 1.2运用 购物筛选排序 院校排名 1.3常见排序算法 2.实…...

《深入探究 Java 中的 boolean 类型》
在 Java 编程语言的世界里,boolean 类型虽然看似简单,却在程序的逻辑控制和决策中起着至关重要的作用。本文将带你深入了解 Java 中的 boolean 类型,从其基本概念、用法到实际应用场景,以及一些常见的注意事项。 一、boolean 类型…...

智享 AI 自动无人直播系统:打破地域与时间枷锁中小微企业的营销破局利器
中小微企业,在商业浪潮中恰似逐浪扁舟,常面临营销成本高、推广渠道窄、专业人才缺等 “暗礁”,而智享 AI 自动无人直播系统恰如精准导航的灯塔,助其破浪前行、突出重围。 成本维度,传统直播人力成本让中小微企业望而却…...

接口测试工具:reqable
背景 在众多接口测试工具中挑选出一个比较好用的接口测试工具。使用过很多工具,如Postman、Apifox、ApiPost等,基本上是同类产品,一般主要使用到的功能就是API接口和cURL,其他的功能目前还暂未使用到。 对比 性能方面ÿ…...

同时多平台git配置:GitHub和Gitee生成不同的SSH Key
文章目录 GitHub和Gitee生成不同的SSH Key步骤1:生成SSH Key步骤2:配置SSH配置文件步骤3:查看SSH公钥步骤4:将SSH公钥添加到GitHub和Gitee步骤5:测试SSH连接步骤6:添加remote远程库 GitHub和Gitee生成不同的…...

刷题计划day24 回溯(三)【复原 IP 地址】【子集】【子集 II】
⚡刷题计划day24 回溯(三)继续,回溯一共会有五个专题,敬请期待关注,可以点个免费的赞哦~ 往期可看专栏,关注不迷路, 您的支持是我的最大动力🌹~ 目录 题目一:复原 IP…...

从“找三角形”讲“等腰三角形”
【题目】 周长为11,且各边长均为整数的三角形有哪些? 【答案】 四种,边长分别为: 2 4 5 3 3 5 1 5 5 3 4 4 【解析】 讲解等腰三角形的概念时,传统方法一般向学生展示一个等腰三角形的实物模型,这…...

Java中的泛型方法和泛型类
在Java编程语言中,泛型(Generics)是一个强大的特性,它使得类、接口和方法能够灵活地操作各种数据类型,同时保持类型安全。泛型主要通过类型参数(Type Parameters)来实现,这些类型参数…...

springboot学习-spring-boot-data-jdbc分页/排序/多表查询的例子
上次使用的是JdbcTemplate实现的,是比较老的方式,重新用spring boot data jdbc和jdbc client 实现一遍。也比较一下这几种的编码差异。数据库方面JAVA给了太多选择,反而不好选了。 上次就试图直接用: public interface UserRepo…...

通信与网络基础
1.网络通信基本概念 通信:人、物通过某种介质和行为进行信息传递与交流 网络通信:终端设备之间通过计算机网络进行通信 两个终端通过网线传递文件 多个终端通过路由器传递文件 终端通过Internet下载文件 2.信息传递过程 图1-1 假定A计算机访问B的web…...

【3.存储系统】综合大题
【考点】存储系统综合大题 【2011年408真题】某计算机存储器按字节编址,虚拟(逻辑)地址空间大小为16 MB,主存(物理)地址空间大小为1 MB,页面大小为4 KB;Cache采用直接映射方式,共8行;主存与Cache之间交换的…...

【Linux】【字符设备驱动】深入解析
Linux字符设备驱动程序用于控制不支持随机访问的硬件设备,如串行端口、打印机、调制解调器等。这类设备通常以字符流的形式与用户空间程序进行交互。本节将深入探讨字符设备驱动的设计原理、实现细节及其与内核其他组件的交互。 1. 引言 字符设备驱动程序是Linux内…...

【JavaEE】多线程(2)
一、线程安全 1.1 线程安全的概念 线程是随机调度执行的,如果多线程环境下的程序运行的结果符合我们预期则说明线程安全,反之,如果遇到其他结果甚至引起了bug则说明线程不安全 1.2 经典例子与解释 下面举一个经典的线程不安全的例子&…...

mac下Gpt Chrome升级成GptBrowser书签和保存的密码恢复
cd /Users/自己的用户名/Library/Application\ Support/ 目录下有 GPT\ Chrome/ Google/ GptBrowser/ GPT\ Chrome 为原来的chrome浏览器的文件存储目录. GptBrowser 为升级后chrome浏览器存储目录 书签所在的文件 Bookmarks 登录账号Login 相关的文件 拷贝到GptBrow…...

使用Grafana K6来测测你的系统负载能力
背景 近期我们有个号称会有很高很高并发的系统要上线,为了测试一下自己开发的系统的负载能力,准备了点海克斯科技,来看看抗不抗的住。 之前笔者写过用Apache JMeter进行压力测试的文章(传送门👉:https://…...