当前位置: 首页 > 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支持结算的国…...

springboot中使用mongodb完成评论功能

pom文件中引入 <!-- mongodb --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency> yml中配置连接 data:mongodb:uri: mongodb://admin:1234561…...

南京仁品耳鼻喉专科医院:12月启动公益义诊月

专业医疗资源送至“家门口”&#xff01;南京仁品耳鼻喉专科医院启动公益义诊月 随着2024年即将步入尾声&#xff0c;南京仁品耳鼻喉医院为回馈社会&#xff0c;提升公众健康福祉&#xff0c;将于12月隆重推出“三甲专家公益义诊月”活动。此次活动旨在通过汇聚众多耳鼻喉领域…...

微信小程序首页搜索框的实现教程

微信小程序首页搜索框的实现教程 前言 在现代移动应用中,搜索功能是用户获取信息的主要方式之一。对于购物小程序而言,提供一个美观且高效的搜索框,可以显著提升用户体验,帮助用户快速找到他们想要的商品。本文将详细介绍如何在微信小程序中实现一个样式优美的搜索框,包…...

Educational Codeforces Round 151 (Rated for Div. 2)

题目链接 B. Come Together 题意 输入 输出 思路 可以将B、C坐标作A的变换&#xff0c;将A平移至原点&#xff0c;然后分情况讨论: B、C两点都在轴上&#xff0c;具体分为同向轴和其他情况B、C两点都在象限中&#xff0c;具体分为相同象限、对角象限和相邻象限分别位于象限…...

第二十一天 深度学习简介

深度学习&#xff08;Deep Learning&#xff0c;简称DL&#xff09;是机器学习的一个分支&#xff0c;它通过构建和训练深层神经网络模型&#xff0c;从数据中学习和提取特征&#xff0c;以实现复杂任务的自动化处理和决策。以下是对深度学习的详细介绍&#xff1a; 一、起源与…...

mongodb/redis/neo4j 如何自己打造一个 web 数据库可视化客户端?

随笔 从千万粉丝“何同学”抄袭开源项目说起&#xff0c;为何纯技术死路一条&#xff1f; 数据源的统一与拆分 监控报警系统的指标、规则与执行闭环 我们的系统应该配置哪些监控报警项&#xff1f; 监控报警系统如何实现自监控? java 老矣&#xff0c;尚能饭否&#xff…...

elementUI如何dialog对话框 不设置 点击遮罩层 自动关闭的功能

背景 用户在填写dialog对话框的时候&#xff0c;有时候误触 遮罩层&#xff0c;导致form表单直接关闭&#xff0c;用户的信息丢失 代码 要使对话框在点击遮罩层时关闭&#xff0c;您需要在 el-dialog 组件上将 close-on-click-modal 属性设置为 false。以下是更新后的代码&…...

循环神经网络:从基础到应用的深度解析

&#x1f35b;循环神经网络&#xff08;RNN&#xff09;概述 循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;是一种能够处理时序数据或序列数据的深度学习模型。不同于传统的前馈神经网络&#xff0c;RNN具有内存单元&#xff0c;能够捕捉序列中前后信息…...

LeetCode 100.相同的树

题目&#xff1a; 给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 思路&#xff1a;灵神 代码&#xff1a; class Solution {public boolean…...

序列化与反序列化

序列化是将对象转换为可被存储或传输的格式&#xff0c;例如将对象转换为字节流或字符串。序列化的过程可以将对象的状态保存下来&#xff0c;以便在需要时可以重新创建对象。 反序列化则是将序列化的对象转换回原始的对象形式&#xff0c;以便可以使用和操作这些对象。 序列…...

spring boot打包fat jar

所谓fat jar就是包含所有依赖的jar以及其他开发的代码的jar包。可以通过java -jar xxx.jar直接启动运行&#xff0c;不需要部署到tomcat中间件就能运行。 接下来我们使用maven进行打包&#xff1a; &#xff08;1&#xff09;在需要带包的主模块的pom中添加build依赖&#xf…...

2021年美亚杯电子数据取证大赛-个人赛

资格赛-案件背景 2021年10月某日早上&#xff0c;本市一个名为"大路建设"的高速公路工地主管发现办公室的计算机被加密并无法开启&#xff0c;其后收到了勒索通知。考虑到高速公路的基建安全&#xff0c;主管决定报警。警方调查人员到达现场取证&#xff0c;发现办公…...

用 React 编写一个笔记应用程序

这篇文章会教大家用 React 编写一个笔记应用程序。用户可以创建、编辑、和切换 Markdown 笔记。 1. nanoid nanoid 是一个轻量级和安全的唯一字符串ID生成器&#xff0c;常用于JavaScript环境中生成随机、唯一的字符串ID&#xff0c;如数据库主键、会话ID、文件名等场景。 …...

泛型擦除是什么?

泛型擦除(Type Erasure)是Java编译器在编译泛型代码时的一种机制&#xff0c;它的目的是确保泛型能够与JAVA的旧版本(即不支持泛型的版本)兼容。泛型擦除会在编译时移除泛型类型信息&#xff0c;并将泛型类型替换为其非泛型的上限类型(通常是Object) 详细解释 在Java中&#…...

鸿蒙Next星河版基础代码

目录&#xff1a; 1、鸿蒙箭头函数的写法2、鸿蒙数据类型的定义3、枚举的定义以及使用4、position绝对定位及层级zIndex5、字符串的拼接转换以及数据的处理(1)字符串转数字(2)数字转字符串(3)布尔值转换情况(4)数组的增删改查 6、三元表达式7、鸿蒙for循环的几种写法7.1、基本用…...

物联网智能项目:智能家居系统的设计与实现

物联网(Internet of Things,IoT)技术正在迅速改变我们的生活方式,特别是在智能家居、工业自动化、环境监控等领域。物联网智能项目通过将设备、传感器、控制器等通过互联网连接,实现设备间的智能交互,带来高效、便捷和智能的体验。本文将介绍一个典型的物联网智能家居项目…...

STL算法之set相关算法

STL一共提供了四种与set(集合)相关的算法&#xff0c;分别是并集(union)、交集(intersection)、差集(difference)、对称差集(symmetric difference)。 目录 set_union set_itersection set_difference set_symmetric_difference 所谓set&#xff0c;可细分为数学上定义的和…...

STM32主要功能

STM32 是由意法半导体&#xff08;STMicroelectronics&#xff09;推出的一系列基于 ARM Cortex-M 内核的微控制器&#xff08;MCU&#xff09;。STM32 微控制器广泛应用于嵌入式系统中&#xff0c;因其高性能、低功耗、丰富的外设接口和多种封装形式而被广泛采用。其主要功能和…...

【数据结构】--ArrayList与顺序表

文章目录 1. 线性表2. 顺序表3. ArrayList简介4. MyArrayList的实现5. ArrayList使用5.1 ArrayList的构造5.2 ArrayList常见操作5.3 ArrayList的遍历5.4 ArrayList的扩容机制 6. ArrayList的具体使用6.1 简单的洗牌算法6.2 杨辉三角 1. 线性表 线性表&#xff08;linear list&…...

多线程篇-3--java内存模型(主内存,共享内存,三大特性,指定重排)

Java内存模型 Java Memory Model&#xff0c;简称JMM&#xff0c;本身是一种抽象的概念&#xff0c;实际上并不存在&#xff0c;它描述的是一组规范&#xff0c;通过这组规范定义了程序中各个变量&#xff08;包括实例字段&#xff0c;静态字段和构成数组对象的元素&#xff0…...

Android Studio的AI工具插件使用介绍

Android Studio的AI工具插件使用介绍 一、前言 Android Studio 的 AI 工具插件具有诸多重要作用&#xff0c;以下是一些常见的方面&#xff1a; 代码生成与自动补全 代码优化与重构 代码解读 学习与知识获取 智能搜索与资源推荐实际使用中可以添加注释&#xff0c;解读某段代…...

【Yarn Bug】 yarn 安装依赖出现的网络连接问题

最近&#xff0c;在初始化 Ant Design Pro 前端脚手架过程中&#xff0c;使用 yarn 安装依赖时遇到了网络连接问题&#xff0c;具体错误信息提示为 info There appears to be trouble with your network connection. Retrying...。通过百度查询&#xff0c;得知出现这种问题的原…...

Vue3的Setup语法动态获取Dom或调用子组件方法

官方文档&#xff1a;https://cn.vuejs.org/api/composition-api-setup.html#composition-api-setup 获取Dom <template><div class"todo" ref"todoDom" click"handleClick"></div> </template><script lang"t…...

中科院一区算法KO-K均值优化算法(K-means Optimizer)-附Matlab免费代码

首先&#xff0c;使用K-means算法在每次迭代中建立聚类区域的形心向量&#xff0c;然后KO提出两种移动策略&#xff0c;以在开发和探索能力之间建立平衡。每次迭代中探索或开发的移动策略的决定取决于一个参数&#xff0c;该参数将被设计为识别每个搜索代理是否在访问的区域中过…...

python数据可视化销量柱状图练习

需求&#xff1a; 假设某店铺的商品销量分为 线上销量 和 线下销量&#xff1a; 使用 叠加柱状图 分别显示线上和线下销量。 在柱状图中添加每种商品的总销量。 图表美观&#xff0c;包含图例、网格、颜色区分等。 代码实现&#xff1a; import matplotlib.pyplot as plt imp…...

甘特图全面指南:原理、制作与实际案例

甘特图&#xff08;Gantt Chart&#xff09;是一种用于项目管理的直观工具&#xff0c;以条形图的形式展示任务的时间进度和依赖关系。它通过简单明了的视觉效果帮助团队跟踪任务进展&#xff0c;分配资源并优化时间安排。本文将深入介绍甘特图的定义、制作方法&#xff0c;以及…...

如何创建 MySQL 数据库的副本 ?

MySQL 是一个广泛使用的开源数据库系统&#xff0c;它提供了多种数据库复制的方法。此功能对于确保跨不同环境的数据可用性和完整性至关重要。 管理 MySQL 数据库通常需要创建数据库的副本。这个任务被称为 MySQL 数据库复制&#xff0c;对于备份、测试、服务器迁移和其他关键…...

基于YOLO模型的目标检测与识别实现在ESP32-S3 EYE上全流程部署

前言 文章首发于 基于YOLO模型的目标检测与识别实现在ESP32-S3 EYE上全流程部署 文章目录 前言项目环境安装ESP-IDF安装开发环境运行环境 训练数据集准备添加自定义数据集 下载预训练模型训练 YOLO 模型模型量化和格式转换模型结果评估训练损失评估指标模型推理 模型部署部署环…...

2411C++,CXImage简单使用

介绍 CxImage是一个可非常简单快速的加载,保存,显示和转换图像的C类. 文件格式和链接的C库 Cximage对象基本上是加了一些成员变量来保存有用信息的一个位图: class CxImage{...protected:void* pDib; //包含标题,调色板,像素BITMAPINFOHEADER head; //标准头文件CXIMAGEINFO…...

Java对象与XML互相转换(xstream)

依赖 <dependency><groupId>com.thoughtworks.xstream</groupId><artifactId>xstream</artifactId><version>1.4.18</version></dependency> 实体类 package com.itheima.util;import lombok.AllArgsConstructor; import lom…...

计算机视觉工程师紧张学习中!

在当今这个日新月异的科技时代&#xff0c;计算机视觉作为人工智能的重要分支&#xff0c;正以前所未有的速度改变着我们的生活和工作方式。为了紧跟时代步伐&#xff0c;提升自我技能&#xff0c;一群怀揣梦想与热情的计算机视觉设计开发工程师们聚集在了本次线下培训活动中。…...

网关整合sentinel无法读取nacos配置问题分析

sentinel无法读取nacos配置问题分析 1.spring-cloud-gateway整合sentinel2.问题现象3.原因猜测4.源码分析4. 结语 最近公司需要上线一个集约项目&#xff0c;虽然为内网项目&#xff0c;但曾经有过内网被攻破&#xff0c;导致内部系统被攻击的案例&#xff0c;且集约系统同时在…...

速盾:高防 CDN 中高级缓存有什么用?

在高防 CDN&#xff08;Content Delivery Network&#xff0c;内容分发网络&#xff09;的服务体系里&#xff0c;高级缓存功能犹如一颗强大的 “性能优化引擎”&#xff0c;对于提升网站或应用的运行效率、减轻源站压力以及改善用户体验等诸多方面都发挥着极为关键的作用。 一…...

大数据期末笔记

第一章、大数据概述 人类的行为及产生的事件的一种记录称之为数据。 1、大数据时代的特征&#xff0c;并结合生活实例谈谈带来的影响。 &#xff08;一&#xff09;特征 1、Volume 规模性&#xff1a;数据量大。 2、Velocity高速性&#xff1a;处理速度快。数据的生成和响…...

Qt详解QUiLoader 动态加载UI文件

文章目录 详解 QUiLoader 模块的使用1. QUiLoader 简介1.1 应用场景 2. 准备工作2.1 添加模块依赖2.2 引入头文件 3. 使用 QUiLoader 加载界面3.1 示例代码form.uimain.cpp 4. 常用方法详解4.1 load函数原型作用参数返回值示例代码 4.2 createWidget函数原型作用参数返回值示例…...

Android -- 简易音乐播放器

Android – 简易音乐播放器 播放器功能&#xff1a;* 1. 播放模式&#xff1a;单曲、列表循环、列表随机&#xff1b;* 2. 后台播放&#xff08;单例模式&#xff09;&#xff1b;* 3. 多位置同步状态回调&#xff1b;处理模块&#xff1a;* 1. 提取文件信息&#xff1a;音频文…...

云平台与阿里云服务器使用

云平台 云就是一堆远程计算机组成的集群。 计算就是各种软件服务。 云平台就是远程计算机集群提供的的各种服务所组成的远程服务平台。 云平台提供的服务主要可以分为三个类别&#xff1a; I 服务 P服务 S服务 i就是基础设施服务infrastructure p就是平台服务platform …...

Dart 中 initializer lists

在 Dart 中&#xff0c;initializer lists 是构造函数的一种特性&#xff0c;允许你在进入构造函数体之前对某些字段进行初始化或进行检查。这些字段包括 final 字段&#xff0c;因为 final 字段必须在构造函数体运行之前被初始化。 以下是它的几个关键点和适用场景&#xff1…...

02.06、回文链表

02.06、[简单] 回文链表 1、题目描述 编写一个函数&#xff0c;检查输入的链表是否是回文的。 2、解题思路&#xff1a; 快慢指针找中点&#xff1a; 利用快慢指针的技巧来找到链表的中间节点。慢指针 slow 每次移动一步&#xff0c;而快指针 fast 每次移动两步。这样&…...

linux中限定特定用户使用crontab

在Linux中&#xff0c;crontab&#xff08;cron table&#xff09;是用来定时执行任务的工具。默认情况下&#xff0c;任何用户&#xff08;包括普通用户&#xff09;都可以为自己的账户创建和管理crontab条目&#xff0c;但前提是这个用户拥有对/var/spool/cron/crontabs目录的…...

Oracle Universal Unique Identifier (UUID)

本文介绍Oracle生成全局唯一ID的函数SYS_GUID&#xff0c;后续会对SYS_GUID和Sequence两种方法进行比较。 SYS_GUID 函数生成并返回一个由 16 个字节组成的全局唯一标识符&#xff08;RAW 值&#xff09;。在大多数平台上&#xff0c;生成的标识符由主机标识符、调用该函数的进…...

LangChain——加载知识库文本文档 PDF文档

文档加载 这涵盖了如何加载目录中的所有文档。 在底层&#xff0c;默认情况下使用 UnstructedLoader。需要安装依赖 pip install unstructuredpython导入方式 from langchain_community.document_loaders import DirectoryLoader我们可以使用 glob 参数来控制加载特定类型文…...

shell编程3,参数传递+算术运算

声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&#…...

Spring boot之BeanDefinition介绍

在spring框架中IOC容器进行bean的创建和管理。Bean的创建是一个比较复杂的过程&#xff0c;它并不像我们创建对象一样只是直接new一下就行&#xff0c;虽然有些bean确实就是New一下。但在Spring中可以通过一些途径对bean进行增强扩展。在这个过程中&#xff0c;BeanDefinition作…...

JAVA:Spring Boot 3 实现 Gzip 压缩优化的技术指南

1、简述 随着 Web 应用的用户量和数据量增加&#xff0c;网络带宽和页面加载速度逐渐成为瓶颈。为了减少数据传输量&#xff0c;提高用户体验&#xff0c;我们可以使用 Gzip 压缩 HTTP 响应。本文将介绍如何在 Spring Boot 3 中实现 Gzip 压缩优化。 2、配置 Spring Boot 3 对…...

探索 IntelliJ IDEA 中 Spring Boot 运行配置

前言 IntelliJ IDEA 作为一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;为 Spring Boot 应用提供了丰富的运行配置选项&#xff0c;定义了如何在 IntelliJ IDEA 中运行 Spring Boot 应用程序&#xff0c;当从主类文件运行应用程序时&#xff0c;IDE 将创建…...

Java学习,反射

Java反射是Java编程语言的一个重要特性&#xff0c;它允许程序在运行时查看任意对象所属的类&#xff0c;获取类的内部信息&#xff08;包括构造器、字段和方法等&#xff09;&#xff0c;并能动态地调用对象的方法或构造器。 反射概念 反射&#xff08;Reflection&#xff09…...

应急响应靶机——Windows挖矿事件

载入虚拟机&#xff0c;开启虚拟机&#xff1a; &#xff08;账户密码&#xff1a;administrator/zgsf123&#xff09; 发现登录进去就弹出终端界面&#xff0c;自动运行powshell命令&#xff0c;看来存在计划任务&#xff0c;自动下载了一些文件&#xff0c;之后就主动结束退…...

0017. shell命令--tac

目录 17. shell命令--tac 功能说明 语法格式 选项说明 实践操作 注意事项 17. shell命令--tac 功能说明 Linux 的 tac 命令用于按行反向输出文件内容&#xff0c;与 cat 命令的输出顺序相反。非常有趣&#xff0c;好记。也就是说&#xff0c;当我们使用tac命令查看文件内…...