左晓为主开发手持机充值管理机
zuoxiao
15 小时以前 80eb39909e0d5c181dc8d6e282a292146ec51e21
refactor(card): 重构卡片制作流程并添加项目号支持

- 移除了 CardReplaceActivity 中的支付方式相关代码
- 修改了 CardReturnActivity 和 CardWriteSuccessActivity 中的逻辑
- 在 MainActivity 中添加了 postCardData 方法,用于上报写卡结果
- 更新了 ManageListActivity 和 ManagerReadActivity,支持项目号输入和验证
- 修改了 NewCard2Activity 中的支付方式获取接口
18个文件已修改
1442 ■■■■■ 已修改文件
README.md 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/BaseApplication.kt 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/CardReplaceActivity.kt 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/CardReturnActivity.kt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/CardWriteSuccessActivity.kt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/MainActivity.kt 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/ManageListActivity.kt 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/ManagerReadActivity.kt 293 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/NewCard2Activity.kt 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/NfcWreatActivity.kt 634 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/RechargeDetailActivity.kt 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/bean/net/NewCardInfo.kt 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/model/CardInfoModel.kt 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/tool/BaseCommon.kt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/tool/CardOperationType.kt 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/tool/NfcWreatHelper.kt 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/layout/activity_card_replace.xml 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/layout/activity_manager_read.xml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md
@@ -592,7 +592,7 @@
private fun getPaymentMethods() {
    ApiManager.getInstance().requestGetLoading(
        this,
        "sell/paymentmethod/get",
        "terminal/paymentmethod/get",
        PaymentMethodResponse::class.java,
        null,
        object : SubscriberListener<BaseResponse<PaymentMethodResponse>>() {
generallibrary/src/main/java/com/dayu/general/BaseApplication.kt
@@ -55,6 +55,13 @@
        }
        /**
         * 获取MainActivity实例
         */
        fun getMainActivity(): com.dayu.general.activity.MainActivity? {
            return mainActivityInstance
        }
        /**
         * 请求获取水价,如果为空则调用MainActivity的获取方法
         */
        fun requestWaterPrice(): Double {
generallibrary/src/main/java/com/dayu/general/activity/CardReplaceActivity.kt
@@ -66,8 +66,6 @@
        cardNum = intent.getStringExtra("cardNum")
        initView()
        // 获取支付方式
        getPaymentMethods()
        // 无论是否有clientNum,都先显示读卡界面,等待用户刷新卡
        resetToReadingState()
@@ -85,100 +83,8 @@
        }
    }
    /**
     * 获取支付方式列表
     */
    private fun getPaymentMethods() {
        ApiManager.getInstance().requestGetLoading(
            this,
            "sell/paymentmethod/get",
            PaymentMethodResponse::class.java,
            null,
            object : SubscriberListener<BaseResponse<PaymentMethodResponse>>() {
                override fun onNext(response: BaseResponse<PaymentMethodResponse>) {
                    if (response.success) {
                        // 获取支付方式列表
                        val paymentMethods = response.content?.obj ?: listOf()
                        if (paymentMethods.isNotEmpty()) {
                            paymentMethodList = paymentMethods
                            // 更新支付方式显示
                            updatePaymentMethodRadioGroup()
                        }
                    } else {
                        Toast.makeText(
                            this@CardReplaceActivity,
                            "获取支付方式失败: ${response.msg}",
                            Toast.LENGTH_SHORT
                        ).show()
                    }
                }
                override fun onError(e: Throwable?) {
                    super.onError(e)
                    Toast.makeText(
                        this@CardReplaceActivity,
                        "获取支付方式失败: ${e?.message ?: "网络异常"}",
                        Toast.LENGTH_SHORT
                    ).show()
                }
            }
        )
    }
    /**
     * 更新支付方式RadioGroup
     */
    private fun updatePaymentMethodRadioGroup() {
        // 清空原有RadioButton
        binding.paymentMethodGroup.removeAllViews()
        // 动态添加RadioButton
        paymentMethodList.forEachIndexed { index, method ->
            val radioButton = RadioButton(this)
            radioButton.id = View.generateViewId() // 生成唯一ID
            radioButton.layoutParams = android.widget.LinearLayout.LayoutParams(
                0,
                resources.getDimensionPixelSize(R.dimen.dimen_40),
                1.0f
            )
            // 如果不是最后一个按钮,添加右边距
            if (index < paymentMethodList.size - 1) {
                (radioButton.layoutParams as android.widget.LinearLayout.LayoutParams).rightMargin =
                    resources.getDimensionPixelSize(R.dimen.dimen_15)
            }
            radioButton.text = method.name
            radioButton.background = resources.getDrawable(R.drawable.radio_selector)
            radioButton.buttonDrawable = null
            radioButton.gravity = android.view.Gravity.CENTER
            radioButton.setTextColor(resources.getColorStateList(R.color.radio_button_text_color))
            radioButton.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
            // 添加到RadioGroup
            binding.paymentMethodGroup.addView(radioButton)
            // 默认选中第一个
            if (index == 0) {
                radioButton.isChecked = true
                paymentMethod = method.name
                paymentId = method.id
            }
        }
        // 设置支付方式选择监听
        binding.paymentMethodGroup.setOnCheckedChangeListener { group, checkedId ->
            // 根据选中的ID获取支付方式
            for (i in 0 until group.childCount) {
                val radioButton = group.getChildAt(i) as RadioButton
                if (radioButton.id == checkedId) {
                    paymentMethod = radioButton.text.toString()
                    paymentId = paymentMethodList[i].id
                    break
                }
            }
        }
    }
    /**
     * 重置到读卡状态
generallibrary/src/main/java/com/dayu/general/activity/CardReturnActivity.kt
@@ -381,7 +381,7 @@
        val intent = Intent(this, NfcWreatActivity::class.java).apply {
            putExtra("cardAddr", cardNumber)
            putExtra("operationTypeCode", CardOperationType.ReturnCard.code) // 使用返还类型进行写卡
            putExtra("operationTypeCode", CardOperationType.SUPPLEMENT.code) // 使用返还类型进行写卡
            putExtra("orderNumber", returnResult.orderNo)
            putExtra("returnAmount", returnAmount)
            putExtra("userCard", updatedUserCard as java.io.Serializable)
generallibrary/src/main/java/com/dayu/general/activity/CardWriteSuccessActivity.kt
@@ -53,7 +53,7 @@
                binding.successMessage.text = message
            }
            CardOperationType.ReturnCard -> {
            CardOperationType.SUPPLEMENT -> {
                // 设置标题
                binding.titleBar.setCenterText("返还成功")
generallibrary/src/main/java/com/dayu/general/activity/MainActivity.kt
@@ -9,6 +9,7 @@
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.dayu.baselibrary.net.subscribers.SubscriberListener
import com.dayu.baselibrary.utils.ToastUtil
import com.dayu.general.BaseApplication
@@ -16,9 +17,13 @@
import com.dayu.general.adapter.TabAdapter
import com.dayu.general.bean.net.UserInfoResult
import com.dayu.general.bean.net.WaterPriceResult
import com.dayu.general.dao.BaseDaoSingleton
import com.dayu.general.databinding.ActivityMainBinding
import com.dayu.general.net.ApiManager
import com.dayu.general.net.BaseResponse
import com.dayu.general.tool.CardOperationType
import com.tencent.bugly.crashreport.CrashReport
import kotlinx.coroutines.launch
class MainActivity : BaseNfcActivity() {
@@ -232,4 +237,111 @@
        }
        return super.onKeyDown(keyCode, event)
    }
    /**
     * 向服务器上报写卡操作结果
     * 写卡成功后调用此方法通知服务器操作完成,成功后更新本地数据库isReported为true
     *
     * @param cardAddr 卡地址/卡号
     * @param operationTypeCode 操作类型代码
     * @param orderNumber 订单号
     * @param regionNumber 区域号
     * @param projectNumber 项目号
     */
    fun postCardData(
        cardAddr: String,
        operationTypeCode: Int,
        orderNumber: String = "",
        regionNumber: String = "",
        projectNumber: String = ""
    ) {
        val map = mutableMapOf<String, Any>()
        // 添加卡地址参数
        if (cardAddr.isNotEmpty()) {
            map["cardAddr"] = cardAddr
        }
        // 添加操作类型参数
        // 判断是否为管理卡制作操作类型(100-108),如果是则传递MANAGEMENT_CARD_WRITE的值(7)
        val operateTypeToSend = if (operationTypeCode in 100..108) {
            CardOperationType.MANAGEMENT_CARD_WRITE.code
        } else {
            operationTypeCode
        }
        map["operateType"] = operateTypeToSend
        // 添加订单号参数(如果存在)
        if (orderNumber.isNotEmpty()) {
            map["orderNumber"] = orderNumber
        }
        // 添加区域号和项目号参数
        if (regionNumber.isNotEmpty()) {
            map["regionNumber"] = regionNumber
        }
        if (projectNumber.isNotEmpty()) {
            map["projectNumber"] = projectNumber
        }
        // 调用服务器接口上报操作结果
        ApiManager.getInstance().requestPostHideLoading(
            this,
            "terminal/card/termCallBack", // 终端写卡回调接口
            String::class.java,
            map,
            object : SubscriberListener<BaseResponse<String>>() {
                override fun onNext(t: BaseResponse<String>) {
                    if (t.success) {
                        // 上报成功,更新本地数据库isReported为true
                        updateCardReportedStatus(cardAddr, orderNumber)
                    } else {
                        // 上报失败,记录错误但不影响用户操作
                        CrashReport.postCatchedException(Exception("上报写卡结果失败: ${t.msg}"))
                    }
                }
                override fun onError(e: Throwable?) {
                    super.onError(e)
                    // 网络错误,记录错误但不影响用户操作
                    CrashReport.postCatchedException(e ?: Exception("上报写卡结果网络错误"))
                }
            }
        )
    }
    /**
     * 更新本地数据库中的上报状态
     * 将CardRegistrationBean中的isReported状态设置为true
     *
     * @param cardNumber 卡号
     * @param orderNumber 订单号
     */
    private fun updateCardReportedStatus(cardNumber: String, orderNumber: String = "") {
        lifecycleScope.launch {
            try {
                val cardRegistrationDao = BaseDaoSingleton.getInstance(this@MainActivity)
                    .cardRegistrationDao()
                // 根据订单号查找CardRegistrationBean记录
                val cardRegistration = if (orderNumber.isNotEmpty()) {
                    cardRegistrationDao.getByOrderId(orderNumber)
                } else {
                    // 如果没有订单号,则通过卡号查找
                    cardRegistrationDao.getByCardNumber(cardNumber)
                }
                if (cardRegistration != null) {
                    // 创建更新后的CardRegistrationBean对象,将isReported设置为true
                    val updatedCardRegistration = cardRegistration.copy(isReported = true)
                    // 更新数据库记录
                    cardRegistrationDao.update(updatedCardRegistration)
                }
            } catch (e: Exception) {
                // 记录异常信息,但不影响用户操作
                CrashReport.postCatchedException(e)
                e.printStackTrace()
            }
        }
    }
}
generallibrary/src/main/java/com/dayu/general/activity/ManageListActivity.kt
@@ -24,7 +24,7 @@
        binding?.tvAreaCard?.setOnClickListener {
            var intent = Intent(this, ManagerReadActivity::class.java).apply {
                putExtra("cardType", CardCommon.REGION_CARD)
                putExtra("operationTypeCode", CardOperationType.RegionCard.code)
                putExtra("operationTypeCode", CardOperationType.RegionCardMake.code)
            }
            startActivity(intent)
        }
@@ -33,7 +33,7 @@
        binding?.tvCheckCard?.setOnClickListener {
            var intent = Intent(this, ManagerReadActivity::class.java).apply {
                putExtra("cardType", CardCommon.CHECK_CARD)
                putExtra("operationTypeCode", CardOperationType.CheckCard.code)
                putExtra("operationTypeCode", CardOperationType.CheckCardMake.code)
            }
            startActivity(intent)
        }
@@ -42,7 +42,7 @@
        binding?.tvDebugCard?.setOnClickListener {
            var intent = Intent(this, ManagerReadActivity::class.java).apply {
                putExtra("cardType", CardCommon.DEBUG_CARD)
                putExtra("operationTypeCode", CardOperationType.DebugCard.code)
                putExtra("operationTypeCode", CardOperationType.DebugCardMake.code)
            }
            startActivity(intent)
        }
@@ -51,7 +51,7 @@
        binding?.tvCleanCard?.setOnClickListener {
            var intent = Intent(this, ManagerReadActivity::class.java).apply {
                putExtra("cardType", CardCommon.CLEAN_CARD_TYPE)
                putExtra("operationTypeCode", CardOperationType.CleanCard.code)
                putExtra("operationTypeCode", CardOperationType.CleanCardMake.code)
            }
            startActivity(intent)
        }
generallibrary/src/main/java/com/dayu/general/activity/ManagerReadActivity.kt
@@ -15,6 +15,7 @@
import com.dayu.general.model.CardInfoModel
import com.dayu.general.net.ApiManager
import com.dayu.general.net.BaseResponse
import com.dayu.general.tool.BaseCommon.Companion.protocol
import com.dayu.general.tool.CardCommon
import com.dayu.general.tool.NfcReadHelper
@@ -48,18 +49,34 @@
            CardCommon.CLEAN_CARD_TYPE -> binding.titleBar.setCenterText("制作清零卡")
            else -> binding.titleBar.setCenterText("读卡")
        }
        binding.titleBar.setOnItemclickListner(ClickType_LEFT_IMAGE) { this.finish() }
        // 根据卡类型设置输入框可见性
        when (cardType) {
            CardCommon.REGION_CARD -> {
                // 区域表号卡:显示区域号输入框
                // 区域表号卡:显示区域号和项目号输入框
                binding.regionNumberLayout.visibility = View.VISIBLE
                binding.projectNumberLayout.visibility = View.VISIBLE
                // 为区域号输入框添加输入验证
                binding.regionNumberEt.addTextChangedListener(object : TextWatcher {
                    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
                    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
                    override fun beforeTextChanged(
                        s: CharSequence?,
                        start: Int,
                        count: Int,
                        after: Int
                    ) {
                    }
                    override fun onTextChanged(
                        s: CharSequence?,
                        start: Int,
                        before: Int,
                        count: Int
                    ) {
                    }
                    override fun afterTextChanged(s: Editable?) {
                        val text = s.toString()
                        if (text.length > 12) {
@@ -68,35 +85,96 @@
                        }
                    }
                })
                // 为项目号输入框添加输入验证(范围1-255)
                binding.projectNumberEt.addTextChangedListener(object : TextWatcher {
                    override fun beforeTextChanged(
                        s: CharSequence?,
                        start: Int,
                        count: Int,
                        after: Int
                    ) {
                    }
                    override fun onTextChanged(
                        s: CharSequence?,
                        start: Int,
                        before: Int,
                        count: Int
                    ) {
                    }
                    override fun afterTextChanged(s: Editable?) {
                        val text = s.toString()
                        if (text.isNotEmpty()) {
                            try {
                                val number = text.toInt()
                                if (number > 255) {
                                    binding.projectNumberEt.setText("255")
                                    binding.projectNumberEt.setSelection(3)
                                } else if (number < 1 && text.length > 1) {
                                    binding.projectNumberEt.setText("1")
                                    binding.projectNumberEt.setSelection(1)
                                }
                            } catch (e: NumberFormatException) {
                                // 如果输入的不是数字,清空
                                binding.projectNumberEt.setText("")
                            }
                        }
                    }
                })
            }
            CardCommon.CHECK_CARD,
            CardCommon.DEBUG_CARD,
            CardCommon.CLEAN_CARD_TYPE -> {
                // 检查卡、调试卡、清零卡:隐藏区域号输入框,只显示卡地址和备注
                // 检查卡、调试卡、清零卡:隐藏区域号和项目号输入框,只显示卡地址和备注
                binding.regionNumberLayout.visibility = View.GONE
                binding.projectNumberLayout.visibility = View.GONE
            }
            else -> {
                // 其他卡类型:隐藏区域号输入框
                // 其他卡类型:隐藏区域号和项目号输入框
                binding.regionNumberLayout.visibility = View.GONE
                binding.projectNumberLayout.visibility = View.GONE
            }
        }
        binding.btnNext.setOnClickListener {
            if (viewModel.cardNumber.value.isNullOrEmpty()) {
            if (viewModel.cardNumber.get().isNullOrEmpty()) {
                ToastUtil.show("请先读取卡号")
            } else if (cardType == CardCommon.REGION_CARD) {
                // 区域表号卡需要验证区域号
                val regionNumber = viewModel.regionNumber.value
                // 区域表号卡需要验证区域号和项目号
                val regionNumber = viewModel.regionNumber.get()
                val projectNumber = viewModel.projectNumber.get()
                if (regionNumber.isNullOrEmpty()) {
                    ToastUtil.show("请输入区域号")
                } else if (regionNumber.length != 12) {
                    ToastUtil.show("区域号必须为12位数字")
                } else if (projectNumber.isNullOrEmpty()) {
                    ToastUtil.show("请输入项目号")
                } else {
                    postCardData(cardType, viewModel.cardNumber.value!!, getRemark(), regionNumber)
                    try {
                        val projectNum = projectNumber.toInt()
                        if (projectNum < 1 || projectNum > 255) {
                            ToastUtil.show("项目号必须在1-255范围内")
                        } else {
                            postCardData(
                                cardType,
                                viewModel.cardNumber.get()!!,
                                getRemark(),
                                regionNumber,
                                projectNumber
                            )
                        }
                    } catch (e: NumberFormatException) {
                        ToastUtil.show("项目号必须为数字")
                    }
                }
            } else {
                // 其他卡类型(包括调试卡、检查卡、清零卡)不需要区域号
                postCardData(cardType, viewModel.cardNumber.value!!, getRemark())
                // 其他卡类型(包括调试卡、检查卡、清零卡)不需要区域号和项目号
                postCardData(cardType, viewModel.cardNumber.get()!!, getRemark())
            }
        }
    }
@@ -115,7 +193,7 @@
    /**
     * 获取备注信息
     */
    fun getRemark(): String = viewModel.remark.value ?: ""
    fun getRemark(): String = viewModel.remark.get() ?: ""
    override fun onNfcBack(intent: Intent) {
        // 处理NFC读取结果
@@ -132,81 +210,134 @@
    }
private fun bytesToHexString(bytes: ByteArray?): String? {
    if (bytes == null || bytes.isEmpty()) return null
    val sb = StringBuilder()
    for (b in bytes) {
        sb.append(String.format("%02X", b))
    }
    return sb.toString()
}
/**
 * 提交数据 - 不带区域号的版本
 */
fun postCardData(cardType: String, cardAddr: String, remark: String) {
    postCardData(cardType, cardAddr, remark, null)
}
/**
 * 提交数据 - 带区域号的版本
 */
fun postCardData(cardType: String, cardAddr: String, remark: String, regionNumber: String?) {
    val map = mutableMapOf<String, Any>()
    if (cardAddr.isNotEmpty()) {
        map["cardAddr"] = cardAddr
    private fun bytesToHexString(bytes: ByteArray?): String? {
        if (bytes == null || bytes.isEmpty()) return null
        val sb = StringBuilder()
        for (b in bytes) {
            sb.append(String.format("%02X", b))
        }
        return sb.toString()
    }
    if (cardType.isNotEmpty()) {
//            map["cardType"] = cardType
        map["cardType"] = "5"
    /**
     * 提交数据 - 不带区域号的版本
     */
    fun postCardData(cardType: String, cardAddr: String, remark: String) {
        postCardData(cardType, cardAddr, remark, null, null)
    }
    if (remark.isNotEmpty()) {
        map["remarks"] = remark
    /**
     * 提交数据 - 带区域号的版本
     */
    fun postCardData(cardType: String, cardAddr: String, remark: String, regionNumber: String?) {
        postCardData(cardType, cardAddr, remark, regionNumber, null)
    }
    // 如果是区域表号卡并且有区域号,添加到请求参数中
    if (cardType == CardCommon.REGION_CARD && !regionNumber.isNullOrEmpty()) {
        map["regionNumber"] = regionNumber
    }
    // 使用正确的类型参数
    ApiManager.getInstance().requestPostLoading(
        this,
        "/sell/card/create_manager_card",
        String::class.java,
        map,
        object : SubscriberListener<BaseResponse<String>>() {
            override fun onNext(t: BaseResponse<String>) {
                if (t.success) {
                    var intent = Intent(this@ManagerReadActivity, NfcWreatActivity::class.java).apply {
                        putExtra("cardAddr", cardAddr)
                        putExtra("orderId", t.content)
                        putExtra("cardType", cardType)
                        if (cardType == CardCommon.REGION_CARD && !regionNumber.isNullOrEmpty()) {
                            putExtra("regionNumber", regionNumber)
                        }
    /**
     * 提交数据 - 完整版本,支持区域号和项目号
     */
    fun postCardData(
        cardType: String,
        cardAddr: String,
        remark: String,
        regionNumber: String?,
        projectNumber: String?
    ) {
        val map = mutableMapOf<String, Any>()
        if (cardAddr.isNotEmpty()) {
            map["cardAddr"] = cardAddr
        }
        if (cardType.isNotEmpty()) {
            // 根据卡类型设置对应的数字编号
            val cardTypeNumber = when (cardType) {
                CardCommon.REGION_CARD -> "2"           // 设置区域表号卡
                CardCommon.ELECTRIC_PRICE_CARD -> "3"   // 取数卡
                CardCommon.CHECK_CARD -> "4"            // 检查卡
                CardCommon.DEBUG_CARD -> "5"            // 测试卡
                CardCommon.CLEAN_CARD_TYPE -> "6"       // 清零卡
                CardCommon.IP_CARD -> "7"               // IP设置卡
                CardCommon.AREA_CARD -> "8"             // 域名设置卡(区域设置卡)
                CardCommon.GPS_CARD -> "9"              // GPS卡
                CardCommon.VALVE_TIME_CARD -> "10"      // 时间配置卡(配置开关阀时间)
                else -> "5" // 默认值
            }
            map["cardType"] = cardTypeNumber
        }
        if (remark.isNotEmpty()) {
            map["remarks"] = remark
        }
        // 如果是区域表号卡并且有区域号,添加到请求参数中
        if (cardType == CardCommon.REGION_CARD && !regionNumber.isNullOrEmpty()) {
            map["regionNumber"] = regionNumber
        }
        // 如果是区域表号卡并且有项目号,添加到请求参数中
        if (cardType == CardCommon.REGION_CARD && !projectNumber.isNullOrEmpty()) {
            map["projectNumber"] = projectNumber
        }
        map["protocol"] = protocol
        map["securityCode"]="A0B1C289"
        // 使用正确的类型参数
        ApiManager.getInstance().requestPostLoading(
            this,
            "terminal/card/createManagementCard",
            String::class.java,
            map,
            object : SubscriberListener<BaseResponse<String>>() {
                override fun onNext(t: BaseResponse<String>) {
                    if (t.success) {
                        var intent =
                            Intent(this@ManagerReadActivity, NfcWreatActivity::class.java).apply {
                                putExtra("cardAddr", cardAddr)
                                putExtra("orderNumber", t.content)
                                putExtra("cardType", cardType)
                                // 根据卡类型设置操作类型代码
                                val operationTypeCode = when (cardType) {
                                    CardCommon.REGION_CARD -> 100      // 区域表号卡操作类型
                                    CardCommon.CHECK_CARD -> 101       // 检查卡操作类型
                                    CardCommon.DEBUG_CARD -> 102       // 调试卡操作类型
                                    CardCommon.CLEAN_CARD_TYPE -> 103  // 清零卡操作类型
                                    CardCommon.IP_CARD -> 104          // IP设置卡操作类型
                                    CardCommon.AREA_CARD -> 105        // 域名设置卡操作类型
                                    CardCommon.GPS_CARD -> 106         // GPS卡操作类型
                                    CardCommon.VALVE_TIME_CARD -> 107  // 时间配置卡操作类型
                                    CardCommon.ELECTRIC_PRICE_CARD -> 108  // 取数卡操作类型
                                    else -> -1
                                }
                                putExtra("operationTypeCode", operationTypeCode)
                                // 传递区域号和项目号(仅当是区域表号卡时)
                                if (cardType == CardCommon.REGION_CARD && !regionNumber.isNullOrEmpty()) {
                                    putExtra("regionNumber", regionNumber)
                                }
                                if (cardType == CardCommon.REGION_CARD && !projectNumber.isNullOrEmpty()) {
                                    putExtra("projectNumber", projectNumber)
                                }
                            }
                        this@ManagerReadActivity.finish()
                        startActivity(intent)
                    } else {
                        // 处理搜索失败的情况
                        ToastUtil.show(t.msg)
                    }
                    this@ManagerReadActivity.finish()
                    startActivity(intent)
                } else {
                    // 处理搜索失败的情况
                    ToastUtil.show(t.msg)
                }
                override fun onError(e: Throwable?) {
                    super.onError(e)
                    ToastUtil.show("接口调取失败: ${e?.message ?: "未知错误"}")
                }
            }
            override fun onError(e: Throwable?) {
                super.onError(e)
                ToastUtil.show("搜索失败: ${e?.message ?: "未知错误"}")
            }
        }
    )
        )
}
    }
}
generallibrary/src/main/java/com/dayu/general/activity/NewCard2Activity.kt
@@ -24,6 +24,7 @@
import com.dayu.general.databinding.ActivityNewCardGeBinding
import com.dayu.general.net.ApiManager
import com.dayu.general.net.BaseResponse
import com.dayu.general.tool.BaseCommon.Companion.protocol
import com.dayu.general.tool.CardCommon.Companion.USER_CARD_TYPE_1
import com.dayu.general.tool.CardOperationType
import com.dayu.general.tool.NfcReadHelper
@@ -170,7 +171,7 @@
    private fun getPaymentMethods() {
        ApiManager.getInstance().requestGetLoading(
            this,
            "sell/paymentmethod/get",
            "terminal/paymentmethod/get",
            PaymentMethodResponse::class.java,
            null,
            object : SubscriberListener<BaseResponse<PaymentMethodResponse>>() {
@@ -323,7 +324,7 @@
        params["amount"] = rechargeAmount  // 充值金额(元)
        params["paymentId"] = paymentId // 支付方式ID
        params["remarks"] = remark // 备注
        params["protocol"] = "p206V1_0_1" // 协议
        params["protocol"] = protocol // 协议
        params["operator"] = BaseApplication.userId // 操作人ID
        // 执行卡片激活API请求
generallibrary/src/main/java/com/dayu/general/activity/NfcWreatActivity.kt
@@ -7,7 +7,11 @@
import com.dayu.baselibrary.tools.nfc.NFCCallBack
import com.dayu.baselibrary.utils.MornyUtil
import com.dayu.baselibrary.utils.ToastUtil
import com.dayu.general.BaseApplication
import com.dayu.general.bean.card.AreaCard
import com.dayu.general.bean.card.CheckCard
import com.dayu.general.bean.card.ClearCard
import com.dayu.general.bean.card.DebugCard
import com.dayu.general.bean.card.UserCard
import com.dayu.general.dao.BaseDaoSingleton
import com.dayu.general.databinding.ActivityNfcWriteGeBinding
@@ -20,39 +24,79 @@
import kotlinx.coroutines.launch
/**
 * NFC写卡操作界面
 *
 * 功能说明:
 * 1. 支持多种卡类型的写卡操作(开卡、充值、销卡、返还、补扣、补卡、检查卡、区域表号卡、调试卡等)
 * 2. 通过NFC技术将数据写入IC卡
 * 3. 写卡成功后向服务器上报操作结果
 * 4. 更新本地数据库中的写卡状态
 *
 * @author: zuo
 * @date: 2021/3/30
 * @description:写卡界面
 * @description: 写卡界面
 */
class NfcWreatActivity : BaseNfcActivity() {
    /** 数据绑定对象 */
    var binding: ActivityNfcWriteGeBinding? = null
    /** 卡类型标识 */
    var cardType = ""
    /** 卡地址/卡号 */
    var cardAddr = ""
    /** 卡工本费(分为单位) */
    var cardFee = 0
    // 充值相关金额
    // ==================== 充值相关金额 ====================
    /** 充值金额(元) */
    private var rechargeAmount = 0.0
    /** 赠送金额(元) */
    private var bonusAmount = 0.0
    // 销卡相关信息
    // ==================== 销卡相关信息 ====================
    /** 退款金额(元) */
    private var refundAmount = 0.0
    /** 卡内余额(元) */
    private var cardBalance = 0.0
    // 返还相关信息
    // ==================== 返还相关信息 ====================
    /** 返还金额(元) */
    private var returnAmount = 0.0
    // 补扣相关信息
    // ==================== 补扣相关信息 ====================
    /** 补扣金额(元) */
    private var deductAmount = 0.0
    // 补卡相关信息
    // ==================== 补卡相关信息 ====================
    /** 补卡工本费(元) */
    private var cardCost = 0.0
    /** 补卡金额(元) */
    private var reissueAmount = 0.0
    //订单编号
    /** 订单编号 */
    var orderNumber = ""
    /** 用户卡对象,包含要写入卡内的所有数据 */
    private lateinit var userCard: UserCard
    private var operationTypeCode = -1;
    /** 操作类型代码 */
    private var operationTypeCode = -1
    /** 操作类型枚举 */
    private var operationType: CardOperationType? = null
    // ==================== 管理卡制作相关参数 ====================
    /** 区域号(12位数字) */
    private var regionNumber: String = ""
    /** 项目号(1-255) */
    private var projectNumber: String = ""
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
@@ -62,9 +106,11 @@
    }
    /**
     * 获取数据
     * 获取传入的初始化数据
     * 从Intent中提取写卡所需的各种参数
     */
    private fun getInitData() {
        // 获取基本卡信息
        cardType = intent?.getStringExtra("cardType") ?: ""
        cardAddr = intent?.getStringExtra("cardAddr") ?: ""
        operationTypeCode = intent?.getIntExtra("operationTypeCode", -1) ?: -1
@@ -89,185 +135,282 @@
        cardCost = intent?.getDoubleExtra("cardCost", 0.0) ?: 0.0
        reissueAmount = intent?.getDoubleExtra("reissueAmount", 0.0) ?: 0.0
        // 获取卡工本费
        if (intent?.hasExtra("cardFee") == true) {
            cardFee = intent?.getIntExtra("cardFee", 0) ?: 0
        }
        // 获取用户卡对象(兼容不同Android版本)
        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)!!
            }
        }
        // 获取区域号和项目号
        regionNumber = intent?.getStringExtra("regionNumber") ?: ""
        projectNumber = intent?.getStringExtra("projectNumber") ?: ""
        // 验证orderNumber不能为空
        if (orderNumber.isEmpty()) {
            ToastUtil.show("订单号不能为空,请重新操作")
            finish()
            return
        }
        // 根据操作类型设置界面显示内容
        if (operationTypeCode != -1) {
            when (operationType) {
                CardOperationType.CleanCard -> {
                    val textData = StringBuilder()
                    // 判断是否来自销卡操作(有退款金额或卡内余额信息)
                    if (refundAmount > 0 || cardBalance > 0) {
                        textData.append("销卡清零操作\n")
                        textData.append("卡地址:$cardAddr\n")
                        if (cardBalance > 0) {
                            textData.append("卡内余额:${String.format("%.2f", cardBalance)}元\n")
                        }
                        if (refundAmount > 0) {
                            textData.append("退款金额:${String.format("%.2f", refundAmount)}元")
                        } else {
                            textData.append("无退款金额")
                        }
                    } else {
                        textData.append("清零卡写卡")
                    }
                    binding?.cardData?.text = textData.toString()
                }
                CardOperationType.OpenCard -> {
                    var textData = StringBuilder()
                    textData.append("用户开卡\n")
                    if (cardFee != 0) {
                        textData.append("工本费:" + cardFee + "元\n")
                    }
                    if (userCard.balance != 0) {
                        textData.append("充值金额:" + MornyUtil.changeF2Y(userCard.balance) + "元")
                    }
                    binding?.cardData?.text = textData.toString()
                }
                CardOperationType.Recharge -> {
                    var textData = StringBuilder()
                    textData.append("用户充值\n")
                    // 显示充值金额
                    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(
                            "充值后余额:" + MornyUtil.changeF2Y(userCard.balance)+ "元"
                        )
                    }
                    binding?.cardData?.text = textData.toString()
                }
                CardOperationType.CancelCard -> {
                    var textData = StringBuilder()
                    textData.append("销卡\n")
                    textData.append("卡内余额:" + MornyUtil.changeF2Y(userCard.balance) + "元\n")
                    textData.append("退款金额:" + refundAmount + "元")
                    binding?.cardData?.text = textData.toString()
                }
                CardOperationType.ReturnCard -> {
                    var textData = StringBuilder()
                    textData.append("返还\n")
                    textData.append("卡内余额:" + MornyUtil.changeF2Y(userCard.balance) + "元\n")
                    textData.append("返还金额:" + returnAmount + "元")
                    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.ReplaceCard -> {
                    var textData = StringBuilder()
                    textData.append("补卡\n")
                    textData.append("卡内余额:" + MornyUtil.changeF2Y(userCard.balance) + "元\n")
                    if (cardCost > 0) {
                        textData.append("工本费:" + String.format("%.2f", cardCost) + "元\n")
                    }
                    if (reissueAmount > 0) {
                        textData.append("补卡金额:" + String.format("%.2f", reissueAmount) + "元")
                    }
                    binding?.cardData?.text = textData.toString()
                }
                CardOperationType.CheckCard -> {
                    var textData = StringBuilder()
                    textData.append("检查卡制作")
                    binding?.cardData?.text = textData.toString()
                }
                CardOperationType.RegionCard -> {
                    var textData = StringBuilder()
                    textData.append("区域表号卡制作")
                    binding?.cardData?.text = textData.toString()
                }
                CardOperationType.DebugCard -> {
                    var textData = StringBuilder()
                    textData.append("调试卡制作")
                    binding?.cardData?.text = textData.toString()
                }
                null -> TODO()
            }
            setupUIForOperationType()
        }
    }
    /**
     * 根据操作类型设置界面显示内容
     * 为不同的卡操作类型显示相应的提示信息
     */
    private fun setupUIForOperationType() {
        when (operationType) {
            CardOperationType.CleanCardMake -> {
                val textData = StringBuilder()
                // 判断是否来自销卡操作(有退款金额或卡内余额信息)
                if (refundAmount > 0 || cardBalance > 0) {
                    textData.append("销卡清零操作\n")
                    textData.append("卡地址:$cardAddr\n")
                    if (cardBalance > 0) {
                        textData.append("卡内余额:${String.format("%.2f", cardBalance)}元\n")
                    }
                    if (refundAmount > 0) {
                        textData.append("退款金额:${String.format("%.2f", refundAmount)}元")
                    } else {
                        textData.append("无退款金额")
                    }
                } else {
                    textData.append("清零卡写卡")
                }
                binding?.cardData?.text = textData.toString()
            }
            CardOperationType.OpenCard -> {
                // 开卡操作显示信息
                var textData = StringBuilder()
                textData.append("用户开卡\n")
                if (cardFee != 0) {
                    textData.append("工本费:" + cardFee + "元\n")
                }
                if (userCard.balance != 0) {
                    textData.append("充值金额:" + MornyUtil.changeF2Y(userCard.balance) + "元")
                }
                binding?.cardData?.text = textData.toString()
            }
            CardOperationType.Recharge -> {
                // 充值操作显示信息
                var textData = StringBuilder()
                textData.append("用户充值\n")
                // 显示充值金额
                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(
                        "充值后余额:" + MornyUtil.changeF2Y(userCard.balance) + "元"
                    )
                }
                binding?.cardData?.text = textData.toString()
            }
            CardOperationType.CancelCard -> {
                // 销卡操作显示信息
                var textData = StringBuilder()
                textData.append("销卡\n")
                textData.append("卡内余额:" + MornyUtil.changeF2Y(userCard.balance) + "元\n")
                textData.append("退款金额:" + refundAmount + "元")
                binding?.cardData?.text = textData.toString()
            }
            CardOperationType.SUPPLEMENT -> {
                // 返还操作显示信息
                var textData = StringBuilder()
                textData.append("返还\n")
                textData.append("卡内余额:" + MornyUtil.changeF2Y(userCard.balance) + "元\n")
                textData.append("返还金额:" + returnAmount + "元")
                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.ReplaceCard -> {
                // 补卡操作显示信息
                var textData = StringBuilder()
                textData.append("补卡\n")
                textData.append("卡内余额:" + MornyUtil.changeF2Y(userCard.balance) + "元\n")
                if (cardCost > 0) {
                    textData.append("工本费:" + String.format("%.2f", cardCost) + "元\n")
                }
                if (reissueAmount > 0) {
                    textData.append("补卡金额:" + String.format("%.2f", reissueAmount) + "元")
                }
                binding?.cardData?.text = textData.toString()
            }
            CardOperationType.CheckCardMake -> {
                // 检查卡制作显示信息
                var textData = StringBuilder()
                textData.append("检查卡制作")
                binding?.cardData?.text = textData.toString()
            }
            CardOperationType.RegionCardMake -> {
                // 区域表号卡制作显示信息
                var textData = StringBuilder()
                textData.append("区域表号卡制作\n")
                if (regionNumber.isNotEmpty()) {
                    textData.append("区域号:$regionNumber\n")
                }
                if (projectNumber.isNotEmpty()) {
                    textData.append("项目号:$projectNumber")
                }
                binding?.cardData?.text = textData.toString()
            }
            CardOperationType.DebugCardMake -> {
                // 调试卡制作显示信息
                var textData = StringBuilder()
                textData.append("调试卡制作")
                binding?.cardData?.text = textData.toString()
            }
            CardOperationType.CheckCardMake -> {
                var textData = StringBuilder()
                textData.append("制作检查卡")
                binding?.cardData?.text = textData.toString()
            }
            CardOperationType.DebugCardMake -> {
                var textData = StringBuilder()
                textData.append("制作调试卡")
                binding?.cardData?.text = textData.toString()
            }
            CardOperationType.CleanCardMake -> {
                var textData = StringBuilder()
                textData.append("制作清零卡")
                binding?.cardData?.text = textData.toString()
            }
            CardOperationType.IpCardMake -> {
                var textData = StringBuilder()
                textData.append("制作IP设置卡")
                binding?.cardData?.text = textData.toString()
            }
            CardOperationType.AreaCardMake -> {
                var textData = StringBuilder()
                textData.append("制作域名设置卡")
                binding?.cardData?.text = textData.toString()
            }
            CardOperationType.GpsCardMake -> {
                var textData = StringBuilder()
                textData.append("制作GPS卡")
                binding?.cardData?.text = textData.toString()
            }
            CardOperationType.ValveTimeCardMake -> {
                var textData = StringBuilder()
                textData.append("制作时间配置卡")
                binding?.cardData?.text = textData.toString()
            }
            CardOperationType.ElectricPriceCardMake -> {
                var textData = StringBuilder()
                textData.append("制作取数卡")
                binding?.cardData?.text = textData.toString()
            }
            null -> TODO()
            CardOperationType.MANAGEMENT_CARD_WRITE -> TODO()
        }
    }
    /**
     * NFC刷卡回调处理
     * 当用户将卡片贴近设备时触发此方法
     *
     * @param intent NFC意图,包含卡片信息
     */
    override fun onNfcBack(intent: Intent) {
        // 读取卡号
        val nfcReadHelper = NfcReadHelper.getInstance(intent, this)
        // 使用正常的getCardNumber()方法,它会自动关闭连接
        val cardNumber = nfcReadHelper.getCardNumber()
        // 验证卡号是否与预期一致
        if (cardNumber.isNotEmpty() && cardNumber == cardAddr) {
            val nfcWreatHelper = NfcWreatHelper.getInstance(intent, this)
            // 根据操作类型执行相应的写卡操作
            when (operationType) {
                CardOperationType.CleanCard -> {
                CardOperationType.CleanCardMake -> {
                    // 清零卡操作(暂未实现具体逻辑)
                }
                CardOperationType.OpenCard -> {
                    // 开卡操作:将用户信息写入卡片
                    nfcWreatHelper.writeUserDataAsync(userCard, object : NFCCallBack {
                        override fun isSusses(flag: Boolean, msg: String?) {
                            // 确保Toast在主线程中调用
                            runOnUiThread {
                                if (flag) {
                                    postCardData(cardAddr)
                                    // 写卡成功,更新写卡状态
                                    updateCardWrittenStatus(cardAddr)
                                } else {
                                    ToastUtil.show("写卡失败: ${msg ?: "未知错误"}")
                                }
                            }
                        }
                    })
                }
                CardOperationType.Recharge -> {
                    // 充值操作:更新卡内余额
                    nfcWreatHelper.writeUserDataAsync(userCard, object : NFCCallBack {
                        override fun isSusses(flag: Boolean, msg: String?) {
                            // 确保Toast在主线程中调用
                            runOnUiThread {
                                if (flag) {
                                    postCardData(cardAddr)
                                    // 充值写卡成功,更新写卡状态
                                    updateCardWrittenStatus(cardAddr)
                                } else {
                                    ToastUtil.show("充值写卡失败: ${msg ?: "未知错误"}")
                                }
@@ -277,14 +420,16 @@
                }
                CardOperationType.CancelCard -> {
                    // 销卡操作:将卡类型设置为无效
                    var userCard = UserCard()
                    userCard.cardType = "00";
                    userCard.cardType = "00" // 设置为无效卡类型
                    nfcWreatHelper.writeUserDataAsync(userCard, object : NFCCallBack {
                        override fun isSusses(flag: Boolean, msg: String?) {
                            // 确保Toast在主线程中调用
                            runOnUiThread {
                                if (flag) {
                                    postCardData(cardAddr)
                                    // 销卡写卡成功,更新写卡状态
                                    updateCardWrittenStatus(cardAddr)
                                } else {
                                    ToastUtil.show("销卡写卡失败: ${msg ?: "未知错误"}")
                                }
@@ -293,13 +438,15 @@
                    })
                }
                CardOperationType.ReturnCard -> {
                CardOperationType.SUPPLEMENT -> {
                    // 返还操作:更新卡内余额
                    nfcWreatHelper.writeUserDataAsync(userCard, object : NFCCallBack {
                        override fun isSusses(flag: Boolean, msg: String?) {
                            // 确保Toast在主线程中调用
                            runOnUiThread {
                                if (flag) {
                                    postCardData(cardAddr)
                                    // 返还写卡成功,更新写卡状态
                                    updateCardWrittenStatus(cardAddr)
                                } else {
                                    ToastUtil.show("返还写卡失败: ${msg ?: "未知错误"}")
                                }
@@ -309,12 +456,14 @@
                }
                CardOperationType.DeductCard -> {
                    // 补扣操作:更新卡内余额
                    nfcWreatHelper.writeUserDataAsync(userCard, object : NFCCallBack {
                        override fun isSusses(flag: Boolean, msg: String?) {
                            // 确保Toast在主线程中调用
                            runOnUiThread {
                                if (flag) {
                                    postCardData(cardAddr)
                                    // 补扣写卡成功,更新写卡状态
                                    updateCardWrittenStatus(cardAddr)
                                } else {
                                    ToastUtil.show("补扣写卡失败: ${msg ?: "未知错误"}")
                                }
@@ -324,12 +473,14 @@
                }
                CardOperationType.ReplaceCard -> {
                    // 补卡操作:将原卡数据写入新卡
                    nfcWreatHelper.writeUserDataAsync(userCard, object : NFCCallBack {
                        override fun isSusses(flag: Boolean, msg: String?) {
                            // 确保Toast在主线程中调用
                            runOnUiThread {
                                if (flag) {
                                    postCardData(cardAddr)
                                    // 补卡写卡成功,更新写卡状态
                                    updateCardWrittenStatus(cardAddr)
                                } else {
                                    ToastUtil.show("补卡写卡失败: ${msg ?: "未知错误"}")
                                }
@@ -338,33 +489,115 @@
                    })
                }
                CardOperationType.CheckCard -> {
                    // 检查卡写卡逻辑
                    ToastUtil.show("检查卡写卡成功!")
                    postCardData(cardAddr)
                CardOperationType.CheckCardMake -> {
                    // 检查卡写卡逻辑(功能卡,无需写入用户数据)
                    var checkCard = CheckCard()
                    nfcWreatHelper.writeDataAsync(
                        checkCard.getZeroBytes(),
                        7,
                        0,
                        object : NFCCallBack {
                            override fun isSusses(flag: Boolean, msg: String?) {
                                // 确保Toast在主线程中调用
                                                            runOnUiThread {
                                if (flag) {
                                    // 检查卡写卡成功,更新写卡状态
                                    updateCardWrittenStatus(cardAddr)
                                } else {
                                    ToastUtil.show("检查卡写卡失败: ${msg ?: "未知错误"}")
                                }
                            }
                            }
                        })
                }
                CardOperationType.RegionCard -> {
                    // 区域表号卡写卡逻辑
                    ToastUtil.show("区域表号卡写卡成功!")
                    postCardData(cardAddr)
                CardOperationType.RegionCardMake -> {
                    // 区域表号卡写卡逻辑(功能卡,无需写入用户数据)
                    var areaCard = AreaCard()
                    areaCard.areaNumber = regionNumber.toInt()
                    areaCard.projectCode = projectNumber.toInt()
                    nfcWreatHelper.writeDataAsync(
                        areaCard.getZeroBytes(),
                        7,
                        0,
                        object : NFCCallBack {
                            override fun isSusses(flag: Boolean, msg: String?) {
                                // 确保Toast在主线程中调用
                                                            runOnUiThread {
                                if (flag) {
                                    // 区域表号卡写卡成功,更新写卡状态
                                    updateCardWrittenStatus(cardAddr)
                                } else {
                                    ToastUtil.show("区域表号卡写卡失败: ${msg ?: "未知错误"}")
                                }
                            }
                            }
                        })
                }
                CardOperationType.DebugCard -> {
                    // 调试卡写卡逻辑
                    ToastUtil.show("调试卡写卡成功!")
                    postCardData(cardAddr)
                CardOperationType.DebugCardMake -> {
                    // 调试卡写卡逻辑(功能卡,无需写入用户数据)
                    var debugCard = DebugCard()
                    nfcWreatHelper.writeDataAsync(
                        debugCard.getZeroBytes(),
                        7,
                        0,
                        object : NFCCallBack {
                            override fun isSusses(flag: Boolean, msg: String?) {
                                // 确保Toast在主线程中调用
                                                            runOnUiThread {
                                if (flag) {
                                    // 调试卡写卡成功,更新写卡状态
                                    updateCardWrittenStatus(cardAddr)
                                } else {
                                    ToastUtil.show("调试卡写卡失败: ${msg ?: "未知错误"}")
                                }
                            }
                            }
                        })
                }
                // 新的管理卡制作操作类型处理
                CardOperationType.RegionCardMake,
                CardOperationType.CheckCardMake,
                CardOperationType.CleanCardMake,
                CardOperationType.IpCardMake,
                CardOperationType.AreaCardMake,
                CardOperationType.GpsCardMake,
                CardOperationType.ValveTimeCardMake,
                CardOperationType.ElectricPriceCardMake -> {
                    // 管理卡写卡逻辑(功能卡,无需写入用户数据)
                    val operationName = when (operationType) {
                        CardOperationType.RegionCardMake -> "区域表号卡"
                        CardOperationType.CheckCardMake -> "检查卡"
                        CardOperationType.DebugCardMake -> "调试卡"
                        CardOperationType.CleanCardMake -> "清零卡"
                        CardOperationType.IpCardMake -> "IP设置卡"
                        CardOperationType.AreaCardMake -> "域名设置卡"
                        CardOperationType.GpsCardMake -> "GPS卡"
                        CardOperationType.ValveTimeCardMake -> "时间配置卡"
                        CardOperationType.ElectricPriceCardMake -> "取数卡"
                        else -> "管理卡"
                    }
                    ToastUtil.show("${operationName}写卡成功!")
                    updateCardWrittenStatus(cardAddr)
                }
                null -> TODO()
                CardOperationType.MANAGEMENT_CARD_WRITE -> TODO()
            }
        } else {
            // 卡号不匹配,提示用户
            ToastUtil.show("卡片错误,当前刷的卡与刚刚的卡不一致")
        }
    }
    /**
     * 更新CardRegistrationBean中的isCardWritten状态为true
     * 更新本地数据库中的写卡状态
     * 将CardRegistrationBean中的isCardWritten状态设置为true
     * 然后跳转到写卡成功界面,并通知MainActivity调用postCardData
     *
     * @param cardNumber 卡号
     */
    private fun updateCardWrittenStatus(cardNumber: String) {
        lifecycleScope.launch {
@@ -372,27 +605,36 @@
                val cardRegistrationDao = BaseDaoSingleton.getInstance(this@NfcWreatActivity)
                    .cardRegistrationDao()
                // 根据卡号查找CardRegistrationBean记录
                val cardRegistration = cardRegistrationDao.getByCardNumber(cardNumber)
                // 根据订单号查找CardRegistrationBean记录
                val cardRegistration = cardRegistrationDao.getByOrderId(orderNumber)
                if (cardRegistration != null) {
                    // 创建更新后的CardRegistrationBean对象,将isCardWritten设置为true
                    val updatedCardRegistration = cardRegistration.copy(isCardWritten = true)
                    // 更新数据库记录
                    cardRegistrationDao.update(updatedCardRegistration)
                    // 在主线程中关闭Activity
                    // 在主线程中关闭Activity并跳转到成功页面
                    runOnUiThread {
                        setResult(RESULT_OK)
                        finish()
                        // 跳转到写卡成功界面
                        Intent(this@NfcWreatActivity, CardWriteSuccessActivity::class.java).apply {
                            putExtra("cardNumber", cardNumber)
                            putExtra("userCard", userCard)
                            if (::userCard.isInitialized) {
                                putExtra("userCard", userCard)
                            }
                            putExtra("operationTypeCode", operationTypeCode)
                            startActivity(this)
                        }
                        // 通知MainActivity调用postCardData
                        notifyMainActivityToPostCardData(cardNumber)
                    }
                }
            } catch (e: Exception) {
                // 记录异常信息
                CrashReport.postCatchedException(e)
                e.printStackTrace()
                runOnUiThread {
@@ -402,40 +644,28 @@
        }
    }
    fun postCardData(cardAddr: String) {
        val map = mutableMapOf<String, Any>()
        if (cardAddr.isNotEmpty()) {
            map["cardAddr"] = cardAddr
        }
        map["operateType"] = operationTypeCode
        if (orderNumber.isNotEmpty()) {
            map["orderNumber"] = orderNumber
        }
        // 使用正确的类型参数
        ApiManager.getInstance().requestPostLoading(
            this,
            "terminal/card/termCallBack",
            String::class.java,
            map,
            object : SubscriberListener<BaseResponse<String>>() {
                override fun onNext(t: BaseResponse<String>) {
                    if (t.success) {
                        updateCardWrittenStatus(cardAddr)
                    } else {
                        // 处理搜索失败的情况
                        ToastUtil.show(t.msg)
                    }
                }
                override fun onError(e: Throwable?) {
                    super.onError(e)
                    ToastUtil.show("上报失败: ${e?.message ?: "未知错误"}")
                }
    /**
     * 通知MainActivity调用postCardData上报写卡结果
     *
     * @param cardNumber 卡号
     */
    private fun notifyMainActivityToPostCardData(cardNumber: String) {
        try {
            val mainActivity = BaseApplication.getMainActivity()
            if (mainActivity != null) {
                // 调用MainActivity的postCardData方法
                mainActivity.postCardData(
                    cardAddr = cardNumber,
                    operationTypeCode = operationTypeCode,
                    orderNumber = orderNumber,
                    regionNumber = regionNumber,
                    projectNumber = projectNumber
                )
            }
        )
        } catch (e: Exception) {
            e.printStackTrace()
            CrashReport.postCatchedException(e)
        }
    }
generallibrary/src/main/java/com/dayu/general/activity/RechargeDetailActivity.kt
@@ -154,7 +154,7 @@
    private fun getPaymentMethods() {
        ApiManager.getInstance().requestGetLoading(
            this,
            "sell/paymentmethod/get",
            "terminal/paymentmethod/get",
            PaymentMethodResponse::class.java,
            null,
            object : SubscriberListener<BaseResponse<PaymentMethodResponse>>() {
@@ -322,6 +322,25 @@
            0.0
        }
        // 获取当前余额(转换为元)
        val currentBalance = userCard?.let {
            // 将分转换为元
            it.balance / 100.0
        } ?: run {
            // 如果用户卡为空,则使用服务器返回的余额
            cardInfo?.balance ?: 0.0
        }
        // 计算充值后的总余额
        val totalAmountAfterRecharge = currentBalance + rechargeAmount + bonusAmount
        // 检查是否超过最大余额限制9999.99元
        if (totalAmountAfterRecharge > 9999.99) {
            val maxRechargeAmount = 9999.99 - currentBalance
            ToastUtil.show("充值失败:充值后余额不能超过9999.99元\n当前余额:${String.format("%.2f", currentBalance)}元\n最多可充值:${String.format("%.2f", maxRechargeAmount)}元")
            return
        }
        // 调用充值接口
        callRechargeApi(rechargeAmount, bonusAmount)
    }
generallibrary/src/main/java/com/dayu/general/bean/net/NewCardInfo.kt
@@ -3,9 +3,7 @@
// 支付方式数据类
data class PaymentMethod(
    val id: Long,
    val name: String,
    val remarks: String,
    val deleted: Int
    val name: String
)
// 支付方式接口返回数据类
generallibrary/src/main/java/com/dayu/general/model/CardInfoModel.kt
@@ -1,28 +1,34 @@
package com.dayu.general.model
import androidx.lifecycle.MutableLiveData
import androidx.databinding.ObservableField
import androidx.lifecycle.ViewModel
class CardInfoModel : ViewModel() {
    val cardNumber = MutableLiveData<String>()
    val remark = MutableLiveData<String>()
    val regionNumber = MutableLiveData<String>()
    val cardNumber = ObservableField<String>()
    val remark = ObservableField<String>()
    val regionNumber = ObservableField<String>()
    val projectNumber = ObservableField<String>()
    
    init {
        cardNumber.value = ""
        remark.value = ""
        regionNumber.value = ""
        cardNumber.set("")
        remark.set("")
        regionNumber.set("")
        projectNumber.set("")
    }
    
    fun setCardNumber(number: String) {
        cardNumber.value = number
        cardNumber.set(number)
    }
    
    fun setRemark(remarkText: String) {
        remark.value = remarkText
        remark.set(remarkText)
    }
    
    fun setRegionNumber(regionNum: String) {
        regionNumber.value = regionNum
        regionNumber.set(regionNum)
    }
    fun setProjectNumber(projectNum: String) {
        projectNumber.set(projectNum)
    }
generallibrary/src/main/java/com/dayu/general/tool/BaseCommon.kt
@@ -10,7 +10,7 @@
      val YuanMo: Int = 1
      val QiHe: Int = 2
      val protocol:String="p206V1_0_1"
  }
generallibrary/src/main/java/com/dayu/general/tool/CardOperationType.kt
@@ -10,11 +10,19 @@
    object CancelCard : CardOperationType(3, "销卡")
    object ReplaceCard : CardOperationType(4, "补卡")
    object DeductCard : CardOperationType(5, "补扣")
    object CleanCard : CardOperationType(6, "清零卡")
    object CheckCard : CardOperationType(7, "检查卡")
    object ReturnCard : CardOperationType(8, "返还")
    object RegionCard : CardOperationType(9, "区域表号卡")
    object DebugCard : CardOperationType(10, "调试卡")
    object SUPPLEMENT : CardOperationType(6, "返还")
    object MANAGEMENT_CARD_WRITE : CardOperationType(7, "管理类型卡写卡")
    // 管理卡制作操作类型(使用100+的代码)
    object RegionCardMake : CardOperationType(100, "制作区域表号卡")
    object CheckCardMake : CardOperationType(101, "制作检查卡")
    object DebugCardMake : CardOperationType(102, "制作调试卡")
    object CleanCardMake : CardOperationType(103, "制作清零卡")
    object IpCardMake : CardOperationType(104, "制作IP设置卡")
    object AreaCardMake : CardOperationType(105, "制作域名设置卡")
    object GpsCardMake : CardOperationType(106, "制作GPS卡")
    object ValveTimeCardMake : CardOperationType(107, "制作时间配置卡")
    object ElectricPriceCardMake : CardOperationType(108, "制作取数卡")
    companion object {
        fun fromCode(code: Int): CardOperationType? {
@@ -24,11 +32,18 @@
                3 -> CancelCard
                4 -> ReplaceCard
                5 -> DeductCard
                6 -> CleanCard
                7 -> CheckCard
                8 -> ReturnCard
                9 -> RegionCard
                10 -> DebugCard
                6 -> SUPPLEMENT
                7 -> MANAGEMENT_CARD_WRITE
                // 管理卡制作操作类型
                100 -> RegionCardMake
                101 -> CheckCardMake
                102 -> DebugCardMake
                103 -> CleanCardMake
                104 -> IpCardMake
                105 -> AreaCardMake
                106 -> GpsCardMake
                107 -> ValveTimeCardMake
                108 -> ElectricPriceCardMake
                else -> null
            }
        }
generallibrary/src/main/java/com/dayu/general/tool/NfcWreatHelper.kt
@@ -78,24 +78,20 @@
     * @param b   书写的块(从0开始数)
     * @param callback 操作结果回调
     */
    fun writeDataAsync(str: ByteArray?, a: Int, b: Int, callback: (Boolean) -> Unit): Disposable {
    fun writeDataAsync(str: ByteArray?, a: Int, b: Int, callback: NFCCallBack): Disposable {
        showLoading()
        val disposable = Observable.fromCallable {
            writeData(str, a, b, object : NFCCallBack {
                override fun isSusses(flag: Boolean, msg: String?) {
                    // 这个回调在IO线程中,不处理UI相关操作
                }
            })
            writeData(str, a, b, callback)
        }
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe({ result ->
            hideLoading()
            callback(result)
            callback.isSusses(result, if (result) "写卡成功" else "写卡失败")
        }, { error ->
            hideLoading()
            error.printStackTrace()
            callback(false)
            callback.isSusses(false, "写卡异常: ${error.message}")
        })
        
        compositeDisposable.add(disposable)
@@ -112,7 +108,7 @@
     */
    fun writeData(str: ByteArray?, a: Int, b: Int, callBack: NFCCallBack): Boolean {
        try {
            return adapter.writeData(str, a, b, false, callBack)
            return adapter.writeData(str, a, b, true, callBack)
        } catch (e: Exception) {
            e.printStackTrace()
        }
generallibrary/src/main/res/layout/activity_card_replace.xml
@@ -107,7 +107,7 @@
        android:layout_above="@+id/bottom_button_container"
        android:layout_below="@+id/titleBar"
        android:orientation="vertical"
        android:visibility="gone">
        android:visibility="visible">
        <ScrollView
            android:layout_width="match_parent"
@@ -125,40 +125,40 @@
                <androidx.cardview.widget.CardView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:cardCornerRadius="6dp"
                    app:cardElevation="1dp">
                    app:cardCornerRadius="8dp"
                    app:cardElevation="3dp">
                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="vertical"
                        android:padding="14dp">
                        android:padding="20dp">
                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="10dp"
                            android:layout_marginBottom="16dp"
                            android:text="卡片信息"
                            android:textColor="@color/base_blue_bg"
                            android:textSize="17sp"
                            android:textSize="20sp"
                            android:textStyle="bold" />
                        <!-- 新卡卡地址 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="1dp"
                            android:layout_marginBottom="2dp"
                            android:background="#F8F9FA"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="8dp">
                            android:padding="12dp">
                            <TextView
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:text="新卡卡地址:"
                                android:textColor="#333333"
                                android:textSize="15sp"
                                android:textSize="17sp"
                                android:textStyle="bold" />
                            <TextView
@@ -167,26 +167,29 @@
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:background="#F5F5F5"
                                android:padding="8dp"
                                android:padding="12dp"
                                android:text="--"
                                android:textColor="#333333"
                                android:textSize="15sp"
                                android:textSize="17sp"
                                android:textIsSelectable="true" />
                        </LinearLayout>
                        <View
                            android:layout_width="match_parent"
                            android:layout_height="1dp"
                            android:layout_marginTop="2dp"
                            android:layout_height="2dp"
                            android:layout_marginTop="4dp"
                            android:layout_marginBottom="4dp"
                            android:background="@color/base_blue_bg" />
                        <!-- 持卡人和卡片状态 - 同一行显示 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginTop="2dp"
                            android:layout_marginBottom="1dp"
                            android:layout_marginTop="4dp"
                            android:layout_marginBottom="2dp"
                            android:background="#FFFFFF"
                            android:orientation="horizontal"
                            android:padding="10dp">
                            android:padding="12dp">
                            <!-- 持卡人 -->
                            <LinearLayout
@@ -200,17 +203,17 @@
                                    android:layout_height="wrap_content"
                                    android:text="持卡人"
                                    android:textColor="#333333"
                                    android:textSize="13sp"
                                    android:textSize="15sp"
                                    android:textStyle="bold" />
                                <TextView
                                    android:id="@+id/tv_user_name"
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:layout_marginTop="3dp"
                                    android:layout_marginTop="6dp"
                                    android:text="--"
                                    android:textColor="#666666"
                                    android:textSize="15sp" />
                                    android:textSize="17sp" />
                            </LinearLayout>
                            <!-- 卡片状态 -->
@@ -225,17 +228,18 @@
                                    android:layout_height="wrap_content"
                                    android:text="卡片状态"
                                    android:textColor="#333333"
                                    android:textSize="13sp"
                                    android:textSize="15sp"
                                    android:textStyle="bold" />
                                <TextView
                                    android:id="@+id/tv_card_status"
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:layout_marginTop="3dp"
                                    android:layout_marginTop="6dp"
                                    android:text="正常"
                                    android:textColor="#4CAF50"
                                    android:textSize="15sp" />
                                    android:textSize="17sp"
                                    android:textStyle="bold" />
                            </LinearLayout>
                        </LinearLayout>
@@ -243,18 +247,18 @@
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="1dp"
                            android:layout_marginBottom="2dp"
                            android:background="#F8F9FA"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="8dp">
                            android:padding="12dp">
                            <TextView
                                android:layout_width="80dp"
                                android:layout_width="90dp"
                                android:layout_height="wrap_content"
                                android:text="卡号:"
                                android:textColor="#333333"
                                android:textSize="14sp"
                                android:textSize="16sp"
                                android:textStyle="bold" />
                            <TextView
@@ -264,25 +268,25 @@
                                android:layout_weight="1"
                                android:text="--"
                                android:textColor="#666666"
                                android:textSize="14sp" />
                                android:textSize="16sp" />
                        </LinearLayout>
                        <!-- 客户编号 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="1dp"
                            android:layout_marginBottom="2dp"
                            android:background="#FFFFFF"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="8dp">
                            android:padding="12dp">
                            <TextView
                                android:layout_width="80dp"
                                android:layout_width="90dp"
                                android:layout_height="wrap_content"
                                android:text="客户编号:"
                                android:textColor="#333333"
                                android:textSize="14sp"
                                android:textSize="16sp"
                                android:textStyle="bold" />
                            <TextView
@@ -292,24 +296,25 @@
                                android:layout_weight="1"
                                android:text="--"
                                android:textColor="#666666"
                                android:textSize="14sp" />
                                android:textSize="16sp" />
                        </LinearLayout>
                        <!-- 手机号 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:background="#FFFFFF"
                            android:layout_marginBottom="2dp"
                            android:background="#F8F9FA"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="8dp">
                            android:padding="12dp">
                            <TextView
                                android:layout_width="80dp"
                                android:layout_width="90dp"
                                android:layout_height="wrap_content"
                                android:text="手机号:"
                                android:textColor="#333333"
                                android:textSize="14sp"
                                android:textSize="16sp"
                                android:textStyle="bold" />
                            <TextView
@@ -319,24 +324,25 @@
                                android:layout_weight="1"
                                android:text="--"
                                android:textColor="#666666"
                                android:textSize="14sp" />
                                android:textSize="16sp" />
                        </LinearLayout>
                        <!-- 卡内余额 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="1dp"
                            android:background="#F8F9FA"
                            android:layout_marginBottom="2dp"
                            android:background="#FFFFFF"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="8dp">
                            android:padding="12dp">
                            <TextView
                                android:layout_width="80dp"
                                android:layout_width="90dp"
                                android:layout_height="wrap_content"
                                android:text="卡内余额:"
                                android:textColor="#333333"
                                android:textSize="14sp"
                                android:textSize="16sp"
                                android:textStyle="bold" />
                            <TextView
@@ -346,7 +352,7 @@
                                android:layout_weight="1"
                                android:text="0.00元"
                                android:textColor="#FF6B35"
                                android:textSize="14sp"
                                android:textSize="18sp"
                                android:textStyle="bold" />
                        </LinearLayout>
@@ -366,70 +372,44 @@
        android:background="#FFFFFF"
        android:elevation="4dp"
        android:orientation="vertical"
        android:padding="16dp"
        android:visibility="gone">
        <!-- 支付方式选择区域 -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_marginBottom="16dp">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="8dp"
                android:text="支付方式"
                android:textColor="#333333"
                android:textSize="16sp"
                android:textStyle="bold" />
            <RadioGroup
                android:id="@+id/payment_method_group"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
                <!-- 支付方式RadioButton将动态添加 -->
            </RadioGroup>
        </LinearLayout>
        android:padding="20dp"
        android:visibility="visible">
        <!-- 工本费和返回金额输入区域 -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginBottom="16dp">
            android:layout_marginBottom="20dp">
            <!-- 返回金额输入 -->
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:layout_marginEnd="8dp"
                android:layout_marginEnd="12dp"
                android:orientation="vertical">
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="6dp"
                    android:layout_marginBottom="8dp"
                    android:text="返回金额(元)"
                    android:textColor="#333333"
                    android:textSize="14sp"
                    android:textSize="16sp"
                    android:textStyle="bold" />
                <EditText
                    android:id="@+id/et_return_amount"
                    android:layout_width="match_parent"
                    android:layout_height="48dp"
                    android:layout_height="56dp"
                    android:background="@drawable/edit_text_bg"
                    android:hint="返回金额"
                    android:inputType="numberDecimal"
                    android:padding="12dp"
                    android:padding="16dp"
                    android:text=""
                    android:textColor="#333333"
                    android:textColorHint="#999999"
                    android:textSize="16sp" />
                    android:textSize="18sp" />
            </LinearLayout>
            <!-- 工本费输入 -->
@@ -437,32 +417,31 @@
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:layout_marginEnd="8dp"
                android:layout_marginStart="12dp"
                android:orientation="vertical">
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="6dp"
                    android:layout_marginBottom="8dp"
                    android:text="工本费(元)"
                    android:textColor="#333333"
                    android:textSize="14sp"
                    android:textSize="16sp"
                    android:textStyle="bold" />
                <EditText
                    android:id="@+id/et_card_cost"
                    android:layout_width="match_parent"
                    android:layout_height="48dp"
                    android:layout_height="56dp"
                    android:background="@drawable/edit_text_bg"
                    android:hint="工本费"
                    android:inputType="numberDecimal"
                    android:padding="12dp"
                    android:padding="16dp"
                    android:text=""
                    android:textColor="#333333"
                    android:textColorHint="#999999"
                    android:textSize="16sp" />
                    android:textSize="18sp" />
            </LinearLayout>
        </LinearLayout>
@@ -470,11 +449,11 @@
        <Button
            android:id="@+id/btn_replace"
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:layout_height="56dp"
            android:background="@drawable/button_blue_bg"
            android:text="确认补卡"
            android:textColor="#FFFFFF"
            android:textSize="@dimen/big_text_size"
            android:textSize="20sp"
            android:textStyle="bold" />
    </LinearLayout>
generallibrary/src/main/res/layout/activity_manager_read.xml
@@ -87,6 +87,34 @@
                        android:textSize="16sp" />
                </LinearLayout>
                <!-- 项目号输入框 - 仅在区域表号卡时显示 -->
                <LinearLayout
                    android:id="@+id/project_number_layout"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal"
                    android:padding="16dp"
                    android:visibility="gone">
                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="项目号:"
                        android:textColor="#333333"
                        android:textSize="16sp" />
                    <EditText
                        android:id="@+id/project_number_et"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:background="@null"
                        android:hint="请输入项目号(1-255)"
                        android:inputType="number"
                        android:maxLength="3"
                        android:text="@={viewModel.projectNumber}"
                        android:textSize="16sp" />
                </LinearLayout>
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"