左晓为主开发手持机充值管理机
feat(generallibrary): 优化充值功能并添加水价获取- 在 activity_recharge_detail.xml 中移除了不必要的 bold 样式- 在 EditText 中添加了金额输入限制(数字和小数点)- 在 BaseApplication 中添加了水价信息存储
- 在 MainActivity 中实现了延时获取水价的功能- 优化了 RechargeDetailActivity 中的水价检查和使用逻辑
- 在 README.md 中添加了金额格式化和水价获取的最佳实践指南
14个文件已修改
3个文件已添加
1213 ■■■■■ 已修改文件
README.md 583 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/BaseNfcReadHelper.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/NativeNfcReadHelper.java 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/NfcReadAdapter.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/BaseApplication.kt 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/MainActivity.kt 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/NfcWreatActivity.kt 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/RechargeDetailActivity.kt 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/RechargeFragment.kt 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/bean/net/RechargeRequest.kt 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/bean/net/RechargeResult.kt 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/bean/net/WaterPriceResult.kt 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/tool/CardOperationType.kt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/tool/NfcReadHelper.kt 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/layout/activity_recharge_detail.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/layout/dialog_search.xml 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/layout/fragment_recharge.xml 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md
@@ -741,6 +741,589 @@
   - 错误信息使用带标题的Dialog,提供清晰的错误分类
   - 长时间操作显示加载Dialog,避免用户误操作
### 金额格式化最佳实践
在处理金额相关的数据时,应该统一使用两位小数格式,确保显示的一致性和准确性。
#### 金额格式化方法
```kotlin
// 使用String.format格式化金额为两位小数
val amount = 123.4
val formattedAmount = String.format("%.2f", amount)
// 结果: "123.40"
// 在显示时添加货币单位
binding.balanceText.text = "${formattedAmount} 元"
```
#### 金额输入验证
```kotlin
private fun validateAmount(amountStr: String): Double? {
    if (amountStr.isEmpty()) {
        ToastUtil.show("请输入金额")
        return null
    }
    val amount = try {
        amountStr.toDouble()
    } catch (e: NumberFormatException) {
        ToastUtil.show("请输入有效的金额")
        return null
    }
    if (amount <= 0) {
        ToastUtil.show("金额必须大于0")
        return null
    }
    // 检查小数位数不超过2位
    val decimalPlaces = amountStr.substringAfter('.', "").length
    if (decimalPlaces > 2) {
        ToastUtil.show("金额最多保留两位小数")
        return null
    }
    return amount
}
```
#### 金额计算和显示示例
```kotlin
private fun calculateAndDisplayAmounts() {
    val rechargeAmount = validateAmount(binding.rechargeAmount.text.toString()) ?: return
    val bonusAmount = validateAmount(binding.bonusAmount.text.toString()) ?: 0.0
    // 计算总金额
    val totalAmount = rechargeAmount + bonusAmount
    // 格式化显示
    val formattedRecharge = String.format("%.2f", rechargeAmount)
    val formattedBonus = String.format("%.2f", bonusAmount)
    val formattedTotal = String.format("%.2f", totalAmount)
    // 更新UI显示
    binding.rechargeAmountText.text = "${formattedRecharge} 元"
    binding.bonusAmountText.text = "${formattedBonus} 元"
    binding.totalAmountText.text = "${formattedTotal} 元"
}
```
#### EditText金额输入限制
在布局文件中设置金额输入的限制:
```xml
<EditText
    android:id="@+id/amountEditText"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:inputType="numberDecimal"
    android:digits="0123456789."
    android:maxLength="10"
    android:hint="请输入金额" />
```
#### TextWatcher实现小数位限制
使用TextWatcher来限制用户只能输入最多两位小数:
```kotlin
/**
 * 设置金额输入限制,最多保留两位小数
 */
private fun setupAmountInputLimit(editText: EditText) {
    editText.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.isEmpty()) return
            // 检查是否包含小数点
            if (text.contains(".")) {
                val parts = text.split(".")
                if (parts.size == 2) {
                    val decimalPart = parts[1]
                    // 如果小数位超过2位,截取前两位
                    if (decimalPart.length > 2) {
                        val newText = "${parts[0]}.${decimalPart.substring(0, 2)}"
                        editText.removeTextChangedListener(this)
                        editText.setText(newText)
                        editText.setSelection(newText.length)
                        editText.addTextChangedListener(this)
                    }
                }
            }
            // 防止输入多个小数点
            val dotCount = text.count { it == '.' }
            if (dotCount > 1) {
                val newText = text.substring(0, text.lastIndexOf('.'))
                editText.removeTextChangedListener(this)
                editText.setText(newText)
                editText.setSelection(newText.length)
                editText.addTextChangedListener(this)
            }
            // 防止以小数点开头
            if (text.startsWith(".")) {
                editText.removeTextChangedListener(this)
                editText.setText("0$text")
                editText.setSelection(editText.text.length)
                editText.addTextChangedListener(this)
            }
        }
    })
}
// 在initView中调用
setupAmountInputLimit(binding.rechargeAmount)
setupAmountInputLimit(binding.bonusAmount)
```
#### 金额格式化注意事项
1. **统一格式**: 所有金额显示都使用两位小数格式
2. **输入验证**: 验证用户输入的金额格式和范围
3. **计算精度**: 使用Double类型进行金额计算,避免精度丢失
4. **显示单位**: 金额显示时统一添加货币单位(如"元")
5. **边界检查**: 检查金额的最大值和最小值限制
## 充值接口使用说明
项目中实现了完整的充值功能,包括充值接口调用和NFC写卡操作。
### 充值接口详情
**接口地址**: `/terminal/card/termRecharge`
**请求方式**: POST
**接口描述**: 终端充值接口,用于创建充值订单并返回写卡所需信息
#### 请求参数
```kotlin
data class RechargeRequest(
    val rechargeType: Int,           // 充值类型 (固定值: 2)
    val cardNum: String,             // 卡号
    val money: String,               // 充值金额 (格式: "300.0")
    val amount: String,              // 充值数量
    val gift: String,                // 赠送金额
    val paymentId: String,           // 支付方式ID
    val price: String,               // 单价 (格式: "0.90")
    val remarks: String,             // 备注 (默认: "充值")
    val operator: Long               // 操作员ID
)
```
#### 请求示例
```json
{
  "rechargeType": 2,
  "cardNum": "53232810100600001",
  "money": "300.0",
  "amount": "50",
  "gift": "5",
  "paymentId": "1838466162264350722",
  "price": "0.90",
  "remarks": "充值",
  "operator": 2024090516595200300
}
```
#### 返回参数
```kotlin
data class RechargeResult(
    val projectNo: Int,              // 项目编号
    val cardNum: String,             // 卡号
    val orderNo: String,             // 订单号
    val waterPrice: Double,          // 水价
    val time: String                 // 时间
)
```
#### 返回示例
```json
{
  "code": "0001",
  "content": {
    "projectNo": 10,
    "cardNum": "53232810100600001",
    "orderNo": "2506041414250065",
    "waterPrice": 0.9,
    "time": "2025-05-08 17:31:02"
  },
  "msg": "请求成功",
  "success": true
}
```
### 充值功能实现
#### 1. 在Activity中调用充值接口
```kotlin
/**
 * 调用充值接口
 */
private fun callRechargeApi(rechargeAmount: Double, bonusAmount: Double) {
    val cardNum = cardInfo?.cardNum ?: cardAddress ?: ""
    // 构建充值请求参数
    val params = mapOf(
        "rechargeType" to 2,
        "cardNum" to cardNum,
        "money" to String.format("%.1f", rechargeAmount),
        "amount" to String.format("%.0f", bonusAmount),
        "gift" to String.format("%.0f", bonusAmount),
        "paymentId" to paymentId.toString(),
        "price" to "0.90",
        "remarks" to "充值",
        "operator" to 2024090516595200300L
    )
    ApiManager.getInstance().requestPostLoading(
        this,
        "terminal/card/termRecharge",
        RechargeResult::class.java,
        params,
        object : SubscriberListener<BaseResponse<RechargeResult>>() {
            override fun onNext(response: BaseResponse<RechargeResult>) {
                if (response.success && response.code == "0001") {
                    // 充值成功,跳转到写卡界面
                    response.content?.let { rechargeResult ->
                        startWriteCardActivity(rechargeResult, rechargeAmount, bonusAmount)
                    }
                } else {
                    ToastUtil.show("充值失败: ${response.msg ?: "未知错误"}")
                }
            }
            override fun onError(e: Throwable?) {
                super.onError(e)
                ToastUtil.show("充值失败: ${e?.message ?: "网络异常"}")
            }
        }
    )
}
```
#### 2. 跳转到写卡界面
```kotlin
/**
 * 启动写卡界面
 */
private fun startWriteCardActivity(rechargeResult: RechargeResult, rechargeAmount: Double, bonusAmount: Double) {
    // 创建UserCard对象用于写卡
    val userCard = UserCard().apply {
        cardInfo?.let { info ->
            userCode = info.cardNum ?: ""
            balance = ((rechargeAmount + bonusAmount) * 100).toInt() // 转换为分
        }
        // 设置其他必要信息
        projectCode = rechargeResult.projectNo
        waterPrice = rechargeResult.waterPrice.toFloat()
        rechargeDate = java.util.Calendar.getInstance()
    }
    // 启动写卡Activity
    val intent = Intent(this, NfcWreatActivity::class.java).apply {
        putExtra("cardType", "USER_CARD")
        putExtra("cardAddr", cardAddress)
        putExtra("operationTypeCode", CardOperationType.Recharge.code)
        putExtra("orderNumber", rechargeResult.orderNo)
        putExtra("userCard", userCard)
    }
    startActivity(intent)
}
```
#### 3. NFC写卡处理
在`NfcWreatActivity`中,充值操作会被自动处理:
```kotlin
CardOperationType.Recharge -> {
    nfcWreatHelper.writeUserDataAsync(userCard, object : NFCCallBack {
        override fun isSusses(flag: Boolean, msg: String?) {
            runOnUiThread {
                if (flag) {
                    postCardData(cardType, cardAddr)
                    ToastUtil.show("充值写卡成功!")
                } else {
                    ToastUtil.show("充值写卡失败: ${msg ?: "未知错误"}")
                }
            }
        }
    })
}
```
### 充值流程说明
1. **用户输入**: 用户在充值界面输入充值金额和赠送金额
2. **参数验证**: 验证输入的金额格式和有效性
3. **接口调用**: 调用`/terminal/card/termRecharge`接口创建充值订单
4. **订单创建**: 服务器创建充值订单并返回订单信息
5. **跳转写卡**: 成功后跳转到NFC写卡界面
6. **NFC写卡**: 用户贴卡进行NFC写卡操作
7. **写卡完成**: 写卡成功后更新卡片余额
### 注意事项
1. **金额格式**: 充值金额使用一位小数格式,如"300.0"
2. **操作员ID**: 需要根据实际登录用户设置正确的操作员ID
3. **支付方式**: 支付方式ID需要从支付方式接口获取
4. **错误处理**: 充值失败时要提供明确的错误信息
5. **写卡验证**: 写卡前要验证卡号一致性
6. **订单跟踪**: 保存订单号用于后续查询和跟踪
### 相关数据类
```kotlin
// 充值请求数据类
data class RechargeRequest(
    val rechargeType: Int,
    val cardNum: String,
    val money: String,
    val amount: String,
    val gift: String,
    val paymentId: String,
    val price: String,
    val remarks: String,
    val operator: Long
)
// 充值结果数据类
data class RechargeResult(
    val projectNo: Int,
    val cardNum: String,
    val orderNo: String,
    val waterPrice: Double,
    val time: String
)
```
## 水价接口使用说明
项目中实现了水价的动态获取和管理功能,支持从服务器获取最新水价并在全局范围内使用。
### 水价接口详情
**接口地址**: `/terminal/client/getWaterPrice`
**请求方式**: GET
**接口描述**: 获取当前系统水价信息
#### 返回参数
```kotlin
data class WaterPriceResult(
    val price: Double                // 水价
)
```
#### 返回示例
```json
{
  "code": "0001",
  "content": {
    "price": 0.9
  },
  "msg": "请求成功",
  "success": true
}
```
### 水价功能实现
#### 1. BaseApplication中的水价存储
```kotlin
class BaseApplication {
    companion object {
        // 水价信息
        var waterPrice: Double = 0.0
    }
}
```
#### 2. MainActivity中延时获取水价
```kotlin
class MainActivity : BaseNfcActivity() {
    private val handler = Handler(Looper.getMainLooper())
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ... 其他初始化代码 ...
        // 延时20秒后获取水价,避免影响启动性能
        handler.postDelayed({
            getWaterPrice()
        }, 20000) // 20秒延时
    }
    /**
     * 获取水价信息
     */
    private fun getWaterPrice() {
        ApiManager.getInstance().requestGetLoading(
            this,
            "terminal/client/getWaterPrice",
            WaterPriceResult::class.java,
            null,
            object : SubscriberListener<BaseResponse<WaterPriceResult>>() {
                override fun onNext(response: BaseResponse<WaterPriceResult>) {
                    if (response.success && response.code == "0001") {
                        // 获取水价成功,保存到BaseApplication
                        response.content?.let { waterPriceResult ->
                            BaseApplication.waterPrice = waterPriceResult.price
                        }
                    }
                }
                override fun onError(e: Throwable?) {
                    super.onError(e)
                    // 网络异常时不显示错误信息,避免影响用户体验
                }
            }
        )
    }
    override fun onDestroy() {
        super.onDestroy()
        // 清理Handler回调,防止内存泄漏
        handler.removeCallbacksAndMessages(null)
    }
}
```
#### 3. RechargeDetailActivity中的水价检查和使用
```kotlin
class RechargeDetailActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ... 其他初始化代码 ...
        // 检查并获取水价
        checkAndGetWaterPrice()
    }
    /**
     * 检查并获取水价
     */
    private fun checkAndGetWaterPrice() {
        // 如果BaseApplication中的水价为空或为0,则重新获取
        if (BaseApplication.waterPrice <= 0.0) {
            getWaterPrice()
        }
    }
    /**
     * 获取水价信息
     */
    private fun getWaterPrice() {
        ApiManager.getInstance().requestGetLoading(
            this,
            "terminal/client/getWaterPrice",
            WaterPriceResult::class.java,
            null,
            object : SubscriberListener<BaseResponse<WaterPriceResult>>() {
                override fun onNext(response: BaseResponse<WaterPriceResult>) {
                    if (response.success && response.code == "0001") {
                        // 获取水价成功,保存到BaseApplication
                        response.content?.let { waterPriceResult ->
                            BaseApplication.waterPrice = waterPriceResult.price
                        }
                    } else {
                        // 获取水价失败,使用默认值
                        if (BaseApplication.waterPrice <= 0.0) {
                            BaseApplication.waterPrice = 0.9 // 设置默认水价
                        }
                    }
                }
                override fun onError(e: Throwable?) {
                    super.onError(e)
                    // 网络异常,使用默认值
                    if (BaseApplication.waterPrice <= 0.0) {
                        BaseApplication.waterPrice = 0.9 // 设置默认水价
                    }
                }
            }
        )
    }
    /**
     * 在充值接口中使用水价
     */
    private fun callRechargeApi(rechargeAmount: Double, bonusAmount: Double) {
        // 获取当前水价,如果为0则使用默认值
        val currentWaterPrice = if (BaseApplication.waterPrice > 0.0) {
            BaseApplication.waterPrice
        } else {
            0.9 // 默认水价
        }
        // 构建充值请求参数
        val params = mapOf(
            // ... 其他参数 ...
            "price" to String.format("%.2f", currentWaterPrice), // 使用获取到的水价
            // ... 其他参数 ...
        )
        // ... 接口调用代码 ...
    }
}
```
### 水价管理流程
1. **应用启动**: MainActivity启动后延时20秒获取水价,避免影响启动性能
2. **全局存储**: 获取到的水价存储在BaseApplication.waterPrice中
3. **按需获取**: 在需要使用水价的界面(如充值界面)检查水价是否为空
4. **重新获取**: 如果水价为空或为0,则重新调用接口获取
5. **默认处理**: 如果获取失败,使用默认水价0.9元
6. **实时使用**: 在充值等业务中使用最新的水价信息
### 技术特点
1. **性能优化**: 延时获取避免影响应用启动速度
2. **全局共享**: 水价信息在BaseApplication中全局共享
3. **按需刷新**: 在需要时检查并重新获取水价
4. **容错处理**: 获取失败时使用默认水价,确保业务正常进行
5. **内存管理**: 正确处理Handler回调,防止内存泄漏
### 使用注意事项
1. **延时设置**: MainActivity中的20秒延时可根据实际需求调整
2. **默认水价**: 默认水价0.9元可根据实际业务需求修改
3. **错误处理**: 水价获取失败时不显示错误信息,避免影响用户体验
4. **数据格式**: 水价使用Double类型存储,使用时格式化为两位小数
5. **生命周期**: 注意在Activity销毁时清理相关资源
### 相关数据类
```kotlin
// 水价结果数据类
data class WaterPriceResult(
    val price: Double                // 水价
)
```
## 贡献指南
1. Fork 项目
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/BaseNfcReadHelper.java
@@ -5,6 +5,8 @@
import com.dayu.baselibrary.bean.BaseManagerToUserCard;
import com.dayu.baselibrary.bean.BaseUserCardCard;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
@@ -49,7 +51,18 @@
     *
     * @return
     */
    public abstract String getCradTypeAndCardNumber();
    public  String getCradTypeAndCardNumber(){
        return getCradTypeAndCardNumber(1);
    };
    /**
     * 获取卡片类型和卡号
     *
     * @return
     */
    public  String getCradTypeAndCardNumber(int sectorIndex){
        return null;
    };
    /**
     * 读取NFC卡的特定扇区信息
@@ -81,6 +94,8 @@
        return null;
    }
    /**
     * 返回监听类
     */
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/NativeNfcReadHelper.java
@@ -7,6 +7,8 @@
import android.nfc.tech.MifareClassic;
import android.util.Log;
import androidx.annotation.NonNull;
import com.dayu.baselibrary.bean.BaseManagerToUserCard;
import com.dayu.baselibrary.bean.BaseUserCardCard;
import com.dayu.baselibrary.tools.HexUtil;
@@ -29,8 +31,6 @@
    private Tag tag;
    //    private NFCCallback callback;
    private static NativeNfcReadHelper helper;
    @Override
@@ -435,9 +435,6 @@
    }
    public String getCardNumberNoClose() {
        if (tag == null) {
            return "";
@@ -483,13 +480,19 @@
        return "";
    }
    @Override
    public String getCradTypeAndCardNumber() {
        return getCradTypeAndCardNumber(1);
    }
    /**
     * 获取卡片类型和卡号
     *
     * @return
     */
    @Override
    public String getCradTypeAndCardNumber() {
    public String getCradTypeAndCardNumber(int sectorIndex) {
        MifareClassic mfc = MifareClassic.get(tag);
        if (null != mfc) {
@@ -498,10 +501,18 @@
                StringBuilder strData = new StringBuilder();
                //获取当前卡号
                boolean isOpen = false;
                for (int i = 0; i < listKeyA.size(); i++) {
                    if (mfc.authenticateSectorWithKeyA(0, listKeyA.get(i))) {
                if (!listKeyA.isEmpty()) {
                    for (int i = 0; i < listKeyA.size(); i++) {
                        if (mfc.authenticateSectorWithKeyA(0, listKeyA.get(i))) {
                            isOpen = true;
                            break;
                        }
                    }
                } else if (!listA_PS.isEmpty()) {
                    if (mfc.authenticateSectorWithKeyA(0, listA_PS.get(0))) {
                        isOpen = true;
                        break;
                    } else if (mfc.authenticateSectorWithKeyA(0, defauleKey)) {
                        isOpen = true;
                    }
                }
                if (isOpen) {
@@ -517,15 +528,23 @@
                    }
                }
                //获取卡片类型
                for (int i = 0; i < listKeyA.size(); i++) {
                    if (mfc.authenticateSectorWithKeyA(1, listKeyA.get(i))) {
                if (!listKeyA.isEmpty()) {
                    for (int i = 0; i < listKeyA.size(); i++) {
                        if (mfc.authenticateSectorWithKeyA(sectorIndex, listKeyA.get(i))) {
                            isOpen = true;
                            break;
                        }
                    }
                } else if (!listA_PS.isEmpty()) {
                    if (mfc.authenticateSectorWithKeyA(sectorIndex, listA_PS.get(sectorIndex))) {
                        isOpen = true;
                        break;
                    } else if (mfc.authenticateSectorWithKeyA(sectorIndex, defauleKey)) {
                        isOpen = true;
                    }
                }
                if (isOpen) {
                    int bIndex = mfc.sectorToBlock(1);
                    byte[] data = mfc.readBlock(bIndex + 0);
                    int bIndex = mfc.sectorToBlock(sectorIndex);
                    byte[] data = mfc.readBlock(bIndex + sectorIndex);
                    if (data != null && data.length > 0) {
                        String hex = HexUtil.byteToHex(data[0]);
                        strData.append(hex);
@@ -676,4 +695,6 @@
    }
}
baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/NfcReadAdapter.java
@@ -3,9 +3,13 @@
import android.app.Activity;
import android.content.Intent;
import androidx.annotation.NonNull;
import com.dayu.baselibrary.activity.BaseNfcActivity;
import com.dayu.baselibrary.bean.BaseUserCardCard;
import com.dayu.baselibrary.utils.ModelUtils;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -92,8 +96,15 @@
        switch (BaseNfcActivity.adapterType) {
            case ModelUtils.defaultType:
                return nativeNfcReadHelper.getCradTypeAndCardNumber();
        }
        return "";
    }
    @Override
    public String getCradTypeAndCardNumber(int sectorIndex) {
        switch (BaseNfcActivity.adapterType) {
            case ModelUtils.defaultType:
                return nativeNfcReadHelper.getCradTypeAndCardNumber(sectorIndex);
        }
        return "";
    }
@@ -126,4 +137,8 @@
        }
        return null;
    }
}
generallibrary/src/main/java/com/dayu/general/BaseApplication.kt
@@ -27,6 +27,11 @@
        var blockId:String=""
        var blockName:String=""
        // 水价信息
        var waterPrice: Double = 0.0
        // MainActivity的引用,用于调用水价获取方法
        private var mainActivityInstance: com.dayu.general.activity.MainActivity? = null
        var projectDataDao: ProjectDataDao? = null
@@ -42,6 +47,25 @@
            return myApplication as BaseApplication
        }
        /**
         * 设置MainActivity实例
         */
        fun setMainActivity(activity: com.dayu.general.activity.MainActivity?) {
            mainActivityInstance = activity
        }
        /**
         * 请求获取水价,如果为空则调用MainActivity的获取方法
         */
        fun requestWaterPrice(): Double {
            if (waterPrice <= 0.0) {
                // 如果水价为空且MainActivity实例存在,则调用获取方法
                mainActivityInstance?.getWaterPriceFromActivity()
            }
            // 如果水价仍为0,返回默认值
            return if (waterPrice > 0.0) waterPrice else 0.9
        }
generallibrary/src/main/java/com/dayu/general/activity/MainActivity.kt
@@ -2,6 +2,8 @@
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.KeyEvent
import android.view.LayoutInflater
import android.widget.Toast
@@ -13,6 +15,7 @@
import com.dayu.general.R
import com.dayu.general.adapter.TabAdapter
import com.dayu.general.bean.net.UserInfoResult
import com.dayu.general.bean.net.WaterPriceResult
import com.dayu.general.databinding.ActivityMainBinding
import com.dayu.general.net.ApiManager
import com.dayu.general.net.BaseResponse
@@ -22,15 +25,33 @@
    var binding: ActivityMainBinding? = null
    private val fragments: ArrayList<Fragment> = ArrayList()
    var mExitTime: Long = 0
    private val handler = Handler(Looper.getMainLooper())
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(LayoutInflater.from(this))
        setContentView(binding?.root)
        // 注册MainActivity实例到BaseApplication
        BaseApplication.setMainActivity(this)
        setupFragments()
        initView()
        initTab()
        getUserInfo()
        // 延时20秒后获取水价
        handler.postDelayed({
            getWaterPriceFromActivity()
        }, 20000) // 20秒延时
    }
    override fun onDestroy() {
        super.onDestroy()
        // 清理Handler回调,防止内存泄漏
        handler.removeCallbacksAndMessages(null)
        // 清理BaseApplication中的MainActivity引用
        BaseApplication.setMainActivity(null)
    }
    override fun onNfcBack(intent: Intent) {
@@ -45,6 +66,37 @@
        }
    }
    /**
     * 获取水价信息 - 公开方法供其他地方调用
     */
    fun getWaterPriceFromActivity() {
        // 如果水价已存在且大于0,则不重复获取
        if (BaseApplication.waterPrice > 0.0) {
            return
        }
        ApiManager.getInstance().requestGetHideLoading(
            this,
            "terminal/client/getWaterPrice",
            WaterPriceResult::class.java,
            null,
            object : SubscriberListener<BaseResponse<WaterPriceResult>>() {
                override fun onNext(response: BaseResponse<WaterPriceResult>) {
                    if (response.success && response.code == "0001") {
                        // 获取水价成功,保存到BaseApplication
                        response.content?.let { waterPriceResult ->
                            BaseApplication.waterPrice = waterPriceResult.price
                        }
                    }
                }
                override fun onError(e: Throwable?) {
                    super.onError(e)
                    // 网络异常时不显示错误信息,避免影响用户体验
                }
            }
        )
    }
    private fun getUserInfo() {
        // 使用正确的类型参数
generallibrary/src/main/java/com/dayu/general/activity/NfcWreatActivity.kt
@@ -83,10 +83,21 @@
                    binding?.cardData?.text = textData.toString()
                }
                CardOperationType.Recharge -> {
                    var textData = StringBuilder()
                    textData.append("用户充值\n")
                    textData.append("订单号:" + orderNumber + "\n")
                    if (userCard.balance != 0) {
                        val balanceInYuan = userCard.balance / 100.0 // 转换为元
                        textData.append("充值金额:" + String.format("%.2f", balanceInYuan) + "元")
                    }
                    binding?.cardData?.text = textData.toString()
                }
                CardOperationType.CancelCard -> TODO()
                CardOperationType.CheckCard -> TODO()
                CardOperationType.DeductCard -> TODO()
                CardOperationType.Recharge -> TODO()
                CardOperationType.ReplaceCard -> TODO()
                null -> TODO()
            }
@@ -136,10 +147,25 @@
                }
                CardOperationType.Recharge -> {
                    nfcWreatHelper.writeUserDataAsync(userCard, object : NFCCallBack {
                        override fun isSusses(flag: Boolean, msg: String?) {
                            // 确保Toast在主线程中调用
                            runOnUiThread {
                                if (flag) {
                                    postCardData(cardType, cardAddr)
                                    ToastUtil.show("充值写卡成功!")
                                } else {
                                    ToastUtil.show("充值写卡失败: ${msg ?: "未知错误"}")
                                }
                            }
                        }
                    })
                }
                CardOperationType.CancelCard -> TODO()
                CardOperationType.CheckCard -> TODO()
                CardOperationType.DeductCard -> TODO()
                CardOperationType.Recharge -> TODO()
                CardOperationType.ReplaceCard -> TODO()
                null -> TODO()
            }
generallibrary/src/main/java/com/dayu/general/activity/RechargeDetailActivity.kt
@@ -3,27 +3,36 @@
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.TypedValue
import android.view.View
import android.widget.EditText
import android.widget.RadioButton
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.dayu.baselibrary.net.subscribers.SubscriberListener
import com.dayu.baselibrary.utils.ToastUtil
import com.dayu.baselibrary.view.TitleBar
import com.dayu.general.BaseApplication
import com.dayu.general.R
import com.dayu.general.bean.net.CardInfoResult
import com.dayu.general.bean.net.PaymentMethod
import com.dayu.general.bean.net.PaymentMethodResponse
import com.dayu.general.bean.net.RechargeRequest
import com.dayu.general.bean.net.RechargeResult
import com.dayu.general.bean.card.UserCard
import com.dayu.general.databinding.ActivityRechargeDetailBinding
import com.dayu.general.net.ApiManager
import com.dayu.general.net.BaseResponse
import com.dayu.general.tool.CardOperationType
class RechargeDetailActivity : AppCompatActivity() {
    private lateinit var binding: ActivityRechargeDetailBinding
    private var cardInfo: CardInfoResult? = null
    private var cardAddress: String? = null
    private var userCard: UserCard? = null
    // 支付方式相关属性
    private var paymentMethod: String = "现金"
@@ -33,11 +42,13 @@
    companion object {
        private const val EXTRA_CARD_INFO = "extra_card_info"
        private const val EXTRA_CARD_ADDRESS = "extra_card_address"
        private const val EXTRA_USER_CARD = "extra_user_card"
        fun start(context: Context, cardInfo: CardInfoResult?, cardAddress: String?) {
        fun start(context: Context, cardInfo: CardInfoResult?, cardAddress: String?, userCard: UserCard?) {
            val intent = Intent(context, RechargeDetailActivity::class.java)
            intent.putExtra(EXTRA_CARD_INFO, cardInfo)
            intent.putExtra(EXTRA_CARD_ADDRESS, cardAddress)
            intent.putExtra(EXTRA_USER_CARD, userCard)
            context.startActivity(intent)
        }
    }
@@ -53,11 +64,14 @@
        // 获取传递的数据
        cardInfo = intent.getSerializableExtra(EXTRA_CARD_INFO) as? CardInfoResult
        cardAddress = intent.getStringExtra(EXTRA_CARD_ADDRESS)
        userCard = intent.getSerializableExtra(EXTRA_USER_CARD) as? UserCard
        initView()
        displayCardInfo()
        // 获取支付方式
        getPaymentMethods()
        // 确保水价可用(如果为空会自动触发MainActivity获取)
        BaseApplication.requestWaterPrice()
    }
    private fun initView() {
@@ -68,9 +82,63 @@
        // 设置按钮点击事件
        binding.rechargeRegistBtn.setOnClickListener {
            // 处理写卡逻辑
            handleWriteCard()
            // 处理充值逻辑
            handleRecharge()
        }
        // 设置金额输入限制
        setupAmountInputLimit(binding.rechargeMorny)
        setupAmountInputLimit(binding.rechargeWater)
    }
    /**
     * 设置金额输入限制,最多保留两位小数
     */
    private fun setupAmountInputLimit(editText: EditText) {
        editText.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.isEmpty()) return
                // 检查是否包含小数点
                if (text.contains(".")) {
                    val parts = text.split(".")
                    if (parts.size == 2) {
                        val decimalPart = parts[1]
                        // 如果小数位超过2位,截取前两位
                        if (decimalPart.length > 2) {
                            val newText = "${parts[0]}.${decimalPart.substring(0, 2)}"
                            editText.removeTextChangedListener(this)
                            editText.setText(newText)
                            editText.setSelection(newText.length)
                            editText.addTextChangedListener(this)
                        }
                    }
                }
                // 防止输入多个小数点
                val dotCount = text.count { it == '.' }
                if (dotCount > 1) {
                    val newText = text.substring(0, text.lastIndexOf('.'))
                    editText.removeTextChangedListener(this)
                    editText.setText(newText)
                    editText.setSelection(newText.length)
                    editText.addTextChangedListener(this)
                }
                // 防止以小数点开头
                if (text.startsWith(".")) {
                    editText.removeTextChangedListener(this)
                    editText.setText("0$text")
                    editText.setSelection(editText.text.length)
                    editText.addTextChangedListener(this)
                }
            }
        })
    }
    /**
@@ -180,7 +248,16 @@
            binding.redInitCode.text = cardAddress ?: ""
            binding.userName.text = info.userName ?: ""
            binding.redCardNum.text = info.cardNum ?: ""
            binding.redRemainderBlance.text = "${info.balance ?: 0} 元"
            // 使用用户卡中的余额显示
            val balance = userCard?.let {
                // 将分转换为元,保留两位小数
                String.format("%.2f", it.balance / 100.0)
            } ?: run {
                // 如果用户卡为空,则使用服务器返回的余额
                String.format("%.2f", info.balance ?: 0.0)
            }
            binding.redRemainderBlance.text = "$balance 元"
            // 设置卡状态和对应颜色
            val cardStatus = when (info.status) {
@@ -203,7 +280,10 @@
        }
    }
    private fun handleWriteCard() {
    /**
     * 处理充值逻辑
     */
    private fun handleRecharge() {
        // 验证充值金额
        val rechargeAmountStr = binding.rechargeMorny.text.toString().trim()
        if (rechargeAmountStr.isEmpty()) {
@@ -235,20 +315,115 @@
            0.0
        }
        // 显示确认信息
        val totalAmount = rechargeAmount + bonusAmount
        val confirmMessage = """
            确认充值信息:
            卡号:${cardAddress}
            充值金额:${rechargeAmount}元
            赠送金额:${bonusAmount}元
            总金额:${totalAmount}元
            支付方式:${paymentMethod}
        """.trimIndent()
        // 调用充值接口
        callRechargeApi(rechargeAmount, bonusAmount)
    }
        ToastUtil.show(confirmMessage)
    /**
     * 调用充值接口
     */
    private fun callRechargeApi(rechargeAmount: Double, bonusAmount: Double) {
        val cardNum = cardInfo?.cardNum ?: cardAddress ?: ""
        if (cardNum.isEmpty()) {
            ToastUtil.show("卡号信息缺失")
            return
        }
        // TODO: 调用写卡API
        // 这里可以添加实际的写卡逻辑
        // 获取水价(如果为空会自动触发MainActivity获取)
        val currentWaterPrice = BaseApplication.requestWaterPrice()
        // 构建充值请求参数
        val rechargeRequest = RechargeRequest(
            rechargeType = 2,
            cardNum = cardNum,
            money = String.format("%.0f", rechargeAmount),
            amount = String.format("%.0f", bonusAmount),
            gift = String.format("%.0f", bonusAmount),
            paymentId = paymentId.toString(),
            price = String.format("%.2f", currentWaterPrice), // 使用统一获取的水价
            remarks = "充值",
            operator = BaseApplication.userId // 默认操作员ID,可以根据实际情况调整
        )
        // 转换为Map格式
        val params = mapOf(
            "rechargeType" to rechargeRequest.rechargeType,
            "cardNum" to rechargeRequest.cardNum,
            "money" to rechargeRequest.money,
            "amount" to rechargeRequest.amount,
            "gift" to rechargeRequest.gift,
            "paymentId" to rechargeRequest.paymentId,
            "price" to rechargeRequest.price,
            "remarks" to rechargeRequest.remarks,
            "operator" to rechargeRequest.operator
        )
        ApiManager.getInstance().requestPostLoading(
            this,
            "terminal/card/termRecharge",
            RechargeResult::class.java,
            params,
            object : SubscriberListener<BaseResponse<RechargeResult>>() {
                override fun onNext(response: BaseResponse<RechargeResult>) {
                    if (response.success && response.code == "0001") {
                        // 充值成功,跳转到写卡界面
                        response.content?.let { rechargeResult ->
                            startWriteCardActivity(rechargeResult, rechargeAmount, bonusAmount)
                        } ?: run {
                            ToastUtil.show("充值成功但返回数据为空")
                        }
                    } else {
                        ToastUtil.show("充值失败: ${response.msg ?: "未知错误"}")
                    }
                }
                override fun onError(e: Throwable?) {
                    super.onError(e)
                    ToastUtil.show("充值失败: ${e?.message ?: "网络异常"}")
                }
            }
        )
    }
    /**
     * 启动写卡界面
     */
    private fun startWriteCardActivity(rechargeResult: RechargeResult, rechargeAmount: Double, bonusAmount: Double) {
        try {
            // 创建UserCard对象用于写卡
            val userCard = UserCard().apply {
                // 设置用户卡信息
                cardInfo?.let { info ->
                    userCode = info.cardNum ?: ""
                    balance = ((rechargeAmount + bonusAmount) * 100).toInt() // 转换为分
                }
                // 设置其他必要信息
                projectCode = rechargeResult.projectNo
                waterPrice = rechargeResult.waterPrice.toFloat()
                rechargeDate = java.util.Calendar.getInstance()
            }
            // 启动写卡Activity
            val intent = Intent(this, NfcWreatActivity::class.java).apply {
                putExtra("cardType", "USER_CARD") // 用户卡类型
                putExtra("cardAddr", cardAddress)
                putExtra("operationTypeCode", CardOperationType.Recharge.code)
                putExtra("orderNumber", rechargeResult.orderNo)
                putExtra("userCard", userCard)
            }
            startActivity(intent)
            // 显示成功信息
            val formattedRecharge = String.format("%.2f", rechargeAmount)
            val formattedBonus = String.format("%.2f", bonusAmount)
            val formattedTotal = String.format("%.2f", rechargeAmount + bonusAmount)
            ToastUtil.show("充值订单创建成功\n订单号: ${rechargeResult.orderNo}\n充值金额: ${formattedRecharge}元\n赠送金额: ${formattedBonus}元\n总金额: ${formattedTotal}元\n请贴卡进行写卡操作")
        } catch (e: Exception) {
            ToastUtil.show("启动写卡界面失败: ${e.message}")
        }
    }
generallibrary/src/main/java/com/dayu/general/activity/RechargeFragment.kt
@@ -10,11 +10,14 @@
import com.dayu.baselibrary.tools.nfc.NfcReadAdapter
import com.dayu.baselibrary.utils.ToastUtil
import com.dayu.baselibrary.view.ConfirmDialog
import com.dayu.baselibrary.view.TipDialog
import com.dayu.general.bean.net.CardInfoResult
import com.dayu.general.databinding.FragmentRechargeBinding
import com.dayu.general.net.ApiManager
import com.dayu.general.net.BaseResponse
import com.dayu.general.tool.NfcReadHelper
import com.dayu.general.bean.card.UserCard
import com.dayu.general.tool.CardCommon
class RechargeFragment : Fragment() {
    var binding: FragmentRechargeBinding? = null
@@ -28,25 +31,25 @@
        binding = FragmentRechargeBinding.inflate(inflater, container, false)
        return binding?.root
    }
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        initView()
    }
    private fun initView() {
        // 初始化界面显示读卡状态
        binding?.rechargeReadLL?.visibility = View.VISIBLE
    }
    private fun resetView() {
        // 重置界面显示读卡状态
        binding?.rechargeReadLL?.visibility = View.VISIBLE
    /**
     * 显示确认对话框
     */
    private fun showConfirmDialog(message: String, onConfirm: () -> Unit) {
        activity?.let { activity ->
            val confirmDialog = TipDialog(activity, message) {
                onConfirm()
            }
            confirmDialog.show()
        }
    }
    /**
     * 处理NFC刷卡信息
     * 该方法由MainActivity调用
@@ -56,32 +59,60 @@
            try {
                // 检查intent中是否包含NFC Tag
                if (intent.getParcelableExtra<android.nfc.Tag>(android.nfc.NfcAdapter.EXTRA_TAG) == null) {
                    ToastUtil.show("未检测到NFC卡片,请确保卡片已正确放置")
                    showConfirmDialog("未检测到NFC卡片,请确保卡片已正确放置") {
                    }
                    return
                }
                // 使用NfcReadAdapter读取卡号
                val nfcAdapter = NfcReadHelper.getInstance(intent, activity)
                cardNumber = nfcAdapter.getCardNumber()
                if (cardNumber.isNullOrEmpty()) {
                    ToastUtil.show("读卡失败,请重新刷卡")
                val cardTypeAndCardNumber = nfcAdapter.getCardTypeAndCardNumber()
                if (cardTypeAndCardNumber.isNullOrBlank() || !cardTypeAndCardNumber.contains(",")) {
                    showConfirmDialog("卡片信息读取失败,请重新刷卡") {
                    }
                    return
                }
                val parts = cardTypeAndCardNumber.split(",")
                if (parts.size < 2) {
                    showConfirmDialog("卡片信息格式异常,请重新刷卡") {
                    }
                    return
                }
                val cardNumber = parts[0]
                val cardType = parts[1]
                this.cardNumber = cardNumber
                if (cardNumber.isBlank()) {
                    showConfirmDialog("卡号为空,无法进行充值,请重新刷卡") {
                    }
                    return
                }
                if (cardType != CardCommon.USER_CARD_TYPE_1) {
                    showConfirmDialog("该卡片不是用户卡,请使用正确的用户卡进行充值操作。") {
                    }
                    return
                }
                // 解析用户卡数据
                val userCard = nfcAdapter.getUserCardData()
                if (userCard == null) {
                    showConfirmDialog("解析卡片数据失败,请重新刷卡") {
                    }
                    return
                }
                // 根据卡号获取卡片详细信息
                getCardInfo(cardNumber!!)
                getCardInfo(cardNumber, userCard)
            } catch (e: Exception) {
                ToastUtil.show("读卡异常:${e.message}")
                showConfirmDialog("读卡异常:${e.message}") {
                }
                e.printStackTrace()
            }
        }
    }
    /**
     * 获取卡片详细信息
     */
    private fun getCardInfo(cardNumber: String) {
    private fun getCardInfo(cardNumber: String, userCard: UserCard) {
        activity?.let { activity ->
            val map = mutableMapOf<String, Any>()
            map["cardAddr"] = cardNumber
@@ -93,8 +124,8 @@
                object : SubscriberListener<BaseResponse<CardInfoResult>>() {
                    override fun onNext(t: BaseResponse<CardInfoResult>) {
                        if (t.success) {
                            // 跳转到充值详情页面
                            RechargeDetailActivity.start(activity, t.content, cardNumber)
                            // 跳转到充值详情页面,传递用户卡信息
                            RechargeDetailActivity.start(activity, t.content, cardNumber, userCard)
                        } else {
                            // 处理获取失败的情况
                            handleCardInfoError(t.code, t.msg)
@@ -103,28 +134,28 @@
                    override fun onError(e: Throwable?) {
                        super.onError(e)
                        ToastUtil.show("获取卡信息失败: ${e?.message ?: "网络异常,请检查网络连接"}")
                        // 重置界面状态
                        resetView()
                        showConfirmDialog("获取卡信息失败: ${e?.message ?: "网络异常,请检查网络连接"}") {
                        }
                    }
                }
            )
        }
    }
    /**
     * 处理卡信息获取错误
     */
    private fun handleCardInfoError(code: String?, msg: String?) {
        val errorTitle: String
        val errorMessage: String
        when (code) {
            "1001" -> {
                // 数据不存在的特殊处理
                errorTitle = "卡片未注册"
                errorMessage = "该卡片未在系统中注册,请先进行开卡操作后再充值。"
            }
            else -> {
                // 其他错误的通用处理
                errorTitle = "获取卡信息失败"
@@ -137,14 +168,9 @@
                }
            }
        }
        // 显示确认对话框
        activity?.let { activity ->
            val confirmDialog = ConfirmDialog(activity, errorTitle, errorMessage) {
                // 点击确认按钮后关闭对话框并重置界面
                resetView()
            }
            confirmDialog.show()
        showConfirmDialog(errorMessage) {
        }
    }
}
generallibrary/src/main/java/com/dayu/general/bean/net/RechargeRequest.kt
New file
@@ -0,0 +1,18 @@
package com.dayu.general.bean.net
import java.io.Serializable
/**
 * 充值请求参数
 */
data class RechargeRequest(
    val rechargeType: Int,           // 充值类型
    val cardNum: String,             // 卡号
    val money: String,               // 充值金额
    val amount: String,              // 充值数量
    val gift: String,                // 赠送金额
    val paymentId: String,           // 支付方式ID
    val price: String,               // 单价
    val remarks: String,             // 备注
    val operator: String               // 操作员ID
) : Serializable
generallibrary/src/main/java/com/dayu/general/bean/net/RechargeResult.kt
New file
@@ -0,0 +1,14 @@
package com.dayu.general.bean.net
import java.io.Serializable
/**
 * 充值接口返回结果
 */
data class RechargeResult(
    val projectNo: Int,              // 项目编号
    val cardNum: String,             // 卡号
    val orderNo: String,             // 订单号
    val waterPrice: Double,          // 水价
    val time: String                 // 时间
) : Serializable
generallibrary/src/main/java/com/dayu/general/bean/net/WaterPriceResult.kt
New file
@@ -0,0 +1,10 @@
package com.dayu.general.bean.net
import java.io.Serializable
/**
 * 水价接口返回结果
 */
data class WaterPriceResult(
    val price: Double                // 水价
) : Serializable
generallibrary/src/main/java/com/dayu/general/tool/CardOperationType.kt
@@ -21,6 +21,8 @@
                3 -> CancelCard
                4 -> ReplaceCard
                5 -> DeductCard
                6 -> CleanCard
                7 -> CheckCard
                else -> null
            }
        }
generallibrary/src/main/java/com/dayu/general/tool/NfcReadHelper.kt
@@ -6,6 +6,7 @@
import android.widget.RelativeLayout
import com.dayu.baselibrary.tools.nfc.BaseNfcReadHelper
import com.dayu.baselibrary.tools.nfc.NfcReadAdapter
import com.dayu.general.bean.card.UserCard
import com.pnikosis.materialishprogress.ProgressWheel
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
@@ -165,7 +166,7 @@
     */
    fun getCardTypeAndCardNumber(): String {
        return try {
            adapter.cradTypeAndCardNumber
            adapter.getCradTypeAndCardNumber(7)
        } catch (e: Exception) {
            e.printStackTrace()
            ""
@@ -315,4 +316,27 @@
    fun clearDisposables() {
        compositeDisposable.clear()
    }
}
    /**
     * 读取用户卡数据
     * @return UserCard对象,如果读取失败则返回null
     */
    fun getUserCardData(): UserCard? {
        return try {
            // 获取基础卡数据
            val baseCard = adapter.getUserCardData(UserCard())
            // 如果获取成功且是UserCard类型,则返回
            if (baseCard is UserCard) {
                baseCard
            } else {
                null
            }
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }
}
generallibrary/src/main/res/layout/activity_recharge_detail.xml
@@ -133,7 +133,7 @@
                            android:text=""
                            android:textColor="#333333"
                            android:textSize="14sp"
                            android:textStyle="bold" />
                          />
                    </LinearLayout>
                    <!-- 卡状态 -->
@@ -266,6 +266,8 @@
                                android:background="@drawable/edit_text_bg"
                                android:hint="请输入充值金额"
                                android:inputType="numberDecimal"
                                android:digits="0123456789."
                                android:maxLength="10"
                                android:padding="12dp"
                                android:textSize="14sp"
                                android:maxLines="1" />
@@ -293,6 +295,8 @@
                                android:background="@drawable/edit_text_bg"
                                android:hint="赠送金额(选填)"
                                android:inputType="numberDecimal"
                                android:digits="0123456789."
                                android:maxLength="10"
                                android:padding="12dp"
                                android:textSize="14sp"
                                android:maxLines="1" />
generallibrary/src/main/res/layout/dialog_search.xml
@@ -29,7 +29,7 @@
        android:id="@+id/tv_farmer_name_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:layout_marginTop="20dp"
        android:text="农户名称"
        android:textColor="#666666"
        android:textSize="14sp"
@@ -39,11 +39,15 @@
    <EditText
        android:id="@+id/et_farmer_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="6dp"
        android:layout_height="48dp"
        android:layout_marginTop="8dp"
        android:background="@drawable/edit_text_bg"
        android:hint="请输入农户名称(选填)"
        android:inputType="text"
        android:paddingStart="16dp"
        android:paddingTop="12dp"
        android:paddingEnd="16dp"
        android:paddingBottom="12dp"
        android:singleLine="true"
        android:textColorHint="#BBBBBB"
        android:textSize="15sp"
@@ -53,7 +57,7 @@
        android:id="@+id/tv_farmer_id_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="14dp"
        android:layout_marginTop="18dp"
        android:text="农户编号"
        android:textColor="#666666"
        android:textSize="14sp"
@@ -63,11 +67,15 @@
    <EditText
        android:id="@+id/et_farmer_id"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="6dp"
        android:layout_height="48dp"
        android:layout_marginTop="8dp"
        android:background="@drawable/edit_text_bg"
        android:hint="请输入农户编号(选填)"
        android:inputType="text"
        android:paddingStart="16dp"
        android:paddingTop="12dp"
        android:paddingEnd="16dp"
        android:paddingBottom="12dp"
        android:singleLine="true"
        android:textColorHint="#BBBBBB"
        android:textSize="15sp"
@@ -77,7 +85,7 @@
        android:id="@+id/tv_card_number_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="14dp"
        android:layout_marginTop="18dp"
        android:text="卡号"
        android:textColor="#666666"
        android:textSize="14sp"
@@ -87,11 +95,15 @@
    <EditText
        android:id="@+id/et_card_number"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="6dp"
        android:layout_height="48dp"
        android:layout_marginTop="8dp"
        android:background="@drawable/edit_text_bg"
        android:hint="请输入卡号(选填)"
        android:inputType="text"
        android:paddingStart="16dp"
        android:paddingTop="12dp"
        android:paddingEnd="16dp"
        android:paddingBottom="12dp"
        android:singleLine="true"
        android:textColorHint="#BBBBBB"
        android:textSize="15sp"
@@ -101,13 +113,13 @@
        android:id="@+id/tv_cancel"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:layout_marginTop="28dp"
        android:layout_marginEnd="6dp"
        android:background="@drawable/cancel_button_ripple"
        android:clickable="true"
        android:focusable="true"
        android:paddingTop="10dp"
        android:paddingBottom="10dp"
        android:paddingTop="12dp"
        android:paddingBottom="12dp"
        android:stateListAnimator="@null"
        android:text="取消"
        android:textColor="#666666"
generallibrary/src/main/res/layout/fragment_recharge.xml
@@ -4,12 +4,19 @@
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/base_green_bg">
    <com.dayu.baselibrary.view.TitleBar
        android:id="@+id/titleBar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dimen_title_height"
        android:background="@color/title_bar_bg"
        android:elevation="4dp"
        app:centerText="充值" />
    <LinearLayout
        android:id="@+id/recharge_read_LL"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:layout_below="@+id/titleBar"
        android:orientation="vertical"
        android:visibility="visible">
@@ -65,14 +72,13 @@
                        android:scaleType="fitCenter"
                        android:src="@mipmap/nfc_write" />
                    <!-- 显示读到的卡号 -->
                    <TextView
                        android:id="@+id/red_initCode"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="20dp"
                        android:gravity="center"
                        android:text=""
                        android:text="请保持手持机和卡片不要移动"
                        android:textColor="#333333"
                        android:textSize="@dimen/new_card_size"
                        android:textStyle="bold" />