左晓为主开发手持机充值管理机
zuoxiao
2025-06-10 9a2399d1a0003d1f2935720474b325639454178d
feat(general): 新增卡片操作类型并优化写卡逻辑

- 新增 CardOperationType 密封类,用于区分不同的卡片操作类型
- 修改工本费输入类型为整数,提高用户体验
- 优化用户卡数据写入逻辑,确保数据长度正确
-修复清零卡和开卡的写卡流程,增加订单号和操作类型支持
- 优化数据传输格式,提高系统稳定性
8个文件已修改
1个文件已添加
213 ■■■■ 已修改文件
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/NativeNfcWriteHelper.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/ManageListActivity.kt 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/NewCard2Activity.kt 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/NfcWreatActivity.kt 79 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/bean/card/UserCard.kt 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/bean/db/CardRegistrationBean.kt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/bean/net/NewCardResult.kt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/tool/CardOperationType.kt 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/layout/activity_new_card_ge.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/NativeNfcWriteHelper.java
@@ -125,6 +125,8 @@
                        mfc.connect();
                        // 验证扇区密码
                        boolean isOpen = false;
                        if (listKeyA.size() != 0) {
                        for (int i = 0; i < listKeyA.size(); i++) {
                            if (mfc.authenticateSectorWithKeyA(a, listKeyA.get(i))) {
                                isOpen = true;
@@ -133,6 +135,14 @@
                                    changePasword(a, mfc);
                                }
                                break;
                                }
                            }
                        } else if (listA_PS.size() != 0 && listA_PS.size() > a) {
                            if (mfc.authenticateSectorWithKeyA(a, defauleKey)) {
                                isOpen = true;
                            } else if (mfc.authenticateSectorWithKeyA(a, listA_PS.get(a))) {
                                isOpen = true;
                            }
                        }
                        if (isOpen) {
@@ -239,7 +249,7 @@
                        boolean isOpen = false;
                        if (listKeyA.size() != 0) {
                            for (int i = 0; i < listKeyA.size(); i++) {
                                if (mfc.authenticateSectorWithKeyA(0, listKeyA.get(i))) {
                                if (mfc.authenticateSectorWithKeyA(a, listKeyA.get(i))) {
                                    isOpen = true;
                                    if (listKeyA.get(i).equals(defauleKey)) {
                                        // 当前为默认白卡密码时写卡时修改密码
@@ -326,7 +336,6 @@
    /**
     * 修改密码
     *
     * @param 书写的扇区
     * @param passWord 密码
     * @return
     */
generallibrary/src/main/java/com/dayu/general/activity/ManageListActivity.kt
@@ -5,6 +5,7 @@
import com.dayu.baselibrary.view.TitleBar.ClickType_LEFT_IMAGE
import com.dayu.general.databinding.ActivityManageListGeBinding
import com.dayu.general.tool.CardCommon
import com.dayu.general.tool.CardOperationType
class ManageListActivity : BaseActivity() {
@@ -21,13 +22,15 @@
        binding?.tvCleanCard?.setOnClickListener {
            var intent = Intent(this, ManagerReadActivity::class.java).apply {
                putExtra("cardType", CardCommon.CLEAN_CARD_TYPE)
                putExtra("operationTypeCode", CardOperationType.CleanCard.code)
            }
            startActivity(intent)
        }
        binding?.tvCheckCard?.setOnClickListener {
            var intent = Intent(this, ManagerReadActivity::class.java).apply {
                putExtra("cardType", CardCommon.CHECK_CARD)
                putExtra("operationTypeCode", CardCommon.CHECK_CARD)
                putExtra("operationTypeCode", CardOperationType.CheckCard.code)
            }
            startActivity(intent)
        }
generallibrary/src/main/java/com/dayu/general/activity/NewCard2Activity.kt
@@ -10,6 +10,7 @@
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import com.dayu.baselibrary.net.subscribers.SubscriberListener
import com.dayu.baselibrary.utils.MornyUtil
import com.dayu.baselibrary.view.TitleBar.ClickType_LEFT_IMAGE
import com.dayu.general.BaseApplication
import com.dayu.general.R
@@ -24,6 +25,7 @@
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
import com.dayu.general.tool.NfcReadHelper
import com.dayu.general.tool.NfcWreatHelper
import com.dayu.general.utils.DateUtils
@@ -308,11 +310,7 @@
        // 获取工本费
        val cardFeeStr = binding.newCardCardFee.text.toString()
        val cardFee = if (cardFeeStr.isEmpty()) 0.0 else cardFeeStr.toDouble()
        // 格式化金额为两位小数
        val formattedRechargeAmount = String.format("%.2f", rechargeAmount)
        val formattedCardFee = String.format("%.2f", cardFee)
        val cardFee = if (cardFeeStr.isEmpty()) 0 else cardFeeStr.toInt()
        val remark = binding.newCardRemark.text.toString()
@@ -369,17 +367,20 @@
                                    putExtra("orderId", orderId)
                                    putExtra("cardAddr", cardPhysicalId)
                                    var userCard = UserCard()
                                    userCard.areaNumber =clientInfo.districtNum
                                    userCard.areaNumber = response.content?.cardNum?.substring(0, 12).toString()
                                    userCard.userCode =clientInfo.clientNum
                                    userCard.phoneNumber =clientInfo.phone
                                    userCard.userCodeNumber = response.content?.cardNum?.toInt()!!
                                    userCard.projectCode = response.content?.projectNo?.toInt()!!
                                    userCard.balance = response.content?.balance?.toInt()!!
                                    userCard.userCodeNumber = response.content?.cardNum?.substring(12)?.toInt()!!
                                    userCard.projectCode = response.content?.projectNo!!
                                    userCard.balance = MornyUtil.changeY2F(response.content?.balance)
//                                    userCard.surplusWater = response.content?.surplusWater?.toInt()!!
                                    userCard.waterPrice = response.content?.waterPrice?.toFloat()!!
//                                    userCard.electricPrice = response.content?.electricPrice?.toFloat()!!
                                    userCard.rechargeDate = DateUtils.parseStringToCalendar(response.content?.time)
                                    putExtra("userCard", userCard)
                                    putExtra("operationTypeCode", CardOperationType.OpenCard.code)
                                    putExtra("orderNumber", response.content?.orderNo)
                                    putExtra("cardFee", cardFee)
                                    startActivity(this)
                                }
                            } catch (e: Exception) {
generallibrary/src/main/java/com/dayu/general/activity/NfcWreatActivity.kt
@@ -9,6 +9,7 @@
import com.dayu.general.bean.card.ClearCard
import com.dayu.general.bean.card.UserCard
import com.dayu.general.tool.CardCommon
import com.dayu.general.tool.CardOperationType
import com.dayu.general.databinding.ActivityNfcWriteGeBinding
import com.dayu.general.net.ApiManager
import com.dayu.general.net.BaseResponse
@@ -17,6 +18,7 @@
import com.dayu.general.dao.BaseDaoSingleton
import com.tencent.bugly.crashreport.CrashReport
import kotlinx.coroutines.launch
import java.lang.StringBuilder
/**
 * @author: zuo
@@ -28,7 +30,13 @@
    var cardType = ""
    var orderId = ""
    var cardAddr = ""
    var cardFee = 0
    //订单编号
    var orderNumber = ""
    private lateinit var userCard: UserCard
    private var operationTypeCode = -1;
    private var operationType: CardOperationType? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
@@ -44,18 +52,44 @@
        cardType = intent?.getStringExtra("cardType") ?: ""
        orderId = intent?.getStringExtra("orderId") ?: ""
        cardAddr = intent?.getStringExtra("cardAddr") ?: ""
        operationTypeCode = intent?.getIntExtra("operationTypeCode", -1) ?: -1
        operationType = CardOperationType.fromCode(operationTypeCode)
        if (intent?.hasExtra("cardFee") == true) {
            cardFee = intent?.getIntExtra("cardFee", 0) ?: 0
        }
        if (intent?.hasExtra("userCard") == true) {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
            userCard = intent?.getSerializableExtra("userCard", UserCard::class.java)!!
        } else {
            userCard = (intent?.getSerializableExtra("userCard") as? UserCard)!!
        }
        if (cardType.isNotEmpty()) {
            when (cardType) {
                CardCommon.CLEAN_CARD_TYPE -> {
        }
        if (operationTypeCode != -1) {
            when (operationType) {
                CardOperationType.CleanCard -> {
                    binding?.cardData?.text = "清零卡写卡"
                }
                CardOperationType.OpenCard -> {
                    var textData = StringBuilder()
                    textData.append("用户开卡\n")
                    if (cardFee != 0) {
                        textData.append("工本费:" + cardFee + "元\n")
                    }
                    if (userCard.balance != 0) {
                        textData.append("充值金额:" + userCard.balance + "元")
                    }
                    binding?.cardData?.text = textData.toString()
                }
                CardOperationType.CancelCard -> TODO()
                CardOperationType.CheckCard -> TODO()
                CardOperationType.DeductCard -> TODO()
                CardOperationType.Recharge -> TODO()
                CardOperationType.ReplaceCard -> TODO()
                null -> TODO()
            }
        }
@@ -64,15 +98,19 @@
    override fun onNfcBack(intent: Intent) {
        val nfcReadHelper = NfcReadHelper.getInstance(intent, this)
        val cardNumber = nfcReadHelper.getCardNumberNoClose()
        // 使用正常的getCardNumber()方法,它会自动关闭连接
        val cardNumber = nfcReadHelper.getCardNumber()
        if (cardNumber.isNotEmpty() && cardNumber == cardAddr) {
            val nfcWreatHelper = NfcWreatHelper.getInstance(intent, this)
            when (cardType) {
                CardCommon.CLEAN_CARD_TYPE -> {
            when (operationType) {
                CardOperationType.CleanCard -> {
                    var clearCard = ClearCard()
                    nfcWreatHelper.writeData(clearCard.getZeroBytes(), 7, 0) { success, message ->
                        // 确保Toast在主线程中调用
                        runOnUiThread {
                        if (success) {
                            postCardData(cardType, cardAddr, "")
                                postCardData(cardType, cardAddr)
                            // 写卡成功后更新数据库中的isCardWritten状态
                            updateCardWrittenStatus(cardAddr)
                            ToastUtil.show("写卡成功!")
@@ -83,11 +121,13 @@
                        }
                    }
                }
                }
                CardCommon.USER_CARD_TYPE_1 -> {
                    binding?.cardData?.text = "写用户卡"
                CardOperationType.OpenCard -> {
                    nfcWreatHelper.writeUserDataAsync(userCard, object : NFCCallBack {
                        override fun isSusses(flag: Boolean, msg: String?) {
                            // 确保Toast在主线程中调用
                            runOnUiThread {
                            if (flag) {
                                // 写卡成功后更新数据库中的isCardWritten状态
                                updateCardWrittenStatus(cardAddr)
@@ -96,9 +136,17 @@
                                ToastUtil.show("写卡失败: ${msg ?: "未知错误"}")
                            }
                        }
                        }
                    })
                }
                CardOperationType.CancelCard -> TODO()
                CardOperationType.CheckCard -> TODO()
                CardOperationType.DeductCard -> TODO()
                CardOperationType.Recharge -> TODO()
                CardOperationType.ReplaceCard -> TODO()
                null -> TODO()
            }
        } else {
            ToastUtil.show("卡片错误,当前刷的卡与刚刚的卡不一致")
@@ -130,7 +178,7 @@
        }
    }
    fun postCardData(cardType: String, cardAddr: String, remark: String) {
    fun postCardData(cardType: String, cardAddr: String) {
        when (cardType) {
            CardCommon.CHECK_CARD -> {
                binding?.cardData?.text = "写用户卡"
@@ -142,17 +190,14 @@
            map["cardAddr"] = cardAddr
        }
        if (cardType.isNotEmpty()) {
//            map["cardType"] = cardType
            map["operateType"] = "1"
        }
        if (remark.isNotEmpty()) {
            map["remarks"] = remark
        map["operateType"] = operationTypeCode
        if (orderNumber.isNotEmpty()) {
            map["orderNumber"] = orderNumber
        }
        // 使用正确的类型参数
        ApiManager.getInstance().requestPostLoading(
            this,
            "/sell/card/call_back",
            "terminal/card/termCallBack",
            String::class.java,
            map,
            object : SubscriberListener<BaseResponse<String>>() {
generallibrary/src/main/java/com/dayu/general/bean/card/UserCard.kt
@@ -106,19 +106,26 @@
            val data = ByteArray(16)
            try {
                // 设置国家行政区域号(BCD格式,6字节,0-5位)
                val areaCodeBytes = BcdUtil.strToBcd(String.format("%012d", areaNumber))
                System.arraycopy(areaCodeBytes, 0, data, 0, 6)
                val areaNumberStr = if (areaNumber.isBlank() || !areaNumber.all { it.isDigit() }) {
                    "000000000000"
                } else {
                    areaNumber.padStart(12, '0')
                }
                val areaCodeBytes = BcdUtil.strToBcd(areaNumberStr)
                System.arraycopy(areaCodeBytes, 0, data, 0, minOf(areaCodeBytes.size, 6))
                // 设置用户卡编号(HEX格式,2字节,6-7位)
                // 设置用户卡编号(HEX格式,2字节,6-7位) - 修复:确保数组长度正确
                val userCodeBytes = HexUtil.hexToByteArray(HexUtil.get10To16LowHigh(userCodeNumber))
                System.arraycopy(userCodeBytes, 0, data, 6, 2)
                val userCodePadded = ByteArray(2)
                System.arraycopy(userCodeBytes, 0, userCodePadded, 0, minOf(userCodeBytes.size, 2))
                System.arraycopy(userCodePadded, 0, data, 6, 2)
                // 设置卡类型(8位)
                data[8] = HexUtil.hexToByte(cardType)
                // 设置手机号(BCD格式,6字节,9-14位)
                val phoneBytes = BcdUtil.strToBcd(phoneNumber.padStart(12, '0'))
                System.arraycopy(phoneBytes, 0, data, 9, 6)
                System.arraycopy(phoneBytes, 0, data, 9, minOf(phoneBytes.size, 6))
                // 设置校验和(15位)
                data[15] = getByteSum(data)
@@ -135,17 +142,25 @@
            try {
                data[0] = projectCode.toByte()
                // 设置余额
                val balanceBytes = HexUtil.hexToByteArray(HexUtil.get10To16LowHigh(balance))
                System.arraycopy(balanceBytes, 0, data, 1, 4)
                // 设置余额 - 修复:确保数组长度正确
                val balanceHex = HexUtil.get10To16LowHigh(balance)
                val balanceBytes = HexUtil.hexToByteArray(balanceHex)
                val balancePadded = ByteArray(4)
                System.arraycopy(balanceBytes, 0, balancePadded, 0, minOf(balanceBytes.size, 4))
                System.arraycopy(balancePadded, 0, data, 1, 4)
                // 设置剩余水量
                val waterBytes = HexUtil.hexToByteArray(HexUtil.get10To16LowHigh(surplusWater))
                System.arraycopy(waterBytes, 0, data, 5, 4)
                // 设置剩余水量 - 修复:确保数组长度正确
                val waterHex = HexUtil.get10To16LowHigh(surplusWater)
                val waterBytes = HexUtil.hexToByteArray(waterHex)
                val waterPadded = ByteArray(4)
                System.arraycopy(waterBytes, 0, waterPadded, 0, minOf(waterBytes.size, 4))
                System.arraycopy(waterPadded, 0, data, 5, 4)
                // 设置电价
                // 设置电价 - 修复:确保数组长度正确
                val priceBytes = HexUtil.hexToByteArray(HexUtil.floatToHexLowHigh(electricPrice))
                System.arraycopy(priceBytes, 0, data, 9, 2)
                val pricePadded = ByteArray(2)
                System.arraycopy(priceBytes, 0, pricePadded, 0, minOf(priceBytes.size, 2))
                System.arraycopy(pricePadded, 0, data, 9, 2)
                // 设置充值时间
                rechargeDate?.let {
@@ -167,16 +182,24 @@
        fun toBytes(): ByteArray {
            val data = ByteArray(16)
            try {
                // 备份余额和水量数据
                val balanceBytes = HexUtil.hexToByteArray(HexUtil.get10To16LowHigh(balance))
                System.arraycopy(balanceBytes, 0, data, 1, 4)
                // 备份余额和水量数据 - 修复:确保数组长度正确
                val balanceHex = HexUtil.get10To16LowHigh(balance)
                val balanceBytes = HexUtil.hexToByteArray(balanceHex)
                val balancePadded = ByteArray(4)
                System.arraycopy(balanceBytes, 0, balancePadded, 0, minOf(balanceBytes.size, 4))
                System.arraycopy(balancePadded, 0, data, 1, 4)
                val waterBytes = HexUtil.hexToByteArray(HexUtil.get10To16LowHigh(surplusWater))
                System.arraycopy(waterBytes, 0, data, 5, 4)
                val waterHex = HexUtil.get10To16LowHigh(surplusWater)
                val waterBytes = HexUtil.hexToByteArray(waterHex)
                val waterPadded = ByteArray(4)
                System.arraycopy(waterBytes, 0, waterPadded, 0, minOf(waterBytes.size, 4))
                System.arraycopy(waterPadded, 0, data, 5, 4)
                // 设置水价
                // 设置水价 - 修复:确保数组长度正确
                val priceBytes = HexUtil.hexToByteArray(HexUtil.floatToHexLowHigh(waterPrice))
                System.arraycopy(priceBytes, 0, data, 9, 2)
                val pricePadded = ByteArray(2)
                System.arraycopy(priceBytes, 0, pricePadded, 0, minOf(priceBytes.size, 2))
                System.arraycopy(pricePadded, 0, data, 9, 2)
                // 设置充值时间
                rechargeDate?.let {
generallibrary/src/main/java/com/dayu/general/bean/db/CardRegistrationBean.kt
@@ -11,7 +11,7 @@
    val userName: String, // 姓名
    val idCard: String, // 身份证号
    val clientId: String, // 农户Id
    val cardFee: Double, // 工本费
    val cardFee: Int, // 工本费
    val remark: String, // 备注
    val paymentMethod: Int, // 支付方式
    val orderId: String? = null, // 订单id,初始为null,后续更新
generallibrary/src/main/java/com/dayu/general/bean/net/NewCardResult.kt
@@ -1,7 +1,7 @@
package com.dayu.general.bean.net
data class NewCardDataResult(
    var projectNo: String,
    var projectNo: Int,
    var cardNum: String,
    var balance: String,
    var waterPrice: String,
generallibrary/src/main/java/com/dayu/general/tool/CardOperationType.kt
New file
@@ -0,0 +1,28 @@
package com.dayu.general.tool
/**
 * 卡片操作类型
 * 用于区分不同的卡片操作
 */
sealed class CardOperationType(val code: Int, val description: String) {
    object OpenCard : CardOperationType(1, "开卡")
    object Recharge : CardOperationType(2, "充值")
    object CancelCard : CardOperationType(3, "销卡")
    object ReplaceCard : CardOperationType(4, "补卡")
    object DeductCard : CardOperationType(5, "补扣")
    object CleanCard : CardOperationType(6, "清零卡")
    object CheckCard : CardOperationType(7, "检查卡")
    companion object {
        fun fromCode(code: Int): CardOperationType? {
            return when (code) {
                1 -> OpenCard
                2 -> Recharge
                3 -> CancelCard
                4 -> ReplaceCard
                5 -> DeductCard
                else -> null
            }
        }
    }
}
generallibrary/src/main/res/layout/activity_new_card_ge.xml
@@ -303,7 +303,7 @@
                                android:layout_height="wrap_content"
                                android:background="@null"
                                android:hint="请输入工本费(选填)"
                                android:inputType="numberDecimal"
                                android:inputType="number"
                                android:textColor="#333333"
                                android:textColorHint="#BBBBBB"
                                android:textSize="@dimen/new_card_size" />