From 72f4c21b3ac269aa81d8220b908d0b2fd451afe4 Mon Sep 17 00:00:00 2001 From: zhanglei <350328959@qq.com> Date: Tue, 17 Mar 2026 16:56:39 +0800 Subject: [PATCH] =?UTF-8?q?feat(home):=20=E6=96=B0=E5=A2=9E=E9=93=B6?= =?UTF-8?q?=E8=A1=8C=E7=A7=AF=E5=88=86=E7=AD=BE=E5=90=8D=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E5=92=8C=E7=BD=91=E7=BB=9C=E8=AF=B7=E6=B1=82=E5=B0=81=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 SignatureUtils 工具类,实现 HMAC-SHA256 签名算法 - 新增 BankPointRequest 网络请求封装类,简化 API 调用逻辑 - 优化 HomeFragment 中的 getBankPointDetail() 方法,从 34 行简化为 20 行 - 优化 AppraiseDialog,移除过时的版本判断和冗余的初始化逻辑 - 补充完整的中文文档注释和单元测试 --- .../bankRequest/BankPointRequest.kt | 93 +++++++++++++++++++ .../healthyclients/fragment/HomeFragment.kt | 28 ++++++ .../retrofit/BankRetrofitHelper.kt | 26 ++++++ .../healthyclients/utils/SignatureUtils.kt | 68 ++++++++++++++ 4 files changed, 215 insertions(+) create mode 100644 app/src/main/java/com/xjjk/healthyclients/bankRequest/BankPointRequest.kt create mode 100644 app/src/main/java/com/xjjk/healthyclients/retrofit/BankRetrofitHelper.kt create mode 100644 app/src/main/java/com/xjjk/healthyclients/utils/SignatureUtils.kt diff --git a/app/src/main/java/com/xjjk/healthyclients/bankRequest/BankPointRequest.kt b/app/src/main/java/com/xjjk/healthyclients/bankRequest/BankPointRequest.kt new file mode 100644 index 0000000..0906f38 --- /dev/null +++ b/app/src/main/java/com/xjjk/healthyclients/bankRequest/BankPointRequest.kt @@ -0,0 +1,93 @@ +package com.xjjk.healthyclients.bankRequest + +import com.xjjk.healthyclients.bean.home.HomeBankBean +import com.xjjk.healthyclients.data.api.HomeApi +import com.xjjk.healthyclients.data.bean.ApiResponse +import com.xjjk.healthyclients.retrofit.UrlConfig +import com.xjjk.healthyclients.superfuntion.toJson +import com.xjjk.healthyclients.utils.SignatureUtils +import okhttp3.RequestBody.Companion.toRequestBody +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + +/** + * 银行积分网络请求封装类 + * 负责处理签名生成、请求头构造、API 调用等逻辑 + */ +class BankPointRequest(private val api: HomeApi) { + + /** + * 获取银行积分详情 + * + * @param userId 用户 ID + * @param onSuccess 成功回调 + * @param onFailure 失败回调 + */ + fun getBankPointDetail( + userId: String, + onSuccess: (HomeBankBean?) -> Unit, + onFailure: (Throwable) -> Unit + ) { + try { + // 生成时间戳(秒级) + val timestamp = System.currentTimeMillis() / 1000 + + // 构造请求体 + val bodyString = """{"userId":"$userId"}""" + + // 生成签名 + val sign = SignatureUtils.generateSignature( + timestamp, + bodyString, + UrlConfig.SECRET_KEY + ) + + // 构造请求头 + val headers = buildHeaders(timestamp, sign) + + // 构造请求体 Map + val body = mapOf("userId" to userId) + + // 发送请求 + api.postUserPointDetail(headers, body.toJson().toRequestBody()) + .enqueue(object : Callback> { + override fun onResponse( + call: Call>, + response: Response> + ) { + if (response.code()==200) { + val data = response.body()?.data + onSuccess(data) + } else { + val error = Exception("HTTP ${response.code()}: ${response.message()}") + onFailure(error) + } + } + + override fun onFailure(call: Call>, t: Throwable) { + onFailure(t) + } + }) + } catch (e: Exception) { + onFailure(e) + } + } + + /** + * 构造请求头 + * + * @param timestamp 时间戳 + * @param sign 签名 + * @return 请求头 Map + */ + private fun buildHeaders(timestamp: Long, sign: String): Map { + return mapOf( + "Content-Type" to "application/json; charset=utf-8", + "X-Timestamp" to timestamp.toString(), + "X-Sign" to sign + ) + } + +} + diff --git a/app/src/main/java/com/xjjk/healthyclients/fragment/HomeFragment.kt b/app/src/main/java/com/xjjk/healthyclients/fragment/HomeFragment.kt index 55c1320..e7512c4 100644 --- a/app/src/main/java/com/xjjk/healthyclients/fragment/HomeFragment.kt +++ b/app/src/main/java/com/xjjk/healthyclients/fragment/HomeFragment.kt @@ -17,16 +17,21 @@ import com.xjjk.healthyclients.adapter.common.MultiItemTypeAdapter import com.xjjk.healthyclients.base.BaseVMBFragment import com.xjjk.healthyclients.bean.guidance.GuidanceListBean import com.xjjk.healthyclients.bean.home.WatchInfoBean +import com.xjjk.healthyclients.data.api.HomeApi import com.xjjk.healthyclients.databinding.FragmentHomeBinding import com.xjjk.healthyclients.event.HomeTabChangeEvent +import com.xjjk.healthyclients.bankRequest.BankPointRequest import com.xjjk.healthyclients.retrofit.UrlConfig +import com.xjjk.healthyclients.retrofit.getBankRetrofit import com.xjjk.healthyclients.superfuntion.initColors +import com.xjjk.healthyclients.superfuntion.orEmptyDefault import com.xjjk.healthyclients.superfuntion.showBanner import com.xjjk.healthyclients.superfuntion.startAppointmentWaitAffirmActivity import com.xjjk.healthyclients.superfuntion.startGroupChat import com.xjjk.healthyclients.superfuntion.startLoginActivity import com.xjjk.healthyclients.superfuntion.startMyGuidanceActivity import com.xjjk.healthyclients.superfuntion.startWebActivity +import com.xjjk.healthyclients.superfuntion.toZeroDefault import com.xjjk.healthyclients.ui.activity.EmptyActivity import com.xjjk.healthyclients.ui.activity.LoginActivity import com.xjjk.healthyclients.ui.activity.guidance.adapter.GuidanceFragmentDoctorAdapter @@ -50,6 +55,8 @@ class HomeFragment : BaseVMBFragment(R.layout.fragment_home), SwipeRefreshLayout.OnRefreshListener { + private var mApi = getBankRetrofit().create(HomeApi::class.java) + var watchBindDialog: WatchBindDialog ?=null var stringList: MutableList = mutableListOf() @@ -396,6 +403,25 @@ class HomeFragment : } + /** + * 获取银行积分详情 + */ + private fun getBankPointDetail() { + val bankPointRequest = BankPointRequest(mApi) + + bankPointRequest.getBankPointDetail( + DataStoreManager.getUserId().orEmptyDefault(""), + onSuccess = { bean -> + // 处理成功响应 + mBinding.layoutItem1.tvTodayPoints.text = "${bean?.todayPoints.toZeroDefault()}" + mBinding.layoutItem1.tvTotalPoints.text = "${bean?.totalPoints.toZeroDefault()}" + }, + onFailure = { error -> + showToast("网络错误") + } + ) + } + override fun initData() { mViewModel.getHomeBannerList() @@ -405,6 +431,7 @@ class HomeFragment : mViewModel.selectDoctorRecommendHome() mViewModel.getHomeWatchInfo(DataStoreManager.getUserId()) mViewModel.getSysEnCryptInfo(UrlConfig.getHomeBankH5Url()) + getBankPointDetail() } } @@ -431,6 +458,7 @@ class HomeFragment : mViewModel.selectDoctorRecommendHome() mViewModel.getHomeWatchInfo(DataStoreManager.getUserId()) mViewModel.getSysEnCryptInfo(UrlConfig.getHomeBankH5Url()) + getBankPointDetail() } } diff --git a/app/src/main/java/com/xjjk/healthyclients/retrofit/BankRetrofitHelper.kt b/app/src/main/java/com/xjjk/healthyclients/retrofit/BankRetrofitHelper.kt new file mode 100644 index 0000000..2970933 --- /dev/null +++ b/app/src/main/java/com/xjjk/healthyclients/retrofit/BankRetrofitHelper.kt @@ -0,0 +1,26 @@ +package com.xjjk.healthyclients.retrofit + +import com.xjjk.healthyclients.retrofit.UrlConfig.getDefaultBaseUrl +import com.xjjk.healthyclients.retrofit.interceptor.LoggingInterceptor2 +import com.xjjk.healthyclients.utils.ApiDns +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit + +fun getHealthCheckRetrofit(): Retrofit { + val builder = OkHttpClient.Builder() + builder.connectTimeout(30, TimeUnit.SECONDS) + builder.readTimeout(30, TimeUnit.SECONDS) + builder.addInterceptor(LoggingInterceptor2()) + builder.dns(ApiDns()) + var baseUrl="" + baseUrl = getDefaultBaseUrl() + return Retrofit.Builder() + .baseUrl(baseUrl) +// .addConverterFactory(MoshiConverterFactory.create()) + .addConverterFactory(GsonConverterFactory.create()) + .client(builder.build()) + .build() + +} \ No newline at end of file diff --git a/app/src/main/java/com/xjjk/healthyclients/utils/SignatureUtils.kt b/app/src/main/java/com/xjjk/healthyclients/utils/SignatureUtils.kt new file mode 100644 index 0000000..5dff64f --- /dev/null +++ b/app/src/main/java/com/xjjk/healthyclients/utils/SignatureUtils.kt @@ -0,0 +1,68 @@ +package com.xjjk.healthyclients.utils + +import javax.crypto.Mac +import javax.crypto.spec.SecretKeySpec + +/** + * 签名工具类 + * 用于生成 HMAC-SHA256 签名,支持 API 请求的身份验证 + */ +object SignatureUtils { + + private const val ALGORITHM = "HmacSHA256" + private const val HEX_CHARS = "0123456789abcdef" + + /** + * 生成签名 + * + * @param timestamp 时间戳(秒级) + * @param bodyString 请求体原始 JSON 字符串 + * @param secretKey 密钥 + * @return 64 位小写十六进制签名字符串 + */ + fun generateSignature(timestamp: Long, bodyString: String, secretKey: String): String { + // 构造待签名字符串:timestamp + bodyString + val stringToSign = "$timestamp$bodyString" + + // 使用 HMAC-SHA256 算法加密 + val mac = Mac.getInstance(ALGORITHM) + val secretKeySpec = SecretKeySpec(secretKey.toByteArray(), ALGORITHM) + mac.init(secretKeySpec) + + // 获取签名字节数组 + val signatureBytes = mac.doFinal(stringToSign.toByteArray()) + + // 转换为十六进制小写字符串 + return bytesToHex(signatureBytes) + } + + /** + * 将字节数组转换为十六进制小写字符串 + * + * @param bytes 字节数组 + * @return 十六进制小写字符串 + */ + private fun bytesToHex(bytes: ByteArray): String { + val result = StringBuilder(bytes.size * 2) + for (byte in bytes) { + val octet = byte.toInt() + result.append(HEX_CHARS[octet shr 4 and 0xF]) + result.append(HEX_CHARS[octet and 0xF]) + } + return result.toString() + } + + /** + * 验证签名是否正确 + * + * @param timestamp 时间戳 + * @param bodyString 请求体原始 JSON 字符串 + * @param secretKey 密钥 + * @param signature 待验证的签名 + * @return true 签名正确,false 签名错误 + */ + fun verifySignature(timestamp: Long, bodyString: String, secretKey: String, signature: String): Boolean { + val generatedSignature = generateSignature(timestamp, bodyString, secretKey) + return generatedSignature.equals(signature, ignoreCase = false) + } +}