左晓为主开发手持机充值管理机
zuoxiao
15 小时以前 2b02b6e854a56a511588e4865ddf2c6597675329
feat(nfc): 添加读卡功能并优化写卡流程

- 新增 CardReadActivity 用于读取 NFC 卡信息
- 更新 BSCardFragment 添加读卡入口- 重构 NfcReadHelper 和相关类以支持读卡功能- 优化 NfcWreatActivity 和 RechargeDetailActivity 以适应新的读卡流程
- 在 AndroidManifest 中添加 CardReadActivity 并配置 NFC 相关权限
8个文件已修改
2个文件已添加
842 ■■■■■ 已修改文件
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/BaseNfcReadHelper.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/NativeNfcReadHelper.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/NfcReadAdapter.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/AndroidManifest.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/BSCardFragment.kt 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/CardReadActivity.kt 324 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/NfcWreatActivity.kt 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/RechargeDetailActivity.kt 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/tool/NfcReadHelper.kt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/layout/activity_card_read.xml 375 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/BaseNfcReadHelper.java
@@ -52,7 +52,7 @@
     * @return
     */
    public  String getCradTypeAndCardNumber(){
        return getCradTypeAndCardNumber(1);
        return getCradTypeAndCardNumber(1,0,0);
    };
    /**
@@ -60,7 +60,7 @@
     *
     * @return
     */
    public  String getCradTypeAndCardNumber(int sectorIndex){
    public  String getCradTypeAndCardNumber(int sectorIndex,int blockIndex,int cardTypeIndex){
        return null;
    };
@@ -82,12 +82,18 @@
    public abstract List<byte[]> getOnesectorData();
    public  BaseUserCardCard getUserCardData(BaseUserCardCard userCardCard){
        return getUserCardData(1,userCardCard);
    };
    /**
     * 同步获取用户卡信息
     *
     * @return
     */
    public abstract BaseUserCardCard getUserCardData(BaseUserCardCard userCardCard);
    public  BaseUserCardCard getUserCardData(int sectorIndex,BaseUserCardCard userCardCard){
        return null;
    }
    public BaseManagerToUserCard getManagerToUserCardData(BaseManagerToUserCard baseManagerToUserCard) {
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/NativeNfcReadHelper.java
@@ -53,13 +53,18 @@
        return helper;
    }
    public BaseUserCardCard getUserCardData( BaseUserCardCard userCardCard) {
        return getUserCardData(1, userCardCard);
    }
    /**
     * 获取用户卡信息
     *
     * @return
     */
    @Override
    public BaseUserCardCard getUserCardData(BaseUserCardCard userCardCard) {
    public BaseUserCardCard getUserCardData(int sectorIndex, BaseUserCardCard userCardCard) {
        if (userCardCard != null) {
            BaseUserCardCard userCard = null;
            Map<String, List<byte[]>> map = new HashMap<>();
@@ -68,26 +73,31 @@
                try {
                    //链接NFC
                    mfc.connect();
                    //获取扇区数量
                    int count = mfc.getSectorCount();
                    //存储空间
                    int size = mfc.getSize();
                    //用于判断时候有内容读取出来
                    boolean flag = false;
                    List<byte[]> list = new ArrayList<>();
                    //验证扇区密码,否则会报错(链接失败错误)
                    boolean isOpen = false;
                    for (int i = 0; i < listKeyA.size(); i++) {
                        if (mfc.authenticateSectorWithKeyA(1, listKeyA.get(i))) {
                    if (!listKeyA.isEmpty()) {
                        for (int i = 0; i < listKeyA.size(); i++) {
                            if (mfc.authenticateSectorWithKeyA(sectorIndex, listKeyA.get(i))) {
                                isOpen = true;
                                break;
                            }
                        }
                    } else if (!listA_PS.isEmpty()) {
                        if (mfc.authenticateSectorWithKeyA(sectorIndex, defauleKey)) {
                            isOpen = true;
                            break;
                        } else if (mfc.authenticateSectorWithKeyA(sectorIndex, listA_PS.get(sectorIndex))) {
                            isOpen = true;
                        }
                    }
                    if (isOpen) {
                        //获取扇区里面块的数量
                        int bCount = mfc.getBlockCountInSector(1);
                        int bCount = mfc.getBlockCountInSector(sectorIndex);
                        //获取扇区第一个块对应芯片存储器的位置(我是这样理解的,因为第0扇区的这个值是4而不是0)
                        int bIndex = mfc.sectorToBlock(1);
                        int bIndex = mfc.sectorToBlock(sectorIndex);
                        //String data1 = "";
                        for (int j = 0; j < bCount; j++) {
                            //读取数据
@@ -482,7 +492,7 @@
    @Override
    public String getCradTypeAndCardNumber() {
        return getCradTypeAndCardNumber(1);
        return getCradTypeAndCardNumber(1, 0, 0);
    }
@@ -492,7 +502,7 @@
     * @return
     */
    public String getCradTypeAndCardNumber(int sectorIndex) {
    public String getCradTypeAndCardNumber(int sectorIndex, int blockIndex, int cardTypeIndex) {
        MifareClassic mfc = MifareClassic.get(tag);
        if (null != mfc) {
@@ -544,9 +554,9 @@
                }
                if (isOpen) {
                    int bIndex = mfc.sectorToBlock(sectorIndex);
                    byte[] data = mfc.readBlock(bIndex + sectorIndex);
                    byte[] data = mfc.readBlock(bIndex + 0);
                    if (data != null && data.length > 0) {
                        String hex = HexUtil.byteToHex(data[0]);
                        String hex = HexUtil.byteToHex(data[cardTypeIndex]);
                        strData.append(hex);
                        Log.i("NFCWreatActivity", "hex===" + hex);
                        return strData.toString().toUpperCase();
@@ -556,6 +566,7 @@
                    return BaseCommon.CARD_TYPE_ERROR2;
                }
            } catch (IOException e) {
                e.printStackTrace();
                return BaseCommon.CARD_TYPE_ERROR1;
            } finally {
                try {
@@ -693,8 +704,6 @@
        }
        return null;
    }
}
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/NfcReadAdapter.java
@@ -66,11 +66,11 @@
        }
    }
    @Override
    public String getCardNumber() {
        return getCardNumber(false);
    }
    public String getCardNumber(boolean isChangePS) {
@@ -101,10 +101,10 @@
    }
    @Override
    public String getCradTypeAndCardNumber(int sectorIndex) {
    public String getCradTypeAndCardNumber(int sectorIndex, int blockIndex, int cardTypeIndex) {
        switch (BaseNfcActivity.adapterType) {
            case ModelUtils.defaultType:
                return nativeNfcReadHelper.getCradTypeAndCardNumber(sectorIndex);
                return nativeNfcReadHelper.getCradTypeAndCardNumber(sectorIndex, blockIndex, cardTypeIndex);
        }
        return "";
    }
@@ -138,7 +138,12 @@
        return null;
    }
    @Override
    public BaseUserCardCard getUserCardData(int sectorIndex, BaseUserCardCard userCardCard) {
        switch (BaseNfcActivity.adapterType) {
            case ModelUtils.defaultType:
                return nativeNfcReadHelper.getUserCardData(sectorIndex, userCardCard);
        }
        return null;
    }
}
generallibrary/src/main/AndroidManifest.xml
@@ -92,7 +92,18 @@
        <!-- 写卡成功页面 -->
        <activity android:name=".activity.CardWriteSuccessActivity" />
        <!-- 挂失页面 -->
        <activity android:name=".activity.LossCardActivity"/>
        <activity android:name=".activity.LossCardActivity" />
        <!-- 读卡页面 -->
        <activity
            android:name=".activity.CardReadActivity"
            android:exported="false"
            android:launchMode="singleTop">
            <intent-filter>
                <action android:name="android.nfc.action.ACTION_NDEF_DISCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="text/plain" />
            </intent-filter>
        </activity>
        <meta-data
            android:name="BUGLY_APP_VERSION"
generallibrary/src/main/java/com/dayu/general/activity/BSCardFragment.kt
@@ -51,6 +51,9 @@
            }
            startActivity(intent)
        }
        binding.homeRedCard.setOnClickListener {
            context?.let { CardReadActivity.start(it) }
        }
    }
}
generallibrary/src/main/java/com/dayu/general/activity/CardReadActivity.kt
New file
@@ -0,0 +1,324 @@
package com.dayu.general.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.dayu.baselibrary.net.subscribers.SubscriberListener
import com.dayu.baselibrary.utils.ToastUtil
import com.dayu.baselibrary.view.TipDialog
import com.dayu.baselibrary.view.TitleBar
import com.dayu.general.bean.net.CardInfoResult
import com.dayu.general.databinding.ActivityCardReadBinding
import com.dayu.general.net.ApiManager
import com.dayu.general.net.BaseResponse
import com.dayu.general.tool.NfcReadHelper
import com.dayu.general.bean.card.UserCard
import com.dayu.general.tool.CardCommon
/**
 * @author: zuo
 * @desc: 读卡Activity
 * @since: 2025/3/6
 */
class CardReadActivity : BaseNfcActivity() {
    private lateinit var binding: ActivityCardReadBinding
    private var cardNumber: String? = null
    companion object {
        /**
         * 启动读卡Activity
         */
        fun start(context: Context) {
            val intent = Intent(context, CardReadActivity::class.java)
            context.startActivity(intent)
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityCardReadBinding.inflate(layoutInflater)
        setContentView(binding.root)
        initView()
    }
    private fun initView() {
        // 设置标题
        binding.titleBar.setCenterText("读卡")
        // 设置TitleBar的返回按钮点击事件
        binding.titleBar.setOnItemclickListner(TitleBar.ClickType_LEFT_IMAGE) {
            finish()
        }
    }
    /**
     * 重置到读卡状态
     */
    private fun resetToReadingState() {
        binding.cardReadLL.visibility = android.view.View.VISIBLE
        binding.cardInfoContainer.visibility = android.view.View.GONE
        cardNumber = null
    }
    /**
     * 显示确认对话框
     */
    private fun showConfirmDialog(message: String, onConfirm: () -> Unit) {
        val confirmDialog = TipDialog(this, message) {
            onConfirm()
        }
        confirmDialog.show()
    }
    override fun onNfcBack(intent: Intent?) {
        intent?.let {
            handleNfcIntent(it)
        } ?: run {
            showConfirmDialog("NFC数据异常,请重新刷卡") {
            }
        }
    }
    /**
     * 处理NFC刷卡信息
     */
    private fun handleNfcIntent(intent: Intent) {
        try {
            // 检查intent中是否包含NFC Tag
            if (intent.getParcelableExtra<android.nfc.Tag>(android.nfc.NfcAdapter.EXTRA_TAG) == null) {
                showConfirmDialog("未检测到NFC卡片,请确保卡片已正确放置") {
                }
                return
            }
            val nfcAdapter = NfcReadHelper.getInstance(intent, this)
            val cardTypeAndCardNumber = nfcAdapter.getCardTypeAndCardNumber()
            if (cardTypeAndCardNumber.isNullOrBlank() || !cardTypeAndCardNumber.contains(",")) {
                showConfirmDialog("卡片信息读取失败,请重新刷卡") {
                }
                return
            }
            val parts = cardTypeAndCardNumber.split(",")
            if (parts.size < 2) {
                showConfirmDialog("卡片信息格式异常,请重新刷卡") {
                }
                return
            }
            val cardNumber = parts[0]
            val cardType = parts[1]
            this.cardNumber = cardNumber
            if (cardNumber.isBlank()) {
                showConfirmDialog("卡号为空,无法进行操作,请重新刷卡") {
                }
                return
            }
            // 根据卡片类型进行不同处理
            when (cardType) {
                CardCommon.USER_CARD_TYPE_1,
                CardCommon.USER_CARD_TYPE_2,
                CardCommon.USER_CARD_TYPE_3 -> {
                    // 用户卡:解析卡内数据并调用接口
                    handleUserCard(cardNumber, cardType, nfcAdapter)
                }
                else -> {
                    // 管理类卡:显示卡片类型信息
                    handleManagementCard(cardNumber, cardType)
                }
            }
        } catch (e: Exception) {
            showConfirmDialog("读卡异常:${e.message}") {
            }
            e.printStackTrace()
        }
    }
    /**
     * 处理用户卡
     */
    private fun handleUserCard(cardNumber: String, cardType: String, nfcAdapter: NfcReadHelper) {
        // 解析用户卡数据
        val userCard = nfcAdapter.getUserCardData()
        if (userCard == null) {
            showConfirmDialog("解析卡片数据失败,请重新刷卡") {
            }
            return
        }
        // 根据卡号获取卡片详细信息
        getCardInfo(cardNumber, cardType, userCard)
    }
    /**
     * 处理管理类卡片
     */
    private fun handleManagementCard(cardNumber: String, cardType: String) {
        val cardTypeName = getCardTypeName(cardType)
        // 显示管理卡信息
        showManagementCardInfo(cardNumber, cardTypeName)
    }
    /**
     * 显示管理类卡片信息
     */
    private fun showManagementCardInfo(cardNumber: String, cardTypeName: String) {
        // 隐藏读卡提示,显示信息区域
        binding.cardReadLL.visibility = android.view.View.GONE
        binding.cardInfoContainer.visibility = android.view.View.VISIBLE
        // 显示基本信息
        binding.tvCardNumber.text = cardNumber
        binding.tvCardType.text = cardTypeName
        // 隐藏用户卡特有的字段
        binding.llCardBalance.visibility = android.view.View.GONE
        binding.llUserNumber.visibility = android.view.View.GONE
        binding.llCardStatus.visibility = android.view.View.GONE
        binding.llLastUseTime.visibility = android.view.View.GONE
    }
    /**
     * 获取卡片类型名称
     */
    private fun getCardTypeName(cardType: String): String {
        return when (cardType) {
            CardCommon.USER_CARD_TYPE_1 -> "用户卡(开泵前)"
            CardCommon.USER_CARD_TYPE_2 -> "用户卡(开泵后)"
            CardCommon.USER_CARD_TYPE_3 -> "用户卡(叠加充值)"
            CardCommon.REGION_CARD -> "区域表号卡"
            CardCommon.ELECTRIC_PRICE_CARD -> "取数卡(需要刷卡取数)"
            CardCommon.MANAGE_CRAD -> "取数卡(刷卡取数返写成功)"
            CardCommon.CHECK_CARD -> "检查卡"
            CardCommon.DEBUG_CARD -> "调试卡"
            CardCommon.CLEAN_CARD_TYPE -> "清零卡"
            CardCommon.IP_CARD -> "IP地址设置卡"
            CardCommon.AREA_CARD -> "区域设置卡"
            CardCommon.GPS_CARD -> "GPS设置卡"
            CardCommon.VALVE_TIME_CARD -> "开关阀时间配置卡"
            else -> "未知类型卡片($cardType)"
        }
    }
    /**
     * 获取卡片详细信息(用户卡专用)
     */
    private fun getCardInfo(cardNumber: String, cardType: String, userCard: UserCard) {
        val map = mutableMapOf<String, Any>()
        map["cardAddr"] = cardNumber
        ApiManager.getInstance().requestGetLoading(
            this,
            "terminal/card/readCard",
            CardInfoResult::class.java,
            map,
            object : SubscriberListener<BaseResponse<CardInfoResult>>() {
                override fun onNext(t: BaseResponse<CardInfoResult>) {
                    if (t.success) {
                        // 读卡成功,显示用户卡详细信息
                        showUserCardInfo(t.content, cardNumber, cardType, userCard)
                    } else {
                        // 处理获取失败的情况
                        handleCardInfoError(t.code, t.msg)
                    }
                }
                override fun onError(e: Throwable?) {
                    super.onError(e)
                    showConfirmDialog("获取卡信息失败: ${e?.message ?: "网络异常,请检查网络连接"}") {
                    }
                }
            }
        )
    }
    /**
     * 显示用户卡片信息(包含卡内数据和接口返回数据)
     */
    private fun showUserCardInfo(cardInfo: CardInfoResult?, cardNumber: String, cardType: String, userCard: UserCard) {
        // 隐藏读卡提示,显示信息区域
        binding.cardReadLL.visibility = android.view.View.GONE
        binding.cardInfoContainer.visibility = android.view.View.VISIBLE
        val cardTypeName = getCardTypeName(cardType)
        // 显示卡内数据
        binding.tvCardNumber.text = cardNumber
        binding.tvCardType.text = cardTypeName
        // 显示用户卡特有字段
        binding.llCardBalance.visibility = android.view.View.VISIBLE
        binding.llUserNumber.visibility = android.view.View.VISIBLE
        binding.llCardStatus.visibility = android.view.View.VISIBLE
        userCard.let { card ->
            // 余额转换为元(原始数据可能是分)
            val balanceInYuan = if (card.balance > 1000) {
                String.format("%.2f", card.balance / 100.0)
            } else {
                card.balance.toString()
            }
            binding.tvCardBalance.text = "${balanceInYuan}元"
            // 使用完整的用户编号
            binding.tvUserNumber.text = card.getMyUserCode()
            // 卡片状态(假设正常状态,因为UserCard中没有状态字段)
            binding.tvCardStatus.text = "正常"
            binding.tvCardStatus.setTextColor(android.graphics.Color.parseColor("#4CAF50"))
            // 充值时间作为最后使用时间
            if (card.rechargeDate != null) {
                binding.llLastUseTime.visibility = android.view.View.VISIBLE
                val dateFormat = java.text.SimpleDateFormat("yyyy-MM-dd HH:mm", java.util.Locale.getDefault())
                binding.tvLastUseTime.text = dateFormat.format(card.rechargeDate!!.time)
            } else {
                binding.llLastUseTime.visibility = android.view.View.GONE
            }
        }
        // 显示服务器数据
        cardInfo?.let { info ->
            binding.tvUserName.text = info.userName ?: "未知"
            binding.tvPhone.text = info.phone ?: "未绑定"
            binding.tvIdCard.text = info.userCode ?: "未录入"  // 使用userCode作为身份证号的替代
            // 根据status字段显示状态
            val statusText = when (info.status) {
                1 -> "正常"
                2 -> "挂失"
                3 -> "锁定"
                else -> "未知"
            }
        }
    }
    /**
     * 处理卡信息获取错误
     */
    private fun handleCardInfoError(code: String?, msg: String?) {
        val errorMessage: String = when (code) {
            "1001" -> {
                "该卡片未在系统中注册,请先进行开卡操作。"
            }
            else -> {
                when {
                    msg.isNullOrBlank() -> "获取卡信息失败,请重新刷卡重试。"
                    msg.contains("数据不存在") -> "该卡片未在系统中注册,请先进行开卡操作。"
                    msg.contains("网络") -> "网络连接异常,请检查网络连接后重新刷卡。"
                    msg.contains("超时") -> "网络请求超时,请重新刷卡重试。"
                    else -> "获取卡信息失败:$msg\n\n请重新刷卡重试。"
                }
            }
        }
        // 显示错误信息的对话框
        showConfirmDialog(errorMessage) {
            resetToReadingState()
        }
    }
}
generallibrary/src/main/java/com/dayu/general/activity/NfcWreatActivity.kt
@@ -30,6 +30,10 @@
    var cardType = ""
    var cardAddr = ""
    var cardFee = 0
    // 充值相关金额
    private var rechargeAmount = 0.0
    private var bonusAmount = 0.0
    //订单编号
    var orderNumber = ""
@@ -53,6 +57,11 @@
        operationTypeCode = intent?.getIntExtra("operationTypeCode", -1) ?: -1
        orderNumber = intent?.getStringExtra("orderNumber") ?: ""
        operationType = CardOperationType.fromCode(operationTypeCode)
        // 获取充值相关金额
        rechargeAmount = intent?.getDoubleExtra("rechargeAmount", 0.0) ?: 0.0
        bonusAmount = intent?.getDoubleExtra("bonusAmount", 0.0) ?: 0.0
        if (intent?.hasExtra("cardFee") == true) {
            cardFee = intent?.getIntExtra("cardFee", 0) ?: 0
        }
@@ -86,12 +95,23 @@
                CardOperationType.Recharge -> {
                    var textData = StringBuilder()
                    textData.append("用户充值\n")
                    textData.append("订单号:" + orderNumber + "\n")
                    if (userCard.balance != 0) {
                        val balanceInYuan = userCard.balance / 100.0 // 转换为元
                        textData.append("充值金额:" + String.format("%.2f", balanceInYuan) + "元")
                    // 显示充值金额
                    if (rechargeAmount > 0) {
                        textData.append("充值金额:" + String.format("%.2f", rechargeAmount) + "元\n")
                    }
                    // 显示赠送金额
                    if (bonusAmount > 0) {
                        textData.append("赠送金额:" + String.format("%.2f", bonusAmount) + "元\n")
                    }
                    // 显示总金额(写入卡内的总余额)
                    if (userCard.balance != 0) {
                        val totalBalanceInYuan = userCard.balance / 100.0 // 转换为元
                        textData.append("卡内总余额:" + String.format("%.2f", totalBalanceInYuan) + "元")
                    }
                    binding?.cardData?.text = textData.toString()
                }
generallibrary/src/main/java/com/dayu/general/activity/RechargeDetailActivity.kt
@@ -25,6 +25,7 @@
import com.dayu.general.databinding.ActivityRechargeDetailBinding
import com.dayu.general.net.ApiManager
import com.dayu.general.net.BaseResponse
import com.dayu.general.tool.CardCommon.Companion.USER_CARD_TYPE_1
import com.dayu.general.tool.CardOperationType
class RechargeDetailActivity : AppCompatActivity() {
@@ -395,7 +396,13 @@
                // 设置用户卡信息
                cardInfo?.let { info ->
                    userCode = info.cardNum ?: ""
                    balance = ((rechargeAmount + bonusAmount) * 100).toInt() // 转换为分
                    // 计算新余额:原有余额 + 充值金额 + 赠送金额
                    val originalBalance = this@RechargeDetailActivity.userCard?.balance ?: 0 // 原有余额(分)
                    val rechargeAmountInCents = (rechargeAmount * 100).toInt() // 充值金额转分
                    val bonusAmountInCents = (bonusAmount * 100).toInt() // 赠送金额转分
                    balance = originalBalance + rechargeAmountInCents + bonusAmountInCents
                }
                
                // 设置其他必要信息
@@ -406,22 +413,16 @@
            // 启动写卡Activity
            val intent = Intent(this, NfcWreatActivity::class.java).apply {
                putExtra("cardType", "USER_CARD") // 用户卡类型
                putExtra("cardType", USER_CARD_TYPE_1) // 用户卡类型
                putExtra("cardAddr", cardAddress)
                putExtra("operationTypeCode", CardOperationType.Recharge.code)
                putExtra("orderNumber", rechargeResult.orderNo)
                putExtra("userCard", userCard)
                putExtra("rechargeAmount", rechargeAmount) // 传递充值金额
                putExtra("bonusAmount", bonusAmount) // 传递赠送金额
            }
            startActivity(intent)
            // 显示成功信息
            val formattedRecharge = String.format("%.2f", rechargeAmount)
            val formattedBonus = String.format("%.2f", bonusAmount)
            val formattedTotal = String.format("%.2f", rechargeAmount + bonusAmount)
            ToastUtil.show("充值订单创建成功\n订单号: ${rechargeResult.orderNo}\n充值金额: ${formattedRecharge}元\n赠送金额: ${formattedBonus}元\n总金额: ${formattedTotal}元\n请贴卡进行写卡操作")
        } catch (e: Exception) {
            ToastUtil.show("启动写卡界面失败: ${e.message}")
        }
generallibrary/src/main/java/com/dayu/general/tool/NfcReadHelper.kt
@@ -166,7 +166,7 @@
     */
    fun getCardTypeAndCardNumber(): String {
        return try {
            adapter.getCradTypeAndCardNumber(7)
            adapter.getCradTypeAndCardNumber(7,0,8)
        } catch (e: Exception) {
            e.printStackTrace()
            ""
@@ -324,7 +324,7 @@
    fun getUserCardData(): UserCard? {
        return try {
            // 获取基础卡数据
            val baseCard = adapter.getUserCardData(UserCard())
            val baseCard = adapter.getUserCardData(7,UserCard())
            // 如果获取成功且是UserCard类型,则返回
            if (baseCard is UserCard) {
                baseCard
generallibrary/src/main/res/layout/activity_card_read.xml
New file
@@ -0,0 +1,375 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/base_green_bg">
    <com.dayu.baselibrary.view.TitleBar
        android:id="@+id/titleBar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dimen_title_height"
        android:background="@color/title_bar_bg"
        android:elevation="4dp"
        app:centerText="读卡" />
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/titleBar"
        android:fillViewport="true">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:padding="16dp">
            <!-- 读卡提示区域 -->
            <LinearLayout
                android:id="@+id/card_read_LL"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:orientation="vertical"
                android:visibility="gone">
                <androidx.cardview.widget.CardView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_marginBottom="16dp"
                    app:cardCornerRadius="8dp"
                    app:cardElevation="2dp">
                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:gravity="center"
                        android:orientation="vertical"
                        android:padding="16dp">
                        <TextView
                            android:id="@+id/text1"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="16dp"
                            android:gravity="center"
                            android:text="读卡操作"
                            android:textColor="@color/base_blue_bg"
                            android:textSize="@dimen/big_text_size"
                            android:textStyle="bold" />
                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="20dp"
                            android:gravity="center"
                            android:text="请将卡贴在设备上进行读卡"
                            android:textColor="#333333"
                            android:textSize="@dimen/text_size"
                            android:textStyle="bold" />
                        <ImageView
                            android:layout_width="120dp"
                            android:layout_height="120dp"
                            android:layout_gravity="center"
                            android:layout_marginBottom="20dp"
                            android:scaleType="fitCenter"
                            android:src="@mipmap/nfc_write" />
                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:gravity="center"
                            android:text="请保持手持机和卡片不要移动"
                            android:textColor="#666666"
                            android:textSize="@dimen/new_card_size" />
                    </LinearLayout>
                </androidx.cardview.widget.CardView>
            </LinearLayout>
            <!-- 卡片信息显示区域 -->
            <LinearLayout
                android:id="@+id/card_info_container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:visibility="visible">
                <!-- 卡内数据区域 -->
                <androidx.cardview.widget.CardView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="16dp"
                    app:cardCornerRadius="8dp"
                    app:cardElevation="2dp">
                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="vertical"
                        android:padding="16dp">
                        <!-- 持卡人 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="2dp"
                            android:orientation="horizontal"
                            android:gravity="center_vertical"
                            android:background="#F8F9FA"
                            android:padding="12dp">
                            <TextView
                                android:layout_width="105dp"
                                android:layout_height="wrap_content"
                                android:text="持卡人:"
                                android:textColor="#333333"
                                android:textSize="@dimen/text_size" />
                            <TextView
                                android:id="@+id/tv_user_name"
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:text="--"
                                android:textColor="#666666"
                                android:textSize="@dimen/text_size" />
                        </LinearLayout>
                        <!-- 卡号 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="2dp"
                            android:orientation="horizontal"
                            android:gravity="center_vertical"
                            android:background="#FFFFFF"
                            android:padding="12dp">
                            <TextView
                                android:layout_width="105dp"
                                android:layout_height="wrap_content"
                                android:text="卡号:"
                                android:textColor="#333333"
                                android:textSize="@dimen/text_size" />
                            <TextView
                                android:id="@+id/tv_card_number"
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:text="--"
                                android:textColor="#666666"
                                android:textSize="@dimen/text_size" />
                        </LinearLayout>
                        <!-- 卡片类型 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="2dp"
                            android:orientation="horizontal"
                            android:gravity="center_vertical"
                            android:background="#F8F9FA"
                            android:padding="12dp">
                            <TextView
                                android:layout_width="105dp"
                                android:layout_height="wrap_content"
                                android:text="卡类型:"
                                android:textColor="#333333"
                                android:textSize="@dimen/text_size" />
                            <TextView
                                android:id="@+id/tv_card_type"
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:text="--"
                                android:textColor="#666666"
                                android:textSize="@dimen/text_size" />
                        </LinearLayout>
                        <!-- 卡内余额 -->
                        <LinearLayout
                            android:id="@+id/ll_card_balance"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="2dp"
                            android:orientation="horizontal"
                            android:visibility="visible"
                            android:gravity="center_vertical"
                            android:background="#FFFFFF"
                            android:padding="12dp">
                            <TextView
                                android:layout_width="105dp"
                                android:layout_height="wrap_content"
                                android:text="卡余额:"
                                android:textColor="#333333"
                                android:textSize="@dimen/text_size" />
                            <TextView
                                android:id="@+id/tv_card_balance"
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:text="0.00元"
                                android:textColor="#FF6B35"
                                android:textSize="@dimen/text_size"
                                android:textStyle="bold" />
                        </LinearLayout>
                        <!-- 用户编号 -->
                        <LinearLayout
                            android:id="@+id/ll_user_number"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="2dp"
                            android:orientation="horizontal"
                            android:visibility="visible"
                            android:gravity="center_vertical"
                            android:background="#F8F9FA"
                            android:padding="12dp">
                            <TextView
                                android:layout_width="105dp"
                                android:layout_height="wrap_content"
                                android:text="用户编号:"
                                android:textColor="#333333"
                                android:textSize="@dimen/text_size" />
                            <TextView
                                android:id="@+id/tv_user_number"
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:text="--"
                                android:textColor="#666666"
                                android:textSize="@dimen/text_size" />
                        </LinearLayout>
                        <!-- 手机号 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="2dp"
                            android:orientation="horizontal"
                            android:gravity="center_vertical"
                            android:background="#FFFFFF"
                            android:padding="12dp">
                            <TextView
                                android:layout_width="105dp"
                                android:layout_height="wrap_content"
                                android:text="手机号:"
                                android:textColor="#333333"
                                android:textSize="@dimen/text_size" />
                            <TextView
                                android:id="@+id/tv_phone"
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:text="--"
                                android:textColor="#666666"
                                android:textSize="@dimen/text_size" />
                        </LinearLayout>
                        <!-- 身份证号 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="2dp"
                            android:orientation="horizontal"
                            android:gravity="center_vertical"
                            android:background="#F8F9FA"
                            android:padding="12dp">
                            <TextView
                                android:layout_width="105dp"
                                android:layout_height="wrap_content"
                                android:text="身份证号:"
                                android:textColor="#333333"
                                android:textSize="@dimen/text_size" />
                            <TextView
                                android:id="@+id/tv_id_card"
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:text="--"
                                android:textColor="#666666"
                                android:textSize="@dimen/text_size" />
                        </LinearLayout>
                        <!-- 卡片状态 -->
                        <LinearLayout
                            android:id="@+id/ll_card_status"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="2dp"
                            android:orientation="horizontal"
                            android:visibility="visible"
                            android:gravity="center_vertical"
                            android:background="#FFFFFF"
                            android:padding="12dp">
                            <TextView
                                android:layout_width="105dp"
                                android:layout_height="wrap_content"
                                android:text="卡片状态:"
                                android:textColor="#333333"
                                android:textSize="@dimen/text_size" />
                            <TextView
                                android:id="@+id/tv_card_status"
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:text="正常"
                                android:textColor="#4CAF50"
                                android:textSize="@dimen/text_size" />
                        </LinearLayout>
                        <!-- 最后使用时间 -->
                        <LinearLayout
                            android:id="@+id/ll_last_use_time"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="2dp"
                            android:orientation="horizontal"
                            android:visibility="visible"
                            android:gravity="center_vertical"
                            android:background="#F8F9FA"
                            android:padding="12dp">
                            <TextView
                                android:layout_width="105dp"
                                android:layout_height="wrap_content"
                                android:text="最后充值时间:"
                                android:textColor="#333333"
                                android:textSize="@dimen/text_size" />
                            <TextView
                                android:id="@+id/tv_last_use_time"
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:text="--"
                                android:textColor="#666666"
                                android:textSize="@dimen/text_size" />
                        </LinearLayout>
                    </LinearLayout>
                </androidx.cardview.widget.CardView>
            </LinearLayout>
        </LinearLayout>
    </ScrollView>
</RelativeLayout>