左晓为主开发手持机充值管理机
zuoxiao
16 小时以前 98994189864f045551f7d06f78017b5df70e1be3
feat(card): 添加补扣功能并优化卡片操作界面

- 新增补扣Activity,实现卡片补扣操作流程
- 更新卡片信息展示和操作界面
- 优化读卡和写卡逻辑,支持多种卡片操作类型
- 修复卡片状态显示和操作限制问题
7个文件已修改
6个文件已添加
980 ■■■■■ 已修改文件
generallibrary/src/main/AndroidManifest.xml 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/BSCardFragment.kt 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/CardCancelActivity.kt 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/CardDeductActivity.kt 391 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/CardWriteSuccessActivity.kt 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/NfcWreatActivity.kt 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/RechargeDetailActivity.kt 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/bean/net/CardCancelResult.kt 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/bean/net/CardDeductResult.kt 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/bean/net/CardReturnResult.kt 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/bean/net/RechargeResult.kt 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/drawable/button_green_bg.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/layout/activity_card_deduct.xml 412 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/AndroidManifest.xml
@@ -126,6 +126,15 @@
                <data android:mimeType="text/plain" />
            </intent-filter>
        </activity>
        <activity android:name=".activity.CardDeductActivity"
            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
@@ -60,6 +60,9 @@
        binding.homeCancelCard.setOnClickListener {
            context?.let { CardCancelActivity.start(it) }
        }
        binding.homeDeduction.setOnClickListener {
            context?.let { CardDeductActivity.start(it) }
        }
    }
}
generallibrary/src/main/java/com/dayu/general/activity/CardCancelActivity.kt
@@ -244,12 +244,20 @@
            binding.tvUserName.text = info.userName ?: "未知"
            binding.tvPhone.text = info.phone ?: "未绑定"
            // 根据status字段显示状态
            val statusText = when (info.state) {
                1 -> "正常"
                2 -> "挂失"
                3 -> "锁定"
                else -> "未知"
            // 根据state字段显示状态
            val (statusText, statusColor) = when (info.state) {
                1 -> Pair("正常", android.graphics.Color.parseColor("#4CAF50")) // 绿色
                2 -> Pair("已注销", android.graphics.Color.parseColor("#FF5722")) // 深橙色
                3 -> Pair("已挂失", android.graphics.Color.parseColor("#FF9800")) // 橙色
                4 -> Pair("无效卡片", android.graphics.Color.parseColor("#F44336")) // 红色
                else -> Pair("未知状态", android.graphics.Color.parseColor("#9E9E9E")) // 灰色
            }
            binding.tvCardStatus.text = statusText
            binding.tvCardStatus.setTextColor(statusColor)
            if (info.state == 3) {
                binding.btnCancelCard.visibility = android.view.View.GONE
            } else {
                binding.btnCancelCard.visibility = android.view.View.VISIBLE
            }
        }
    }
generallibrary/src/main/java/com/dayu/general/activity/CardDeductActivity.kt
New file
@@ -0,0 +1,391 @@
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.MornyUtil
import com.dayu.baselibrary.utils.ToastUtil
import com.dayu.baselibrary.view.TipDialog
import com.dayu.baselibrary.view.TitleBar
import com.dayu.general.BaseApplication
import com.dayu.general.bean.card.UserCard
import com.dayu.general.bean.net.CardInfoResult
import com.dayu.general.bean.net.CardDeductResult
import com.dayu.general.databinding.ActivityCardDeductBinding
import com.dayu.general.net.ApiManager
import com.dayu.general.net.BaseResponse
import com.dayu.general.tool.CardCommon
import com.dayu.general.tool.CardOperationType
import com.dayu.general.tool.NfcReadHelper
/**
 * @author: AI Assistant
 * @desc: 补扣Activity
 * @since: 2025/6/17
 */
class CardDeductActivity : BaseNfcActivity() {
    private lateinit var binding: ActivityCardDeductBinding
    private var cardNumber: String? = null
    private var cardInfo: CardInfoResult? = null
    private var userCard: UserCard? = null
    companion object {
        /**
         * 启动补扣Activity
         */
        fun start(context: Context) {
            val intent = Intent(context, CardDeductActivity::class.java)
            context.startActivity(intent)
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityCardDeductBinding.inflate(layoutInflater)
        setContentView(binding.root)
        initView()
    }
    private fun initView() {
        // 设置TitleBar的返回按钮点击事件
        binding.titleBar.setOnItemclickListner(TitleBar.ClickType_LEFT_IMAGE) {
            finish()
        }
        // 设置补扣按钮点击事件
        binding.btnDeduct.setOnClickListener {
            performCardDeduct()
        }
    }
    /**
     * 重置到读卡状态
     */
    private fun resetToReadingState() {
        binding.scrollReadCard.visibility = android.view.View.VISIBLE
        binding.cardInfoContainer.visibility = android.view.View.GONE
        binding.bottomButtonContainer.visibility = android.view.View.GONE
        cardNumber = null
        cardInfo = null
        binding.etDeductAmount.setText("")
        binding.etRemarks.setText("")
    }
    /**
     * 显示确认对话框
     */
    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 -> {
                    // 管理类卡不支持补扣
                    showConfirmDialog("该卡片类型不支持补扣操作") {
                        resetToReadingState()
                    }
                }
            }
        } 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
        }
        // 输出用户卡内所有信息到日志
        android.util.Log.d("CardDeductActivity", "=== 用户卡信息 ===")
        android.util.Log.d("CardDeductActivity", "卡号: $cardNumber")
        android.util.Log.d("CardDeductActivity", "卡片类型: $cardType")
        android.util.Log.d("CardDeductActivity", "卡内余额: ${userCard.balance}")
        android.util.Log.d("CardDeductActivity", "==================")
        this.userCard = userCard
        // 根据卡号获取卡片详细信息
        getCardInfo(cardNumber, cardType, userCard)
    }
    /**
     * 获取卡片详细信息(用户卡专用)
     */
    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.scrollReadCard.visibility = android.view.View.GONE
        binding.cardInfoContainer.visibility = android.view.View.VISIBLE
        binding.bottomButtonContainer.visibility = android.view.View.VISIBLE
        this.cardInfo = cardInfo
        binding.tvCardNumber.text = cardNumber
        userCard.let { card ->
            // 余额转换为元(原始数据可能是分)
            binding.tvCardBalance.text = MornyUtil.changeF2Y(card.balance) + "元"
        }
        // 显示服务器数据
        cardInfo?.let { info ->
            binding.tvUserName.text = info.userName ?: "未知"
            binding.tvPhone.text = info.phone ?: "未绑定"
            // 根据state字段显示状态
            val (statusText, statusColor) = when (info.state) {
                1 -> Pair("正常", android.graphics.Color.parseColor("#4CAF50")) // 绿色
                2 -> Pair("已注销", android.graphics.Color.parseColor("#FF5722")) // 深橙色
                3 -> Pair("已挂失", android.graphics.Color.parseColor("#FF9800")) // 橙色
                4 -> Pair("无效卡片", android.graphics.Color.parseColor("#F44336")) // 红色
                else -> Pair("未知状态", android.graphics.Color.parseColor("#9E9E9E")) // 灰色
            }
            binding.tvCardStatus.text = statusText
            binding.tvCardStatus.setTextColor(statusColor)
        }
    }
    /**
     * 处理卡信息获取错误
     */
    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()
        }
    }
    /**
     * 执行补扣操作
     */
    private fun performCardDeduct() {
        // 验证输入
        val deductAmountStr = binding.etDeductAmount.text.toString().trim()
        val remarks = binding.etRemarks.text.toString().trim()
        // 补扣金额验证(必填)
        if (deductAmountStr.isEmpty()) {
            ToastUtil.show("请输入补扣金额")
            return
        }
        val deductAmount = try {
            val amount = deductAmountStr.toDouble()
            if (amount <= 0) {
                ToastUtil.show("补扣金额必须大于0")
                return
            }
            amount
        } catch (e: NumberFormatException) {
            ToastUtil.show("请输入有效的补扣金额")
            return
        }
        // 备注处理(非必填)
        val finalRemarks = if (remarks.isEmpty()) {
            "补扣" // 如果未输入备注,使用默认值
        } else {
            remarks
        }
        callDeductCardApi(deductAmount, finalRemarks)
    }
    /**
     * 调用补扣API接口
     */
    private fun callDeductCardApi(deductAmount: Double, finalRemarks: String) {
        if (cardNumber.isNullOrBlank()) {
            ToastUtil.show("卡号信息异常,请重新刷卡")
            return
        }
        // 获取当前卡内余额(以分为单位)
        val currentBalance = userCard?.balance?.let { MornyUtil.changeF2Y(it) } ?: 0
        val map = mutableMapOf<String, Any>()
        map["cardNum"] = cardInfo?.cardNum.toString()
        map["repayMorny"] = deductAmount.toString()
        map["balance"] = currentBalance.toString()
        map["remarks"] = finalRemarks
        map["operator"] = BaseApplication.userId
        ApiManager.getInstance().requestPostLoading(
            this,
            "terminal/card/termRepay",
            CardDeductResult::class.java,
            map,
            object : SubscriberListener<BaseResponse<CardDeductResult>>() {
                override fun onNext(t: BaseResponse<CardDeductResult>) {
                    if (t.success && t.content != null) {
                        // 补扣成功,跳转到写卡界面
                        startWriteCardActivity(t.content!!, deductAmount, userCard!!)
                    } else {
                        // 补扣失败
                        val errorMsg = if (t.msg.isNullOrBlank()) "补扣失败,请重试" else t.msg
                        showConfirmDialog("补扣失败:$errorMsg") {
                        }
                    }
                }
                override fun onError(e: Throwable?) {
                    super.onError(e)
                    showConfirmDialog("补扣请求失败: ${e?.message ?: "网络异常,请检查网络连接"}") {
                    }
                }
            }
        )
    }
    /**
     * 跳转到写卡界面进行卡内容更新
     */
    private fun startWriteCardActivity(
        deductResult: CardDeductResult,
        deductAmount: Double,
        userCard: UserCard
    ) {
        // 创建更新后的用户卡数据
        val updatedUserCard = UserCard().apply {
            // 复制原有属性
            cardType = userCard.cardType
            areaNumber = userCard.areaNumber
            userCode = userCard.userCode
            userCodeNumber = userCard.userCodeNumber
            phoneNumber = userCard.phoneNumber
            projectCode = userCard.projectCode
            surplusWater = userCard.surplusWater
            waterPrice = userCard.waterPrice
            electricPrice = userCard.electricPrice
            rechargeDate = userCard.rechargeDate
            // 使用返回的新余额
            balance = MornyUtil.changeY2F(deductResult.balance.toString())
        }
        val intent = Intent(this, NfcWreatActivity::class.java).apply {
            putExtra("cardAddr", cardNumber)
            putExtra("operationTypeCode", CardOperationType.DeductCard.code) // 使用补扣类型进行写卡
            putExtra("orderNumber", deductResult.orderNo)
            putExtra("deductAmount", deductAmount)
            putExtra("userCard", updatedUserCard as java.io.Serializable)
        }
        startActivity(intent)
        finish()
    }
}
generallibrary/src/main/java/com/dayu/general/activity/CardWriteSuccessActivity.kt
@@ -64,6 +64,22 @@
                binding.successMessage.text = message
            }
            
            CardOperationType.DeductCard -> {
                // 设置标题
                binding.titleBar.setCenterText("补扣成功")
                // 设置成功信息
                binding.successTitle.text = "补扣成功"
                // 设置详细信息
                val message = if (cardNumber.isNullOrBlank()) {
                    "卡片补扣操作已成功完成\n卡内余额已更新"
                } else {
                    "卡片补扣操作已成功完成\n卡号:$cardNumber\n卡内余额已更新"
                }
                binding.successMessage.text = message
            }
            else -> {
                // 默认显示写卡成功
                binding.titleBar.setCenterText("写卡成功")
generallibrary/src/main/java/com/dayu/general/activity/NfcWreatActivity.kt
@@ -41,6 +41,9 @@
    // 返还相关信息
    private var returnAmount = 0.0
    // 补扣相关信息
    private var deductAmount = 0.0
    //订单编号
    var orderNumber = ""
    private lateinit var userCard: UserCard
@@ -74,6 +77,9 @@
        // 获取返还相关信息
        returnAmount = intent?.getDoubleExtra("returnAmount", 0.0) ?: 0.0
        // 获取补扣相关信息
        deductAmount = intent?.getDoubleExtra("deductAmount", 0.0) ?: 0.0
        if (intent?.hasExtra("cardFee") == true) {
            cardFee = intent?.getIntExtra("cardFee", 0) ?: 0
@@ -174,8 +180,15 @@
                    binding?.cardData?.text = textData.toString()
                }
                CardOperationType.DeductCard -> {
                    var textData = StringBuilder()
                    textData.append("补扣\n")
                    textData.append("卡内余额:" + MornyUtil.changeF2Y(userCard.balance) + "元\n")
                    textData.append("补扣金额:" + deductAmount + "元")
                    binding?.cardData?.text = textData.toString()
                }
                CardOperationType.CheckCard -> TODO()
                CardOperationType.DeductCard -> TODO()
                CardOperationType.ReplaceCard -> TODO()
                null -> TODO()
            }
@@ -272,8 +285,22 @@
                    })
                }
                CardOperationType.DeductCard -> {
                    nfcWreatHelper.writeUserDataAsync(userCard, object : NFCCallBack {
                        override fun isSusses(flag: Boolean, msg: String?) {
                            // 确保Toast在主线程中调用
                            runOnUiThread {
                                if (flag) {
                                    postCardData(cardAddr)
                                } else {
                                    ToastUtil.show("补扣写卡失败: ${msg ?: "未知错误"}")
                                }
                            }
                        }
                    })
                }
                CardOperationType.CheckCard -> TODO()
                CardOperationType.DeductCard -> TODO()
                CardOperationType.ReplaceCard -> TODO()
                null -> TODO()
            }
generallibrary/src/main/java/com/dayu/general/activity/RechargeDetailActivity.kt
@@ -405,15 +405,7 @@
            val userCard = UserCard().apply {
                // 设置用户卡信息
                cardInfo?.let { info ->
                    userCode = info.cardNum ?: ""
                    // 计算新余额:原有余额 + 充值金额 + 赠送金额
                    val originalBalance =
                        this@RechargeDetailActivity.userCard?.balance ?: 0 // 原有余额(分)
                    val rechargeAmountInCents = (rechargeAmount * 100).toInt() // 充值金额转分
                    val bonusAmountInCents = (bonusAmount * 100).toInt() // 赠送金额转分
                    balance = originalBalance + rechargeAmountInCents + bonusAmountInCents
                    balance = MornyUtil.changeY2F(rechargeResult.balance)
                }
                // 设置其他必要信息
generallibrary/src/main/java/com/dayu/general/bean/net/CardCancelResult.kt
New file
@@ -0,0 +1,13 @@
package com.dayu.general.bean.net
/**
 * 销卡结果数据类
 * @author: zuo
 * @date: 2025/01/17
 * @description: 销卡接口返回数据
 */
data class CardCancelResult(
    val projectNo: Int,     // 项目编号
    val cardNum: String,    // 卡号
    val orderNo: String     // 订单号
)
generallibrary/src/main/java/com/dayu/general/bean/net/CardDeductResult.kt
New file
@@ -0,0 +1,33 @@
/**
 * @author: AI Assistant
 * @desc: 补扣接口返回结果
 * @since: 2025/6/17
 */
package com.dayu.general.bean.net
import java.io.Serializable
/**
 * 补扣操作的API返回结果
 */
data class CardDeductResult(
    /**
     * 补扣后的余额
     */
    val balance: Float = 0f,
    /**
     * 水价
     */
    val waterPrice: Double = 0.0,
    /**
     * 时间
     */
    val time: String = "",
    /**
     * 订单号
     */
    val orderNo: String = ""
) : Serializable
generallibrary/src/main/java/com/dayu/general/bean/net/CardReturnResult.kt
New file
@@ -0,0 +1,33 @@
/**
 * @author: zuo
 * @desc: 返还接口返回结果
 * @since: 2025/6/17
 */
package com.dayu.general.bean.net
import java.io.Serializable
/**
 * 返还操作的API返回结果
 */
data class CardReturnResult(
    /**
     * 返还后的余额
     */
    val balance: Float = 0f,
    /**
     * 水价
     */
    val waterPrice: Double = 0.0,
    /**
     * 时间
     */
    val time: String = "",
    /**
     * 订单号
     */
    val orderNo: String = ""
) : Serializable
generallibrary/src/main/java/com/dayu/general/bean/net/RechargeResult.kt
@@ -10,5 +10,6 @@
    val cardNum: String,             // 卡号
    val orderNo: String,             // 订单号
    val waterPrice: Double,          // 水价
    val time: String                 // 时间
    val time: String,                // 时间
    val balance: String              // 余额
) : Serializable 
generallibrary/src/main/res/drawable/button_green_bg.xml
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/button_select_text_color" />
    <corners android:radius="6dp" />
</shape>
generallibrary/src/main/res/layout/activity_card_deduct.xml
New file
@@ -0,0 +1,412 @@
<?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="补扣"
        app:leftImage="@mipmap/icon_back" />
    <!-- 读卡提示区域 - 全屏显示 -->
    <ScrollView
        android:id="@+id/scroll_read_card"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/titleBar"
        android:fillViewport="true"
        android:visibility="visible">
        <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">
                <androidx.cardview.widget.CardView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_marginBottom="16dp"
                    app:cardCornerRadius="8dp"
                    app:cardElevation="2dp">
                    <RelativeLayout
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:padding="16dp">
                        <TextView
                            android:id="@+id/tv_title"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="100dp"
                            android:gravity="center"
                            android:text="补扣操作"
                            android:layout_marginTop="20dp"
                            android:textColor="@color/base_blue_bg"
                            android:textSize="@dimen/big_text_size"
                            android:textStyle="bold" />
                        <TextView
                            android:id="@+id/tv_subtitle"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_below="@+id/tv_title"
                            android:layout_marginBottom="20dp"
                            android:gravity="center"
                            android:text="请将需要补扣的卡片贴在设备上进行读取"
                            android:textColor="#333333"
                            android:textSize="@dimen/text_size"
                            android:textStyle="bold" />
                        <ImageView
                            android:id="@+id/iv_nfc"
                            android:layout_width="120dp"
                            android:layout_height="120dp"
                            android:layout_below="@+id/tv_subtitle"
                            android:layout_centerHorizontal="true"
                            android:layout_marginBottom="20dp"
                            android:scaleType="fitCenter"
                            android:src="@mipmap/nfc_write" />
                        <TextView
                            android:id="@+id/tv_tip"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_below="@+id/iv_nfc"
                            android:gravity="center"
                            android:text="请保持手持机和卡片不要移动"
                            android:textColor="#666666"
                            android:textSize="@dimen/new_card_size" />
                    </RelativeLayout>
                </androidx.cardview.widget.CardView>
            </LinearLayout>
        </LinearLayout>
    </ScrollView>
    <!-- 卡片信息显示区域 - 带固定底部按钮 -->
    <LinearLayout
        android:id="@+id/card_info_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/bottom_button_container"
        android:layout_below="@+id/titleBar"
        android:orientation="vertical"
        android:visibility="gone">
        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:fillViewport="true"
            android:padding="12dp">
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">
                <!-- 卡片信息区域 -->
                <androidx.cardview.widget.CardView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="12dp"
                    app:cardCornerRadius="8dp"
                    app:cardElevation="2dp">
                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="vertical"
                        android:padding="12dp">
                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="8dp"
                            android:gravity=""
                            android:text="卡片信息"
                            android:textColor="@color/base_blue_bg"
                            android:textSize="16sp"
                            android:textStyle="bold" />
                        <!-- 持卡人 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="1dp"
                            android:background="#F8F9FA"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="8dp">
                            <TextView
                                android:layout_width="80dp"
                                android:layout_height="wrap_content"
                                android:text="持卡人:"
                                android:textColor="#333333"
                                android:textSize="14sp" />
                            <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="14sp" />
                        </LinearLayout>
                        <!-- 卡号 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="1dp"
                            android:background="#FFFFFF"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="8dp">
                            <TextView
                                android:layout_width="80dp"
                                android:layout_height="wrap_content"
                                android:text="卡地址:"
                                android:textColor="#333333"
                                android:textSize="14sp" />
                            <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="14sp" />
                        </LinearLayout>
                        <!-- 卡片状态 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="1dp"
                            android:background="#F8F9FA"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="8dp">
                            <TextView
                                android:layout_width="80dp"
                                android:layout_height="wrap_content"
                                android:text="卡片状态:"
                                android:textColor="#333333"
                                android:textSize="14sp" />
                            <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="14sp" />
                        </LinearLayout>
                        <!-- 卡内余额 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="1dp"
                            android:background="#FFFFFF"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="8dp">
                            <TextView
                                android:layout_width="80dp"
                                android:layout_height="wrap_content"
                                android:text="卡内余额:"
                                android:textColor="#333333"
                                android:textSize="14sp" />
                            <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="14sp"
                                android:textStyle="bold" />
                        </LinearLayout>
                        <!-- 手机号 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:background="#F8F9FA"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="8dp">
                            <TextView
                                android:layout_width="80dp"
                                android:layout_height="wrap_content"
                                android:text="手机号:"
                                android:textColor="#333333"
                                android:textSize="14sp" />
                            <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="14sp" />
                        </LinearLayout>
                    </LinearLayout>
                </androidx.cardview.widget.CardView>
                <!-- 补扣操作区域 -->
                <androidx.cardview.widget.CardView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:cardCornerRadius="8dp"
                    app:cardElevation="2dp">
                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="vertical"
                        android:padding="12dp">
                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="12dp"
                            android:gravity=""
                            android:text="补扣操作"
                            android:textColor="@color/base_blue_bg"
                            android:textSize="16sp"
                            android:textStyle="bold" />
                        <!-- 补扣金额和备注并排显示 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:orientation="vertical">
                            <!-- 补扣金额输入 -->
                            <LinearLayout
                                android:layout_width="match_parent"
                                android:layout_height="wrap_content"
                                android:layout_marginEnd="6dp"
                                android:layout_weight="1"
                                android:orientation="vertical">
                                <TextView
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:layout_marginBottom="6dp"
                                    android:text="补扣金额(元): *"
                                    android:textColor="#333333"
                                    android:textSize="14sp"
                                    android:textStyle="bold" />
                                <EditText
                                    android:id="@+id/et_deduct_amount"
                                    android:layout_width="match_parent"
                                    android:layout_height="40dp"
                                    android:layout_marginTop="5dp"
                                    android:background="@drawable/edit_text_bg"
                                    android:hint="请输入补扣金额"
                                    android:inputType="numberDecimal"
                                    android:padding="8dp"
                                    android:textColor="#333333"
                                    android:textColorHint="#999999"
                                    android:textSize="14sp" />
                            </LinearLayout>
                            <!-- 备注输入 -->
                            <LinearLayout
                                android:layout_width="match_parent"
                                android:layout_height="wrap_content"
                                android:layout_marginTop="5dp"
                                android:layout_weight="1"
                                android:orientation="vertical">
                                <TextView
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:layout_marginBottom="6dp"
                                    android:text="备注:"
                                    android:textColor="#333333"
                                    android:textSize="14sp"
                                    android:textStyle="bold" />
                                <EditText
                                    android:id="@+id/et_remarks"
                                    android:layout_width="match_parent"
                                    android:layout_height="80dp"
                                    android:layout_marginTop="5dp"
                                    android:background="@drawable/edit_text_bg"
                                    android:hint="请输入补扣备注"
                                    android:inputType="text"
                                    android:minLines="2"
                                    android:padding="8dp"
                                    android:textColor="#333333"
                                    android:textColorHint="#999999"
                                    android:textSize="14sp" />
                            </LinearLayout>
                        </LinearLayout>
                    </LinearLayout>
                </androidx.cardview.widget.CardView>
            </LinearLayout>
        </ScrollView>
    </LinearLayout>
    <!-- 底部按钮区域 -->
    <LinearLayout
        android:id="@+id/bottom_button_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="#FFFFFF"
        android:elevation="4dp"
        android:orientation="vertical"
        android:padding="16dp"
        android:visibility="gone">
        <!-- 补扣按钮 -->
        <Button
            android:id="@+id/btn_deduct"
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:background="@drawable/button_green_bg"
            android:text="确认补扣"
            android:textColor="#FFFFFF"
            android:textSize="@dimen/big_text_size"
            android:textStyle="bold" />
    </LinearLayout>
</RelativeLayout>