当前位置: 首页 > news >正文

鸿蒙开发-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 应用内支付 开发步骤 步骤一&#xff1a;判断当前登录的华为账号所在服务地是否支持应用内支付 在使用应用内支付之前&#xff0c;您的应用需要向IAP Kit发送queryEnvironmentStatus请求&#xff0c;以此判断用户当前登录的华为帐号所在的服务地是否在IAP Kit支持结算的国…...

TYUT设计模式大题

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

Webman中实现定时任务

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

《以 C++破局:人工智能系统可解释性的探索与实现》

在当今科技飞速发展的时代&#xff0c;人工智能已深度融入我们的生活&#xff0c;从医疗诊断到金融决策&#xff0c;从交通管控到司法审判&#xff0c;其影响力无处不在。然而&#xff0c;在这些涉及重大利益和社会影响的关键领域&#xff0c;人工智能系统却面临着严峻的信任危…...

C++:QTableWidget删除选中行(单行,多行即可)

转自博客&#xff1a; Qt C -在QTableWidget中删除行 - 腾讯云开发者社区 - 腾讯云 我的界面&#xff1a; 采集机器人位置和姿态信息并写入QTableWidget控件中 删除代码&#xff1a; 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、数据中台、数据湖、数据仓库、数据集市的区别 网上看了好多有关数据湖的帖子&#xff0c;还有数据中台、数据湖、数据仓库、数据集市的区别的帖子&#xff0c;发现帖子写的都很多&#xff0c;而且专业名词很多&am…...

EDKII之安全启动详细介绍

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

原生js上传图片

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

使用torch==2.5.1版本用的清华源遇到的坑

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

泷羽Sec-星河飞雪-BurpSuite之解码、日志、对比模块基础使用

免责声明 学习视频来自 B 站up主泷羽sec&#xff0c;如涉及侵权马上删除文章。 笔记的只是方便各位师傅学习知识&#xff0c;以下代码、网站只涉及学习内容&#xff0c;其他的都与本人无关&#xff0c;切莫逾越法律红线&#xff0c;否则后果自负。 泷羽sec官网&#xff1a;http…...

对拍详细使用方法

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

Python面向对象编程与模块化设计练习

需求&#xff1a; 编写一个BankAccount类&#xff0c;模拟银行账户功能&#xff1a; 属性&#xff1a;账户名、余额 方法&#xff1a;存款、取款、查询余额 使用模块将类和测试代码分离。 模块文件&#xff1a;bank_account.py 该模块包含 BankAccount 类。 class BankAccoun…...

Linux系统硬件老化测试脚本:自动化负载与监控

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

搭建一个基于Web的文档管理系统,用于存储、共享和协作编辑文档

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

排序学习整理(1)

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

《深入探究 Java 中的 boolean 类型》

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

智享 AI 自动无人直播系统:打破地域与时间枷锁中小微企业的营销破局利器

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

接口测试工具:reqable

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

同时多平台git配置:GitHub和Gitee生成不同的SSH Key

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

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU&#xff1a;如何让RNN变得更聪明&#xff1f; 在深度学习的世界里&#xff0c;循环神经网络&#xff08;RNN&#xff09;以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而&#xff0c;传统RNN存在的一个严重问题——梯度消失&#…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

【单片机期末】单片机系统设计

主要内容&#xff1a;系统状态机&#xff0c;系统时基&#xff0c;系统需求分析&#xff0c;系统构建&#xff0c;系统状态流图 一、题目要求 二、绘制系统状态流图 题目&#xff1a;根据上述描述绘制系统状态流图&#xff0c;注明状态转移条件及方向。 三、利用定时器产生时…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

Yolov8 目标检测蒸馏学习记录

yolov8系列模型蒸馏基本流程&#xff0c;代码下载&#xff1a;这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中&#xff0c;**知识蒸馏&#xff08;Knowledge Distillation&#xff09;**被广泛应用&#xff0c;作为提升模型…...

二维FDTD算法仿真

二维FDTD算法仿真&#xff0c;并带完全匹配层&#xff0c;输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...