前言:
各位同学大家好 很久没有跟大家见面了 具体多久我也不清楚,最近研究一下arkui-x调起华为支付功能 所以就分享给大家
作者:徐庆
团队:坚果派 公众号:“大前端之旅” 润开鸿生态技术专家,华为HDE,CSDN博客专家,CSDN超级个体,CSDN特邀嘉宾,InfoQ签约作者,OpenHarmony布道师,电子发烧友专家博客,51CTO博客专家,擅长HarmonyOS/OpenHarmony应用开发、熟悉服务卡片开发。欢迎合作。
效果图:
-
akrui-x 效果
-
在安卓上面的效果
-
调起华为支付
我们是通过AkrUi-X和安卓交互 最后就接了华为支付的Android 版本最后调起了支付
具体实现
-
ArkUI端代码实现
akrui 端我这边只写了一个按钮调用华为内购支付和接收数据
-
导入平台桥接模块
// 导入平台桥接模块
import bridge from '@arkui-x.bridge';
创建平台桥接对象
复制代码
// 创建平台桥接对象
private bridgeImpl = bridge.createBridge('Bridge');
复制代码
// 发送数据到Android侧,并通过状态变量,将Android侧的响应数据显示在页面上
this.nativeResponse = await this.bridgeImpl.sendMessage('Hello ArkUI-X!');
接收回传回来的数据
复制代码
aboutToAppear() {
this.getHelloArkUI();
}
getHelloArkUI() {
// 调用Android侧方法
this.bridgeImpl.callMethod('getHelloArkUI').then((result: string) => {
// 通过状态变量,将Android侧方法的返回值显示在页面上
this.helloArkUI = result;
});
完整代码
// 导入平台桥接模块
import bridge from '@arkui-x.bridge';
@Entry
@Component
struct Index {
// 创建平台桥接对象
private bridgeImpl = bridge.createBridge('Bridge');
@State helloArkUI: string = '';
@State nativeResponse: string = '';
aboutToAppear() {
this.getHelloArkUI();
}
getHelloArkUI() {
}
build() {
Row() {
Column() {
Text(this.helloArkUI)
.fontSize(15)
.margin(10)
Button('点击华为支付')
.fontSize(15)
.margin(10)
.onClick(async () => {
// 发送数据到Android侧,并通过状态变量,将Android侧的响应数据显示在页面上
await this.bridgeImpl.sendMessage('Hello ArkUI-X!');
})
Text('Response from Native: ' + this.nativeResponse)
.fontSize(15)
.margin(10)
}
.width('100%')
}
.height('100%')
}
}
安卓版本华为内购支付部分
我们编译项目 需要导入arkui-x编译之后产生的安卓原生宿主工程
官方文档地址
要的依赖
//华为支付
implementation 'com.huawei.hms:iap:6.13.0.300'
plugins {
id 'com.android.application'
id 'com.huawei.agconnect'
}
添加 json文件
在外层的 build.gradle 里面添加如下配置
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
maven {url 'https://developer.huawei.com/repo/'}
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.1'
classpath 'com.huawei.agconnect:agcp:1.6.0.300'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
maven {url 'https://developer.huawei.com/repo/'}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
akrui-x 和原生安卓通信交互
引用平台桥接模块
package com.example.helloworld;
/**
* 作者:xuqing
* 时间:2024年04月01日 15:27:31
* 邮箱:1693891473@qq.com
* 说明:
*/
import android.app.Activity;
import android.content.Context;
import android.util.Log;
// 引用平台桥接模块
import com.example.helloworld.huaweipay.OjjlgHuaweiPay;
import ohos.ace.adapter.capability.bridge.BridgePlugin;
import ohos.ace.adapter.capability.bridge.IMessageListener;
public class Bridge extends BridgePlugin implements IMessageListener {
private static final String TAG = "Bridge";
private Context context;
public Bridge(Context context, String name, int id) {
super(context, name, id);
this.context=context;
setMessageListener(this);
}
// Android侧方法,供ArkUI侧调用
public String getHelloArkUI() {
return "Hello ArkUI!";
}
// 注册回调,接收ArkUI侧发来的数据
@Override
public Object onMessage(Object object) {
// new EntryEntryAbilityActivity(). initgooglePlay();
//这里的 xxxxxx换城你们自己的商品id
// GooglePay.getInstance().toGooglePay((Activity) context,"xxxxxx");
OjjlgHuaweiPay.getInstance().fucnxPayInApp((Activity) context,"1","testpay");
Log.e(TAG, "onMessage: "+object.toString() );
return "java onMessage success";
}
@Override
public void onMessageResponse(Object object) {
}
}
内购支付初始化
/**
* @param context 链接华为服务器
*/
public void queryIsReady(Activity context) {
this.context = context;
mClient = Iap.getIapClient(context);
IapRequestHelper.isEnvReady(mClient, new IapApiCallback<IsEnvReadyResult>() {
@Override
public void onSuccess(IsEnvReadyResult result) {
Log.e(TAG, "onSuccess: " + "检查支付成功 --- > ");
//initview();
}
@Override
public void onFail(Exception e) {
Log.e(TAG, "isEnvReady fail, " + e.getMessage());
ExceptionHandle.handle(context, e);
}
});
}
查询商品ID
/**
* @param productId
* @param mContext 查询商品ID
*/
public void queryProducts( Activity mContext,final String productId,String token) {
Log.e(TAG, "queryProducts: productId -- >"+productId );
if(mClient!=null){
List<String> productIds = new ArrayList<String>();
productIds.add(productId);
IapRequestHelper.obtainProductInfo(mClient, productIds, IapClient.PriceType.IN_APP_CONSUMABLE,
new IapApiCallback<ProductInfoResult>() {
@Override
public void onSuccess(ProductInfoResult result) {
Log.i(TAG, "obtainProductInfo, success");
if (result == null) {
return;
}
Log.e(TAG, "onSuccess:result.getReturnCode() -- > "+result.getReturnCode() );
// if(result.getReturnCode()==0){
if (result.getProductInfoList() != null&&result.getProductInfoList().size()>0) {
List<ProductInfo> productInfoList=result.getProductInfoList();
for(ProductInfo productInfo:productInfoList){
String product_id=productInfo.getProductId(); //商品id
String product_name=productInfo.getProductName();// 商品名称
String product_price=productInfo.getPrice();// 商品金额
String currency_code=productInfo.getCurrency();// 币种
buy((Activity) mContext,productId,token);
}
}else {
Log.e(TAG, "onSuccess: " + result.getErrMsg() + result.getReturnCode());
Log.e(TAG, "onSuccess: " + "商品列表为空");
Toast.makeText(mContext, "商品列表为空", Toast.LENGTH_SHORT);
}
}
@Override
public void onFail(Exception e) {
Log.e(TAG, "obtainProductInfo: " + e.getMessage());
}
});
}
}
调起支付
/***
*
* @param productId
* @param
*
*
*
*/
public void buy(final Activity activity, String productId, String token) {
IapRequestHelper.createPurchaseIntent(mClient, productId, IapClient.PriceType.IN_APP_CONSUMABLE,token,
new IapApiCallback<PurchaseIntentResult>() {
@Override
public void onSuccess(PurchaseIntentResult result) {
if (result == null) {
Log.e(TAG, "result is null");
return;
}
IapRequestHelper.startResolutionForResult(activity, result.getStatus(), REQ_CODE_BUY);
}
@Override
public void onFail(Exception e) {
int errorCode = ExceptionHandle.handle(activity, e);
if (errorCode != ExceptionHandle.SOLVED) {
Log.i(TAG, "createPurchaseIntent, returnCode: " + errorCode);
// Handle error scenarios.
switch (errorCode) {
case OrderStatusCode.ORDER_PRODUCT_OWNED:
// queryPurchases(null);
break;
default:
break;
}
}
}
});
}
支付回调
/**
*
*
* @param requestCode
* @param resultCode
* @param data
* 华为内购支付回调
*/
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.e(TAG, "onActivityResult");
if (requestCode == REQ_CODE_BUY) {
if (data == null) {
Log.e(TAG, "data is null");
return;
}
PurchaseResultInfo purchaseResultInfo = Iap.getIapClient(context).parsePurchaseResultInfoFromIntent(data);
switch(purchaseResultInfo.getReturnCode()) {
case OrderStatusCode.ORDER_STATE_CANCEL:
Log.e(TAG, "onActivityResult:ORDER_STATE_CANCEL " );
Toast.makeText(context, " ORDER_STATE_CANCEL Order has been canceled!", Toast.LENGTH_SHORT).show();
break;
case OrderStatusCode.ORDER_STATE_FAILED:
Log.e(TAG, "onActivityResult: ORDER_STATE_FAILED ");
case OrderStatusCode.ORDER_PRODUCT_OWNED:
Log.e(TAG, "onActivityResult: ORDER_PRODUCT_OWNED ");
queryPurchases(null);
break;
case OrderStatusCode.ORDER_STATE_SUCCESS:
Log.e(TAG, "onActivityResult: ORDER_STATE_SUCCESS ");
//先去服务器验签 然后再消耗
deliverProduct(purchaseResultInfo.getInAppPurchaseData());
break;
default:
break;
}
return;
}
}
消耗
/**
* Deliver the product.
*
* @param inAppPurchaseDataStr Includes the purchase details.
* @param
*/
public void deliverProduct(final String inAppPurchaseDataStr) {
// final String inAppPurchaseDataSignature
try {
InAppPurchaseData inAppPurchaseDataBean = new InAppPurchaseData(inAppPurchaseDataStr);
if (inAppPurchaseDataBean.getPurchaseState() != InAppPurchaseData.PurchaseState.PURCHASED) {
return;
}
String purchaseToken = inAppPurchaseDataBean.getPurchaseToken();
String productId = inAppPurchaseDataBean.getProductId();
IapRequestHelper.consumeOwnedPurchase(mClient, purchaseToken);
} catch (JSONException e) {
Log.e(TAG, "delivery:" + e.getMessage());
}
}
具体消耗逻辑
/**
* Consume all the unconsumed purchases with priceType 0.
* @param iapClient IapClient instance to call the consumeOwnedPurchase API.
* @param purchaseToken which is generated by the Huawei payment server during product payment and returned to the app through InAppPurchaseData.
*/
public static void consumeOwnedPurchase(IapClient iapClient, String purchaseToken) {
Log.i(TAG, "call consumeOwnedPurchase");
Task<ConsumeOwnedPurchaseResult> task = iapClient.consumeOwnedPurchase(createConsumeOwnedPurchaseReq(purchaseToken));
task.addOnSuccessListener(new OnSuccessListener<ConsumeOwnedPurchaseResult>() {
@Override
public void onSuccess(ConsumeOwnedPurchaseResult result) {
Log.i(TAG, "consumeOwnedPurchase success");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof IapApiException) {
IapApiException apiException = (IapApiException)e;
int returnCode = apiException.getStatusCode();
Log.e(TAG, "consumeOwnedPurchase fail, IapApiException returnCode: " + returnCode);
} else {
// Other external errors
Log.e(TAG, e.getMessage());
}
}
});
}
查询未消耗逻辑(补单逻辑)
/**
* Call the obtainOwnedPurchases API to obtain the data about consumable
* products that the user has purchased but has not been delivered.
* 查询未消耗的逻辑
*
*
*
*/
public void queryPurchases(String continuationToken) {
final String tag = "obtainOwnedPurchases";
IapRequestHelper.obtainOwnedPurchases(mClient, IapClient.PriceType.IN_APP_CONSUMABLE,
continuationToken, new IapApiCallback<OwnedPurchasesResult>() {
@Override
public void onSuccess(OwnedPurchasesResult result) {
if (result == null) {
Log.e(TAG, tag + " result is null");
return;
}
Log.i(TAG, "obtainOwnedPurchases, success");
if (result.getInAppPurchaseDataList() != null) {
List<String> inAppPurchaseDataList = result.getInAppPurchaseDataList();
List<String> inAppSignature= result.getInAppSignature();
for (int i = 0; i < inAppPurchaseDataList.size(); i++) {
final String inAppPurchaseData = inAppPurchaseDataList.get(i);
final String inAppPurchaseDataSignature = inAppSignature.get(i);
deliverProduct(inAppPurchaseData);
}
}
if (!TextUtils.isEmpty(result.getContinuationToken())) {
queryPurchases(result.getContinuationToken());
}
}
@Override
public void onFail(Exception e) {
Log.e(TAG, "obtainOwnedPurchases, type=" + IapClient.PriceType.IN_APP_CONSUMABLE + ", " + e.getMessage());
ExceptionHandle.handle(context, e);
}
});
}
完整Android端代码
package com.example.helloworld;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.example.helloworld.huaweipay.OjjlgHuaWeiBilling;
import com.example.helloworld.huaweipay.OjjlgHuaweiPay;
import ohos.stage.ability.adapter.StageActivity;
/**
* Example ace activity class, which will load ArkUI-X ability instance.
* StageActivity is provided by ArkUI-X
* @see <a href=
* "https://gitee.com/arkui-crossplatform/doc/blob/master/contribute/tutorial/how-to-build-Android-app.md">
* to build android library</a>
*/
public class EntryEntryAbilityActivity extends StageActivity {
private static final String TAG = "MainActivity";
private Context context=EntryEntryAbilityActivity.this;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.e("HiHelloWorld", "EntryEntryAbilityActivity");
new Bridge(this, "Bridge", getInstanceId());
//setInstanceName("com.example.helloworld:entry:EntryAbility:");
setInstanceName("com.example.helloworld:entry:EntryAbility:");
super.onCreate(savedInstanceState);
OjjlgHuaweiPay.getInstance().initHuaweiPay((Activity) context);
// GooglePay.getInstance().initgooglePlay(EntryEntryAbilityActivity.this);
}
@Override
protected void onResume() {
super.onResume();
// GooglePay.getInstance().queryPurchasesAsync(EntryEntryAbilityActivity.this);
}
@Override
public void onStop() {
super.onStop();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
OjjlgHuaWeiBilling.getInstance().onActivityResult(requestCode,resultCode,intent);
}
}
OjjlgHuaweiPay 代码
package com.example.helloworld.huaweipay;
import android.app.Activity;
/**
* 作者:xuqing
* 时间:2024年4月16日13:50:31
* 邮箱:1693891473@qq.com
* 说明:华为支付
*/
public class OjjlgHuaweiPay {
private static OjjlgHuaweiPay instance=null;
private OjjlgHuaweiPay() {
}
public static OjjlgHuaweiPay getInstance(){
if(instance==null){
synchronized (OjjlgHuaweiPay.class){
if(instance==null){
instance=new OjjlgHuaweiPay();
}
}
}
return instance;
}
public void initHuaweiPay(Activity context){
OjjlgHuaWeiBilling.getInstance().queryIsReady(context);
}
public void fucnxPayInApp(Activity activity, String productId,String token ){
if(productId == null){
// NbzgmLog.qecrnVUnControl("clwuyPayInApp payParmas null");
return;
}
if(!OjjlgHuaWeiBilling.getInstance().isReady()|| activity ==null) {
// NbzgmLog.qecrnVUnControl("clwuybuyInApp googleBillingUtil || mActivity null");
initHuaweiPay(activity);
return;
}
OjjlgHuaWeiBilling.getInstance().queryProducts(activity,productId,token);
}
}
package com.example.helloworld.huaweipay;
import static com.example.helloworld.huaweipay.Constants.REQ_CODE_BUY;
import android.app.Activity;
import android.content.Intent;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import com.huawei.hms.iap.Iap;
import com.huawei.hms.iap.IapClient;
import com.huawei.hms.iap.entity.InAppPurchaseData;
import com.huawei.hms.iap.entity.IsEnvReadyResult;
import com.huawei.hms.iap.entity.OrderStatusCode;
import com.huawei.hms.iap.entity.OwnedPurchasesResult;
import com.huawei.hms.iap.entity.ProductInfo;
import com.huawei.hms.iap.entity.ProductInfoResult;
import com.huawei.hms.iap.entity.PurchaseIntentResult;
import com.huawei.hms.iap.entity.PurchaseResultInfo;
import org.json.JSONException;
import java.util.ArrayList;
import java.util.List;
/**
* 作者:xuqing
* 时间:2024年3月5日17:36:50
* 邮箱:1693891473@qq.com
* 说明:华为支付
*
*
*/
public class OjjlgHuaWeiBilling {
private static final String TAG = "OjjlgHuaWeiBilling";
private IapClient mClient;
private Activity context;
public int retryTimes = 0;
private static OjjlgHuaWeiBilling instance=null;
private OjjlgHuaWeiBilling() {
}
public static OjjlgHuaWeiBilling getInstance(){
if(instance==null){
synchronized (OjjlgHuaWeiBilling.class){
if(instance==null){
instance=new OjjlgHuaWeiBilling();
}
}
}
return instance;
}
/**
* @param context 链接华为服务器
*/
public void queryIsReady(Activity context) {
this.context = context;
mClient = Iap.getIapClient(context);
IapRequestHelper.isEnvReady(mClient, new IapApiCallback<IsEnvReadyResult>() {
@Override
public void onSuccess(IsEnvReadyResult result) {
Log.e(TAG, "onSuccess: " + "检查支付成功 --- > ");
//initview();
}
@Override
public void onFail(Exception e) {
Log.e(TAG, "isEnvReady fail, " + e.getMessage());
ExceptionHandle.handle(context, e);
}
});
}
/**
* google内购服务是否已经准备好
*
* @return boolean
*/
public boolean isReady() {
return mClient != null;
}
/**
* @param productId
* @param mContext 查询商品ID
*/
public void queryProducts( Activity mContext,final String productId,String token) {
Log.e(TAG, "queryProducts: productId -- >"+productId );
if(mClient!=null){
List<String> productIds = new ArrayList<String>();
productIds.add(productId);
IapRequestHelper.obtainProductInfo(mClient, productIds, IapClient.PriceType.IN_APP_CONSUMABLE,
new IapApiCallback<ProductInfoResult>() {
@Override
public void onSuccess(ProductInfoResult result) {
Log.i(TAG, "obtainProductInfo, success");
if (result == null) {
return;
}
Log.e(TAG, "onSuccess:result.getReturnCode() -- > "+result.getReturnCode() );
// if(result.getReturnCode()==0){
if (result.getProductInfoList() != null&&result.getProductInfoList().size()>0) {
List<ProductInfo> productInfoList=result.getProductInfoList();
for(ProductInfo productInfo:productInfoList){
String product_id=productInfo.getProductId(); //商品id
String product_name=productInfo.getProductName();// 商品名称
String product_price=productInfo.getPrice();// 商品金额
String currency_code=productInfo.getCurrency();// 币种
buy((Activity) mContext,productId,token);
}
}else {
Log.e(TAG, "onSuccess: " + result.getErrMsg() + result.getReturnCode());
Log.e(TAG, "onSuccess: " + "商品列表为空");
Toast.makeText(mContext, "商品列表为空", Toast.LENGTH_SHORT);
}
}
@Override
public void onFail(Exception e) {
Log.e(TAG, "obtainProductInfo: " + e.getMessage());
}
});
}
}
/***
*
* @param productId
* @param
*
*
*
*/
public void buy(final Activity activity, String productId, String token) {
IapRequestHelper.createPurchaseIntent(mClient, productId, IapClient.PriceType.IN_APP_CONSUMABLE,token,
new IapApiCallback<PurchaseIntentResult>() {
@Override
public void onSuccess(PurchaseIntentResult result) {
if (result == null) {
Log.e(TAG, "result is null");
return;
}
IapRequestHelper.startResolutionForResult(activity, result.getStatus(), REQ_CODE_BUY);
}
@Override
public void onFail(Exception e) {
int errorCode = ExceptionHandle.handle(activity, e);
if (errorCode != ExceptionHandle.SOLVED) {
Log.i(TAG, "createPurchaseIntent, returnCode: " + errorCode);
// Handle error scenarios.
switch (errorCode) {
case OrderStatusCode.ORDER_PRODUCT_OWNED:
// queryPurchases(null);
break;
default:
break;
}
}
}
});
}
/**
*
*
* @param requestCode
* @param resultCode
* @param data
* 华为内购支付回调
*/
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.e(TAG, "onActivityResult");
if (requestCode == REQ_CODE_BUY) {
if (data == null) {
Log.e(TAG, "data is null");
return;
}
PurchaseResultInfo purchaseResultInfo = Iap.getIapClient(context).parsePurchaseResultInfoFromIntent(data);
switch(purchaseResultInfo.getReturnCode()) {
case OrderStatusCode.ORDER_STATE_CANCEL:
Log.e(TAG, "onActivityResult:ORDER_STATE_CANCEL " );
Toast.makeText(context, " ORDER_STATE_CANCEL Order has been canceled!", Toast.LENGTH_SHORT).show();
break;
case OrderStatusCode.ORDER_STATE_FAILED:
Log.e(TAG, "onActivityResult: ORDER_STATE_FAILED ");
case OrderStatusCode.ORDER_PRODUCT_OWNED:
Log.e(TAG, "onActivityResult: ORDER_PRODUCT_OWNED ");
queryPurchases(null);
break;
case OrderStatusCode.ORDER_STATE_SUCCESS:
Log.e(TAG, "onActivityResult: ORDER_STATE_SUCCESS ");
//先去服务器验签 然后再消耗
deliverProduct(purchaseResultInfo.getInAppPurchaseData());
break;
default:
break;
}
return;
}
}
/**
* Deliver the product.
*
* @param inAppPurchaseDataStr Includes the purchase details.
* @param
*/
public void deliverProduct(final String inAppPurchaseDataStr) {
// final String inAppPurchaseDataSignature
try {
InAppPurchaseData inAppPurchaseDataBean = new InAppPurchaseData(inAppPurchaseDataStr);
if (inAppPurchaseDataBean.getPurchaseState() != InAppPurchaseData.PurchaseState.PURCHASED) {
return;
}
String purchaseToken = inAppPurchaseDataBean.getPurchaseToken();
String productId = inAppPurchaseDataBean.getProductId();
IapRequestHelper.consumeOwnedPurchase(mClient, purchaseToken);
} catch (JSONException e) {
Log.e(TAG, "delivery:" + e.getMessage());
}
}
/**
* Call the obtainOwnedPurchases API to obtain the data about consumable
* products that the user has purchased but has not been delivered.
* 查询未消耗的逻辑
*
*
*
*/
public void queryPurchases(String continuationToken) {
final String tag = "obtainOwnedPurchases";
IapRequestHelper.obtainOwnedPurchases(mClient, IapClient.PriceType.IN_APP_CONSUMABLE,
continuationToken, new IapApiCallback<OwnedPurchasesResult>() {
@Override
public void onSuccess(OwnedPurchasesResult result) {
if (result == null) {
Log.e(TAG, tag + " result is null");
return;
}
Log.i(TAG, "obtainOwnedPurchases, success");
if (result.getInAppPurchaseDataList() != null) {
List<String> inAppPurchaseDataList = result.getInAppPurchaseDataList();
List<String> inAppSignature= result.getInAppSignature();
for (int i = 0; i < inAppPurchaseDataList.size(); i++) {
final String inAppPurchaseData = inAppPurchaseDataList.get(i);
final String inAppPurchaseDataSignature = inAppSignature.get(i);
deliverProduct(inAppPurchaseData);
}
}
if (!TextUtils.isEmpty(result.getContinuationToken())) {
queryPurchases(result.getContinuationToken());
}
}
@Override
public void onFail(Exception e) {
Log.e(TAG, "obtainOwnedPurchases, type=" + IapClient.PriceType.IN_APP_CONSUMABLE + ", " + e.getMessage());
ExceptionHandle.handle(context, e);
}
});
}
}
最后总结
鸿蒙arkui-x才是刚起步希望有更多开发者能加入进来一起共同建设完善好生态。也希望国产系统和框架越来越好 最后呢 希望我都文章能帮助到各位同学工作和学习 如果你觉得文章还不错麻烦给我三连 关注点赞和转发 谢谢