左晓为主开发手持机充值管理机
zuojincheng
2 天以前 dd0f9e5f533d868d68c5fc343a44356b537b3988
feat(nfc): 新增用户卡写入功能并优化开卡流程

- 新增用户卡写入功能,支持异步写卡- 优化开卡流程,增加订单号和用户信息传递
- 修复部分数据类型不匹配问题
- 优化异常处理和日志记录
7个文件已修改
3个文件已添加
387 ■■■■ 已修改文件
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/NativeNfcWriteHelper.java 137 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/NfcWriteAdapter.java 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/NewCard2Activity.kt 88 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/NfcWreatActivity.kt 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/bean/card/UserCard.kt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/bean/db/CardRegistrationBean.kt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/bean/net/NewCardInfo.kt 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/bean/net/NewCardResult.kt 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/tool/NfcWreatHelper.kt 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/utils/DateUtils.kt 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/NativeNfcWriteHelper.java
@@ -25,7 +25,6 @@
    private static NativeNfcWriteHelper helper;
    public void setIntent(Intent intent) {
        this.tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    }
@@ -44,7 +43,6 @@
        return helper;
    }
    /**
     * 写卡
     *
@@ -59,15 +57,15 @@
                MifareClassic mfc = MifareClassic.get(tag);
                if (null != mfc) {
                    try {
                        //连接NFC
                        // 连接NFC
                        mfc.connect();
                        //验证扇区密码
                        // 验证扇区密码
                        boolean isOpen = false;
                        for (int i = 0; i < listKeyA.size(); i++) {
                            if (mfc.authenticateSectorWithKeyA(a, listKeyA.get(i))) {
                                isOpen = true;
                                if (listKeyA.get(i).equals(defauleKey)) {
                                    //当前为默认白卡密码时写卡时修改密码
                                    // 当前为默认白卡密码时写卡时修改密码
                                    changePasword(a, mfc);
                                }
                                break;
@@ -84,7 +82,7 @@
                                    data = userCard.getTwoBytes();
                                }
                                int bIndex = mfc.sectorToBlock(a);
                                //写卡
                                // 写卡
                                mfc.writeBlock(bIndex + b, data);
                            }
                            return true;
@@ -109,6 +107,85 @@
        return false;
    }
    /**
     * 写卡(带回调)
     *
     * @param userCard 用户卡内容
     * @param sector   扇区
     * @param callBack 回调接口
     */
    public boolean writeUserData(BaseUserCardCard userCard, int sector, NFCCallBack callBack) {
        if (userCard != null) {
            int a = sector;
            try {
                MifareClassic mfc = MifareClassic.get(tag);
                if (null != mfc) {
                    try {
                        // 连接NFC
                        mfc.connect();
                        // 验证扇区密码
                        boolean isOpen = false;
                        for (int i = 0; i < listKeyA.size(); i++) {
                            if (mfc.authenticateSectorWithKeyA(a, listKeyA.get(i))) {
                                isOpen = true;
                                if (listKeyA.get(i).equals(defauleKey)) {
                                    // 当前为默认白卡密码时写卡时修改密码
                                    changePasword(a, mfc);
                                }
                                break;
                            }
                        }
                        if (isOpen) {
                            for (int b = 0; b < 3; b++) {
                                byte[] data;
                                if (b == 0) {
                                    data = userCard.getZeroBytes();
                                } else if (b == 1) {
                                    data = userCard.getOneBytes();
                                } else {
                                    data = userCard.getTwoBytes();
                                }
                                int bIndex = mfc.sectorToBlock(a);
                                // 写卡
                                mfc.writeBlock(bIndex + b, data);
                            }
                            if (callBack != null) {
                                callBack.isSusses(true, "用户数据写入成功");
                            }
                            return true;
                        }
                        if (callBack != null) {
                            callBack.isSusses(false, "扇区" + a + "密码验证失败");
                        }
                        return false;
                    } catch (Exception e) {
                        e.printStackTrace();
                        if (callBack != null) {
                            callBack.isSusses(false, "扇区" + a + "写卡异常: " + e.getMessage());
                        }
                        return false;
                    } finally {
                        try {
                            mfc.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                if (callBack != null) {
                    callBack.isSusses(false, "写卡异常: " + e.getMessage());
                }
                return false;
            }
        } else {
            if (callBack != null) {
                callBack.isSusses(false, "用户卡数据为空");
            }
        }
        return false;
    }
    /**
     * 写卡
@@ -136,36 +213,36 @@
                MifareClassic mfc = MifareClassic.get(tag);
                if (null != mfc) {
                    try {
                        //连接NFC
                        // 连接NFC
                        if (isConnect) {
                            mfc.connect();
                        }
                        //获取扇区数量
                        // 获取扇区数量
                        int count = mfc.getSectorCount();
                        //如果传进来的扇区大了或者小了直接退出方法
                        // 如果传进来的扇区大了或者小了直接退出方法
                        if (a > count - 1 || a < 0) {
                            if (callBack != null) {
                                callBack.isSusses(false, "扇区错误--" + a);
                            }
                            return false;
                        }
                        //获取写的扇区的块的数量
                        // 获取写的扇区的块的数量
                        int bCount = mfc.getBlockCountInSector(a);
                        //如果输入的块大了或者小了也是直接退出
                        // 如果输入的块大了或者小了也是直接退出
                        if (b > bCount - 1 || b < 0) {
                            if (callBack != null) {
                                callBack.isSusses(false, "块区错误--" + b);
                            }
                            return false;
                        }
                        //验证扇区密码
                        // 验证扇区密码
                        boolean isOpen = false;
                        if (listKeyA.size() != 0) {
                            for (int i = 0; i < listKeyA.size(); i++) {
                                if (mfc.authenticateSectorWithKeyA(0, listKeyA.get(i))) {
                                    isOpen = true;
                                    if (listKeyA.get(i).equals(defauleKey)) {
                                        //当前为默认白卡密码时写卡时修改密码
                                        // 当前为默认白卡密码时写卡时修改密码
                                        changePasword(a, mfc);
                                    }
                                    break;
@@ -181,7 +258,7 @@
                        }
                        if (isOpen) {
                            int bIndex = mfc.sectorToBlock(a);
                            //写卡
                            // 写卡
                            mfc.writeBlock(bIndex + b, str);
                            // 校验写入数据是否正确
                            boolean isVerified = true;
@@ -246,16 +323,15 @@
        return false;
    }
    /**
     * 修改密码
     *
     * @param         书写的扇区
     * @param 书写的扇区
     * @param passWord 密码
     * @return
     */
    public boolean changePasword( List<byte[]> passWord, boolean isConnect, boolean isClose, NFCCallBack callBack) {
        if (passWord == null || passWord.size()!=16) {
    public boolean changePasword(List<byte[]> passWord, boolean isConnect, boolean isClose, NFCCallBack callBack) {
        if (passWord == null || passWord.size() != 16) {
            if (callBack != null) {
                callBack.isSusses(false, "密码数组为空或长度不为16");
            }
@@ -276,7 +352,7 @@
                    mfc.connect();
                }
                for (int i = 0; i < passWord.size(); i++) {
                    byte [] passWordItem = passWord.get(i);
                    byte[] passWordItem = passWord.get(i);
                    // 验证扇区密码
                    boolean isAuthenticated = false;
@@ -358,7 +434,7 @@
    /**
     * 修改密码
     *
     * @param a  扇区
     * @param a   扇区
     * @param mfc MifareClassic实例
     * @return
     */
@@ -367,24 +443,24 @@
        byte[] data = new byte[16];
        if (null != mfc) {
            try {
                //将密码转换为keyA
                // 将密码转换为keyA
                byte[] dataA = HexUtil.hexToByteArray(companyKeyA);
                for (int i = 0; i < dataA.length; i++) {
                    data[i] = dataA[i];
                }
                //输入控制位
                // 输入控制位
                data[6] = (byte) 0xFF;
                data[7] = (byte) 0x07;
                data[8] = (byte) 0x80;
                data[9] = (byte) 0x69;
                byte[] dataB = HexUtil.hexToByteArray(companyKeyB);
                //将密码转换为KeyB
                // 将密码转换为KeyB
                for (int i = 0; i < dataB.length; i++) {
                    data[i + 10] = dataB[i];
                }
                int bIndex = mfc.sectorToBlock(a);
                int bCount = mfc.getBlockCountInSector(a);
                //写到扇区的最后一个块
                // 写到扇区的最后一个块
                mfc.writeBlock(bIndex + bCount - 1, data);
                return true;
@@ -399,8 +475,6 @@
        return false;
    }
    /**
     * 初始化卡
     *
@@ -412,15 +486,15 @@
            MifareClassic mfc = MifareClassic.get(tag);
            if (null != mfc) {
                try {
                    //连接NFC
                    // 连接NFC
                    mfc.connect();
                    //获取扇区数量
                    // 获取扇区数量
                    int count = mfc.getSectorCount();
                    byte[] data = new byte[16];
                    String initData = "FFFFFFFFFFFFFF078069FFFFFFFFFFFF";
                    byte[] initDataBytes = HexUtil.hexToByteArray(initData);
                    for (int sector = 0; sector < count; sector++) {
                        //验证扇区密码
                        // 验证扇区密码
                        boolean isOpen = false;
                        for (int i = 0; i < listKeyA.size(); i++) {
                            if (mfc.authenticateSectorWithKeyA(sector, listKeyA.get(i))) {
@@ -429,7 +503,7 @@
                            }
                        }
                        if (isOpen) {
                            //获取写的扇区的块的数量
                            // 获取写的扇区的块的数量
                            int blockCount = mfc.getBlockCountInSector(sector);
                            int blockIndex = mfc.sectorToBlock(sector);
                            for (int block = 0; block < blockCount; block++) {
@@ -444,7 +518,7 @@
                                } else {
                                    mfc.writeBlock(blockIndex, initDataBytes);
                                }
                                //写卡
                                // 写卡
                                blockIndex++;
                            }
                        }
@@ -467,6 +541,5 @@
        }
        return false;
    }
}
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/NfcWriteAdapter.java
@@ -33,11 +33,27 @@
    }
    @Override
    public boolean writeUserData(BaseUserCardCard userCard,int sector) {
    public boolean writeUserData(BaseUserCardCard userCard, int sector) {
        switch (BaseNfcActivity.adapterType) {
            case ModelUtils.defaultType:
                return nativeNfcWriteHelper.writeUserData(userCard, sector);
        }
        return false;
    }
    /**
     * 写用户数据(带回调)
     *
     * @param userCard 用户卡数据
     * @param sector   扇区
     * @param callBack 回调接口
     * @return 是否成功
     */
    public boolean writeUserData(BaseUserCardCard userCard, int sector, NFCCallBack callBack) {
        switch (BaseNfcActivity.adapterType) {
            case ModelUtils.defaultType:
                return nativeNfcWriteHelper.writeUserData(userCard, sector, callBack);
        }
        return false;
    }
@@ -56,7 +72,7 @@
    public boolean writeData(byte[] str, int a, int b, NFCCallBack callBack) {
        switch (BaseNfcActivity.adapterType) {
            case ModelUtils.defaultType:
                return nativeNfcWriteHelper.writeData(str, a, b,callBack);
                return nativeNfcWriteHelper.writeData(str, a, b, callBack);
        }
        return false;
@@ -66,7 +82,7 @@
    public boolean writeData(byte[] str, int a, int b, boolean isConnect, NFCCallBack callBack) {
        switch (BaseNfcActivity.adapterType) {
            case ModelUtils.defaultType:
                return nativeNfcWriteHelper.writeData(str, a, b,isConnect,callBack);
                return nativeNfcWriteHelper.writeData(str, a, b, isConnect, callBack);
        }
        return false;
@@ -82,10 +98,10 @@
        return false;
    }
    public boolean changePasword( List<byte[]> passWord, boolean isConnect, boolean isClose, NFCCallBack callBack) {
    public boolean changePasword(List<byte[]> passWord, boolean isConnect, boolean isClose, NFCCallBack callBack) {
        switch (BaseNfcActivity.adapterType) {
            case ModelUtils.defaultType:
                return nativeNfcWriteHelper.changePasword(passWord,isConnect, isClose,callBack);
                return nativeNfcWriteHelper.changePasword(passWord, isConnect, isClose, callBack);
        }
        return false;
    }
generallibrary/src/main/java/com/dayu/general/activity/NewCard2Activity.kt
@@ -13,15 +13,23 @@
import com.dayu.baselibrary.view.TitleBar.ClickType_LEFT_IMAGE
import com.dayu.general.BaseApplication
import com.dayu.general.R
import com.dayu.general.bean.card.UserCard
import com.dayu.general.bean.db.CardRegistrationBean
import com.dayu.general.bean.net.ClientInfo
import com.dayu.general.bean.net.NewCardDataResult
import com.dayu.general.bean.net.PaymentMethod
import com.dayu.general.bean.net.PaymentMethodResponse
import com.dayu.general.dao.BaseDaoSingleton
import com.dayu.general.databinding.ActivityNewCardGeBinding
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.NfcReadHelper
import com.dayu.general.tool.NfcWreatHelper
import com.dayu.general.utils.DateUtils
import com.tencent.bugly.crashreport.CrashReport
import kotlinx.coroutines.launch
import java.util.Calendar
/**
 * Description: 用户开卡界面(同步修改白卡密码)
@@ -50,43 +58,17 @@
    // 客户ID
    private var clientId: String = ""
    private lateinit var clientInfo : ClientInfo
    // 是否已读卡
    private var isReadCard: Boolean = false
    private var orderId:String=""
    companion object {
        private const val TAG = "NewCard2Activity"
    }
    // 支付方式数据类
    data class PaymentMethod(
        val id: Long,
        val name: String,
        val remarks: String,
        val deleted: Int
    )
    // 支付方式接口返回数据类
    data class PaymentMethodResponse(
        val itemTotal: Any?,
        val obj: List<PaymentMethod>,
        val pageCurr: Any?,
        val pageSize: Any?,
        val pageTotal: Any?
    )
    // 用户信息数据类
    data class ClientInfo(
        val clientId: String,
        val clientNum: String,
        val name: String,
        val districtNum: String,
        val phone: String,
        val idCard: String,
        val villageName: String,
        val address: String,
        val cardCount: Int,
        val operateDt: String
    )
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
@@ -134,7 +116,7 @@
            object : SubscriberListener<BaseResponse<ClientInfo>>() {
                override fun onNext(response: BaseResponse<ClientInfo>) {
                    if (response.success) {
                        val clientInfo = response.content
                        clientInfo = response.content!!
                        if (clientInfo != null) {
                            // 显示客户信息到界面
                            displayClientInfo(clientInfo)
@@ -337,47 +319,69 @@
        // 构建请求参数
        val params = HashMap<String, Any>()
        params["cardAddr"] = cardPhysicalId // 水卡地址(物理ID)
        params["clientNum"] = binding.newCardFarmerCode.text.toString() // 农户编号
        params["cardCost"] = (cardFee * 100).toInt() // 购卡金额(工本费)转为分
        params["amount"] = (rechargeAmount * 100).toInt() // 充值金额转为分
        params["clientId"] = clientId // 农户ID
        params["cardCost"] = cardFee  // 购卡金额(工本费)(元)
        params["amount"] = rechargeAmount  // 充值金额(元)
        params["paymentId"] = paymentId // 支付方式ID
        params["remarks"] = remark // 备注
        params["protocol"] = "p206V1_0_1" // 协议
        params["operator"] = BaseApplication.userId // 操作人ID
        // 执行卡片激活API请求
        ApiManager.getInstance().requestPostLoading(
            this,
            "sell/card/active",
            String::class.java,
            "terminal/card/termActiveCard",
            NewCardDataResult::class.java,
            params,
            object : SubscriberListener<BaseResponse<String>>() {
                override fun onNext(response: BaseResponse<String>) {
            object : SubscriberListener<BaseResponse<NewCardDataResult>>() {
                override fun onNext(response: BaseResponse<NewCardDataResult>) {
                    if (response.success) {
                        orderId=response.content?.orderNo.toString()
                        // 保存开卡信息到数据库
                        val cardRegistration = CardRegistrationBean(
                            cardNumber = cardPhysicalId,
                            userName = binding.newCardUserName.text.toString(),
                            idCard = binding.newCardIdCard.text.toString(),
                            farmerCode = binding.newCardFarmerCode.text.toString(),
                            clientId = clientId,
                            cardFee = cardFee,
                            remark = binding.newCardRemark.text.toString(),
                            paymentMethod = paymentId.toInt(),
                            isReported = true,
                            isCardWritten = true
                        )
                            isCardWritten = true,
                            operatorId = orderId,
                            )
                        // 使用协程在后台线程中保存数据
                        lifecycleScope.launch {
                            try {
                                BaseDaoSingleton.getInstance(this@NewCard2Activity)
                                    .cardRegistrationDao().insert(cardRegistration)
                                Toast.makeText(
                                    this@NewCard2Activity,
                                    "开卡成功",
                                    Toast.LENGTH_SHORT
                                ).show()
                                setResult(RESULT_OK)
                                finish()
                                Intent(this@NewCard2Activity, NfcWreatActivity::class.java).apply {
                                    putExtra("cardType", USER_CARD_TYPE_1)
                                    putExtra("orderId", orderId)
                                    putExtra("cardAddr", cardPhysicalId)
                                    var userCard = UserCard()
                                    userCard.areaNumber =clientInfo.districtNum
                                    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.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)
                                    startActivity(this)
                                }
                            } catch (e: Exception) {
                                CrashReport.postCatchedException(e)
                                Toast.makeText(
generallibrary/src/main/java/com/dayu/general/activity/NfcWreatActivity.kt
@@ -1,10 +1,11 @@
    package com.dayu.general.activity
package com.dayu.general.activity
import android.content.Intent
import android.os.Bundle
import com.dayu.baselibrary.net.subscribers.SubscriberListener
import com.dayu.baselibrary.utils.ToastUtil
import com.dayu.general.bean.card.ClearCard
import com.dayu.general.bean.card.UserCard
import com.dayu.general.tool.CardCommon
import com.dayu.general.databinding.ActivityNfcWriteGeBinding
import com.dayu.general.net.ApiManager
@@ -22,6 +23,7 @@
    var cardType = ""
    var orderId = ""
    var cardAddr = ""
    private lateinit var userCard: UserCard
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
@@ -37,6 +39,12 @@
        cardType = intent?.getStringExtra("cardType") ?: ""
        orderId = intent?.getStringExtra("orderId") ?: ""
        cardAddr = intent?.getStringExtra("cardAddr") ?: ""
        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 -> {
@@ -68,6 +76,12 @@
                        }
                    }
                }
                CardCommon.USER_CARD_TYPE_1 -> {
                    binding?.cardData?.text = "写用户卡"
                    nfcWreatHelper.writeUserData(userCard)
                }
            }
        } else {
            ToastUtil.show("卡片错误,当前刷的卡与刚刚的卡不一致")
generallibrary/src/main/java/com/dayu/general/bean/card/UserCard.kt
@@ -12,7 +12,7 @@
 */
class UserCard : BaseUserCardCard(), Serializable {
    var cardType: String = USER_CARD_TYPE_1 // 卡类型:A1终端写卡 A8刷卡开泵后值 A2叠加充值
    var areaNumber: Int = 0 // 国家行政区域号(12位BCD,精确到村)
    var areaNumber: String = "" // 国家行政区域号(12位BCD,精确到村)
    var userCode: String = "" // 用户编号BCD
    var userCodeNumber: Int = 0 // 用户卡编号(HEX)
    var phoneNumber: String = "" // 手机号(BCD)
@@ -55,7 +55,7 @@
            userCard.apply {
                // 解析国家行政区域号(0-5位)
                val areaCodeBytes = zero.copyOfRange(0, 6)
                areaNumber = BcdUtil.bcdToStr(areaCodeBytes).toInt()
                areaNumber = BcdUtil.bcdToStr(areaCodeBytes)
                // 解析用户卡编号(6-7位)
                val userCodeNumberBytes = zero.copyOfRange(6, 8)
generallibrary/src/main/java/com/dayu/general/bean/db/CardRegistrationBean.kt
@@ -10,7 +10,7 @@
    val cardNumber: String, // IC卡卡号
    val userName: String, // 姓名
    val idCard: String, // 身份证号
    val farmerCode: String, // 农户编号
    val clientId: String, // 农户Id
    val cardFee: Double, // 工本费
    val remark: String, // 备注
    val paymentMethod: Int, // 支付方式
generallibrary/src/main/java/com/dayu/general/bean/net/NewCardInfo.kt
New file
@@ -0,0 +1,32 @@
package com.dayu.general.bean.net
// 支付方式数据类
data class PaymentMethod(
    val id: Long,
    val name: String,
    val remarks: String,
    val deleted: Int
)
// 支付方式接口返回数据类
data class PaymentMethodResponse(
    val itemTotal: Any?,
    val obj: List<PaymentMethod>,
    val pageCurr: Any?,
    val pageSize: Any?,
    val pageTotal: Any?
)
// 用户信息数据类
data class ClientInfo(
    val clientId: String,
    val clientNum: String,
    val name: String,
    val districtNum: String,
    val phone: String,
    val idCard: String,
    val villageName: String,
    val address: String,
    val cardCount: Int,
    val operateDt: String
)
generallibrary/src/main/java/com/dayu/general/bean/net/NewCardResult.kt
New file
@@ -0,0 +1,10 @@
package com.dayu.general.bean.net
data class NewCardDataResult(
    var projectNo: String,
    var cardNum: String,
    var balance: String,
    var waterPrice: String,
    var time: String,
    var orderNo: String
)
generallibrary/src/main/java/com/dayu/general/tool/NfcWreatHelper.kt
@@ -146,6 +146,32 @@
    }
    /**
     * 写卡(异步)
     *
     * @param userCard 用户卡内容
     * @param callBack 操作结果和消息回调
     */
    fun writeUserDataAsync(userCard: UserCard, callBack: NFCCallBack): Disposable {
        showLoading()
        val disposable = Observable.fromCallable {
            writeUserData(userCard, callBack)
        }
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe({ result ->
            hideLoading()
            // 结果已经在writeUserData中通过callBack回调了
        }, { error ->
            hideLoading()
            error.printStackTrace()
            callBack.isSusses(false, "异步写卡异常: ${error.message}")
        })
        compositeDisposable.add(disposable)
        return disposable
    }
    /**
     * 写卡
     *
     * @param userCard 用户卡内容
@@ -161,6 +187,22 @@
    }
    /**
     * 写卡
     *
     * @param userCard 用户卡内容
     * @param callBack 回调接口
     */
    fun writeUserData(userCard: UserCard, callBack: NFCCallBack): Boolean {
        try {
            return adapter.writeUserData(userCard, 7, callBack)
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
            callBack.isSusses(false, "写卡异常: ${e.message}")
        }
        return false
    }
    /**
     * 修改密码(异步)
     *
     * @param ps 密码列表
generallibrary/src/main/java/com/dayu/general/utils/DateUtils.kt
New file
@@ -0,0 +1,30 @@
package com.dayu.general.utils
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
object DateUtils {
    private const val DATE_PATTERN = "yyyy-MM-dd HH:mm:ss"
    private val sdf by lazy { SimpleDateFormat(DATE_PATTERN, Locale.getDefault()) }
    /**
     * 将字符串转换为 Calendar 对象
     * @param dateStr 时间字符串,格式:yyyy-MM-dd HH:mm:ss
     * @return 解析后的 Calendar 对象,失败返回 null
     */
    fun parseStringToCalendar(dateStr: String?): Calendar? {
        if (dateStr.isNullOrEmpty()) return null
        return try {
            val date = sdf.parse(dateStr)
            val calendar = Calendar.getInstance()
            calendar.time = date ?: return null
            calendar
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }
}