Browse Source

feat 升级对FCC3.0的支持(不再支持FCC2.0)

RobinTan1024 4 years ago
parent
commit
1c9cedb855

+ 2 - 0
.idea/dictionaries/11017950.xml

@@ -1,11 +1,13 @@
 <component name="ProjectDictionaryState">
   <dictionary name="11017950">
     <words>
+      <w>apitype</w>
       <w>bugly</w>
       <w>currentbu</w>
       <w>doverfuelingsolutions</w>
       <w>errcd</w>
       <w>gson</w>
+      <w>ifsf</w>
       <w>issp</w>
       <w>isspt</w>
       <w>leakcanary</w>

+ 87 - 56
app/src/main/java/com/doverfuelingsolutions/issp/api/FuelInfoApi.kt

@@ -2,22 +2,22 @@ package com.doverfuelingsolutions.issp.api
 
 import com.doverfuelingsolutions.issp.R
 import com.doverfuelingsolutions.issp.api.basic.RetrofitUtil
-import com.doverfuelingsolutions.issp.api.dto.DFSResult
-import com.doverfuelingsolutions.issp.api.dto.ResultAccumulationInfo
-import com.doverfuelingsolutions.issp.api.dto.ResultBarcode
-import com.doverfuelingsolutions.issp.api.dto.ResultPumpInfo
-import com.doverfuelingsolutions.issp.api.entity.DefaultParams
+import com.doverfuelingsolutions.issp.api.dto.*
 import com.doverfuelingsolutions.issp.api.entity.NozzleInfo
-import com.doverfuelingsolutions.issp.api.entity.ParametersBean
 import com.doverfuelingsolutions.issp.api.entity.PumpInfo
 import com.doverfuelingsolutions.issp.api.service.ServiceStation
 import com.doverfuelingsolutions.issp.utils.log.DFSLog
 import com.doverfuelingsolutions.issp.utils.sp.SPKeys
 import com.doverfuelingsolutions.issp.utils.sp.SPUtil
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
 import com.wayne.www.waynelib.fdc.message.DeviceClass
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.GlobalScope
 import kotlinx.coroutines.launch
+import okhttp3.MediaType
+import okhttp3.RequestBody
+import okhttp3.ResponseBody
 import retrofit2.Call
 import retrofit2.Callback
 import retrofit2.Response
@@ -33,40 +33,37 @@ object FuelInfoApi {
      */
     suspend fun requestPumpInfo() = suspendCoroutine<DFSResult<List<PumpInfo>>> {
         GlobalScope.launch(Dispatchers.IO) {
-            val resultPumpList = requestPumpList()
-            if (!resultPumpList.success || resultPumpList.data == null) {
-                it.resume(DFSResult.fail(resultPumpList.message))
-                return@launch
-            } else if (resultPumpList.data.isEmpty()) {
-                it.resume(DFSResult.success(emptyList()))
-                return@launch
-            }
+            val resultApi = getApiInfo(WayneApiConfig.Api_GetPumpsLayout)
+            if (resultApi.success && resultApi.data != null) {
+                val resultPumpList = requestPumpList(resultApi.data.Path)
+                if (!resultPumpList.success || resultPumpList.data == null) {
+                    it.resume(DFSResult.fail(resultPumpList.message))
+                    return@launch
+                } else if (resultPumpList.data.isEmpty()) {
+                    it.resume(DFSResult.success(emptyList()))
+                    return@launch
+                }
 
             val pumpList = arrayListOf<PumpInfo>()
             for (item in resultPumpList.data) {
-                if (item.Nozzles.isEmpty()) return@launch
-                // 当前油泵的油品名称
+                if (item.Nozzles.isEmpty()) continue
                 val firstNozzle = item.Nozzles[0]
-
-                val local = SPUtil.getBarcodeInfo(firstNozzle.ProductBarcode)
-                val name: String = if (local != null) {
-                    local.ItemName
-                } else {
-                    val resultBarcodeInfo = SystemApi.barcodeName(firstNozzle.ProductBarcode)
-                    if (resultBarcodeInfo.success && resultBarcodeInfo.data != null && resultBarcodeInfo.data.IsFuelItem) {
-                        SPUtil.setBarcodeInfo(firstNozzle.ProductBarcode, resultBarcodeInfo.data)
-                        resultBarcodeInfo.data.ItemName
-                    } else {
-                        ""
-                    }
-                }
-                val pump = PumpInfo(item.PumpId, name, item.Nozzles.map { resultNozzleInfo ->
-                    NozzleInfo(item.PumpId, resultNozzleInfo.LogicalId, resultNozzleInfo.SiteLevelNozzleId, resultNozzleInfo.ProductBarcode, name)
+                // FCC3.0 传递了油品名称,不需要像 FCC2.0 那样自行查询
+                val pump = PumpInfo(item.PumpId, firstNozzle.ProductName, item.Nozzles.map { resultNozzleInfo ->
+                    NozzleInfo(
+                        item.PumpId,
+                        resultNozzleInfo.LogicalId,
+                        resultNozzleInfo.SiteLevelNozzleId,
+                        resultNozzleInfo.ProductBarcode,
+                        firstNozzle.ProductName)
                 })
                 pumpList.add(pump)
             }
 
             it.resume(DFSResult.success(pumpList))
+            } else {
+                it.resume(DFSResult.fail(resultApi.message))
+            }
         }
     }
 
@@ -74,50 +71,84 @@ object FuelInfoApi {
      * 获取累计数信息
      */
     suspend fun accumulationInfo(dc: DeviceClass) = suspendCoroutine<DFSResult<ResultAccumulationInfo>> {
-        val bean = ParametersBean().apply {
-            Value = dc.releaseTokenElement
-        }
-        serviceStation.accumulationInfo(DefaultParams(listOf(bean))).enqueue(object : Callback<List<ResultAccumulationInfo>> {
-            override fun onResponse(call: Call<List<ResultAccumulationInfo>>, response: Response<List<ResultAccumulationInfo>>) {
-                val code = response.code()
-                val body = response.body()
-                if (code == 200 && body != null && body.isNotEmpty()) {
-                    it.resume(DFSResult.success(body[0]))
-                    return
-                }
-
-                it.resume(DFSResult.fail(R.string.fail_get_accumulation))
-            }
+        GlobalScope.launch(Dispatchers.IO) {
+            val resultApi = getApiInfo(WayneApiConfig.Api_AccumulationInfo)
+            if (resultApi.success && resultApi.data != null) {
+                val params = RequestBody.create(MediaType.parse("application/json"), "{\"Parameters\":[{\"Name\":\"releasetoken\",\"Value\":\"${dc.releaseTokenElement}\"}]}")
+                serviceStation.postBody(resultApi.data.Path, params).enqueue(object : Callback<ResponseBody> {
+                    override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
+                        val code = response.code()
+                        val body = response.body()?.string()
+                        if (code == 200 && body != null) {
+                            val obj = Gson().fromJson<Array<ResultAccumulationInfo>>(body, object : TypeToken<Array<ResultAccumulationInfo>>() {}.type)
+                            it.resume(DFSResult.success(obj[0]))
+                            return
+                        }
+                        it.resume(DFSResult.fail(R.string.return_data_error))
+                    }
 
-            override fun onFailure(call: Call<List<ResultAccumulationInfo>>, t: Throwable) {
-                DFSLog.e("FuelInfoApi.accumulationInfo.onFailure", t)
-                it.resume(DFSResult.fail(R.string.fail_get_accumulation))
+                    override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
+                        DFSLog.e(t)
+                        it.resume(DFSResult.fail(t.message.toString()))
+                    }
+                })
+            } else {
+                it.resume(DFSResult.fail(resultApi.message))
             }
-        })
+        }
     }
 
     /**
      * 获取泵+枪信息
      */
-    private suspend fun requestPumpList() = suspendCoroutine<DFSResult<Array<ResultPumpInfo>>> {
-        serviceStation.getNozzleInfo().enqueue(object : Callback<Array<ResultPumpInfo>> {
-            override fun onResponse(call: Call<Array<ResultPumpInfo>>, response: Response<Array<ResultPumpInfo>>) {
+    private suspend fun requestPumpList(url: String) = suspendCoroutine<DFSResult<Array<ResultPumpInfo>>> {
+        val params = RequestBody.create(MediaType.parse("application/json"), "{\"Parameters\":[]}")
+        serviceStation.postBody(url, params).enqueue(object : Callback<ResponseBody> {
+            override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
                 val code = response.code()
-                val body = response.body()
+                val body = response.body()?.string()
                 if (code == 200 && body != null) {
-                    it.resume(DFSResult.success(body))
+                    val obj = Gson().fromJson<Array<ResultPumpInfo>>(body, (object : TypeToken<Array<ResultPumpInfo>>() {}).type)
+                    it.resume(DFSResult.success(obj))
                     return
                 }
                 it.resume(DFSResult.fail(R.string.return_data_error))
             }
 
-            override fun onFailure(call: Call<Array<ResultPumpInfo>>, t: Throwable) {
-                DFSLog.e("FuelInfoApi.getNozzleData.onFailure", t)
+            override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
+                DFSLog.e("FuelInfoApi.requestPumpList.onFailure", t)
                 it.resume(DFSResult.fail(t.message.toString()))
             }
         })
     }
 
+    /* Wayne FCC3.0 后,不能使用静态 Url 请求接口,必须查询得到动态 Url 后调用 */
+    private val apiInfoListType = (object : TypeToken<List<ResultApiInfo>>() {}).type
+    private suspend fun getApiInfo(type: String) = suspendCoroutine<DFSResult<ResultApiInfo>> {
+        val params = RequestBody.create(MediaType.parse("application/json"), "[\"webApi\", [\"IfsfFdcServer\"]]")
+        serviceStation.postBody(WayneApiConfig.URL_SERVICE_DISCOVER, params)
+            .enqueue(object : Callback<ResponseBody> {
+                override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
+                    val code = response.code()
+                    val body = response.body()?.string()
+                    if (code == 200 && body != null) {
+                        val obj = Gson().fromJson<List<ResultApiInfo>>(body, apiInfoListType)
+                        val api = obj.find { item -> item.ApiName.equals(type, true) }
+                        if (api != null) {
+                            it.resume(DFSResult.success(api))
+                            return
+                        }
+                    }
+                    it.resume(DFSResult.fail(R.string.return_data_error))
+                }
+
+                override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
+                    DFSLog.e("FuelInfoApi.getApiInfo.onFailure", t)
+                    it.resume(DFSResult.fail(t.message.toString()))
+                }
+            })
+    }
+
     private fun serviceMaker(): ServiceStation {
         val ip = SPUtil.getString(SPKeys.FUEL_IP)
         val port = SPUtil.getString(SPKeys.FUEL_PORT)

+ 2 - 2
app/src/main/java/com/doverfuelingsolutions/issp/api/SystemApi.kt

@@ -92,10 +92,10 @@ object SystemApi {
     /**
      * 向服务器备案即将进行支付的订单
      */
-    suspend fun generateOrder(dc: DeviceClass) = suspendCoroutine<DFSResult<PosTrx>> {
+    suspend fun generateOrder(dc: DeviceClass, posItemUniqueId: String) = suspendCoroutine<DFSResult<PosTrx>> {
         val clientPosTrx = ClientPosTrx(PosTrxType.Sale)
         val clientRingUpPosItem = ClientRingUpPosItem()
-        SPUtil.getBarcodeInfo(dc.productNo1.toInt())?.let { info -> clientRingUpPosItem.posItemUniqueId = info.Id }
+        clientRingUpPosItem.posItemUniqueId = posItemUniqueId
         clientRingUpPosItem.setFuelItemSoldOnPumpId(dc.pumpNo)
         clientRingUpPosItem.setFuelItemClientSidePrice(dc.unitPrice)
         clientRingUpPosItem.fuelItemFdcTransactionSeqNo = dc.transactionSeqNo

+ 7 - 0
app/src/main/java/com/doverfuelingsolutions/issp/api/WayneApiConfig.kt

@@ -40,6 +40,13 @@ class WayneApiConfig {
         const val FUEL_IP_DEFAULT = "192.168.1.80"
         const val FUEL_PORT_DEFAULT = "8384"
 
+        // 服务发现的 APIName
+        const val Api_GetPumpsLayout = "GetPumpsLayout"
+        const val Api_AccumulationInfo = "GetFuelSaleTrxDetailsAsync"
+
+        // URL
+        const val URL_SERVICE_DISCOVER = "u/?apitype=service&an=ShowMeApi&pn=ProcessorsDispatcher&en=Edge.Core.Processor.Dispatcher.DefaultDispatcher"
+
         // 实时值
         val HOST_BASE: String get() {
             val domain = SPUtil.getString(SPKeys.SERVER_DOMAIN)

+ 9 - 9
app/src/main/java/com/doverfuelingsolutions/issp/api/basic/LoggingInterceptor.kt

@@ -10,19 +10,19 @@ class LoggingInterceptor : Interceptor {
         val request = chain.request()
         val time = System.currentTimeMillis()
 
-        val requestName = request.header("requestName")
-        val requestBuilder = request.newBuilder()
-        requestBuilder.removeHeader("requestName")
-        val newRequest = requestBuilder.build()
+//        val requestBuilder = request.newBuilder()
+//        requestBuilder.removeHeader("requestName")
+//        val newRequest = requestBuilder.build()
 
-        DFSLog.v("=====>>>>> request[$requestName]: ${newRequest.method()} ${newRequest.url()}")
-        val response = chain.proceed(newRequest)
+        val response = chain.proceed(request)
         val body = response.body()
         val stringBody = if (body == null) "null" else response.peekBody(Long.MAX_VALUE).string()
         DFSLog.v(
-            "=====>>>>> request[$requestName] done: ${System.currentTimeMillis() - time}ms",
-            "code = ${response.code()}",
-            "body = $stringBody"
+            "=====>>>>> request done in ${System.currentTimeMillis() - time}ms",
+            "method: ${request.method()}",
+            "url: ${request.url()}",
+            "code: ${response.code()}",
+            "body: $stringBody"
         )
         return response
     }

+ 6 - 0
app/src/main/java/com/doverfuelingsolutions/issp/api/dto/ResultApiInfo.kt

@@ -0,0 +1,6 @@
+package com.doverfuelingsolutions.issp.api.dto
+
+data class ResultApiInfo(
+    val ApiName: String,
+    val Path: String,
+)

+ 1 - 0
app/src/main/java/com/doverfuelingsolutions/issp/api/dto/ResultNozzleInfo.kt

@@ -4,4 +4,5 @@ data class ResultNozzleInfo(
     val LogicalId: Int,
     val SiteLevelNozzleId: Int,
     val ProductBarcode: Int,
+    val ProductName: String,
 )

+ 1 - 2
app/src/main/java/com/doverfuelingsolutions/issp/api/entity/PumpInfo.kt

@@ -2,8 +2,7 @@ package com.doverfuelingsolutions.issp.api.entity
 
 data class PumpInfo(
     val pumpId: Int,
-    val oilName: String,
+    val productName: String,
     val nozzleList: List<NozzleInfo>,
 ) {
-    constructor(nozzle: NozzleInfo) : this(nozzle.pumpId, nozzle.barcodeName, listOf(nozzle))
 }

+ 11 - 0
app/src/main/java/com/doverfuelingsolutions/issp/api/service/ServiceStation.kt

@@ -1,15 +1,26 @@
 package com.doverfuelingsolutions.issp.api.service
 
 import com.doverfuelingsolutions.issp.api.dto.ResultAccumulationInfo
+import com.doverfuelingsolutions.issp.api.dto.ResultApiInfo
 import com.doverfuelingsolutions.issp.api.dto.ResultPumpInfo
 import com.doverfuelingsolutions.issp.api.entity.DefaultParams
+import okhttp3.RequestBody
+import okhttp3.ResponseBody
 import retrofit2.Call
 import retrofit2.http.Body
 import retrofit2.http.Headers
 import retrofit2.http.POST
+import retrofit2.http.Url
 
 interface ServiceStation {
 
+    @POST
+    fun postBody(@Url url: String, @Body params: RequestBody) : Call<ResponseBody>
+
+    @Headers("requestName: getApiInfo", "Content-Type: application/json")
+    @POST("u/?apitype=service&an=ShowMeApi&pn=ProcessorsDispatcher&en=Edge.Core.Processor.Dispatcher.DefaultDispatcher")
+    fun getApiInfo(@Body params: RequestBody = RequestBody.create(okhttp3.MediaType.parse("application/json"), "[\"webApi\", [\"IfsfFdcServer\"]]")): Call<Array<ResultApiInfo>>
+
     @Headers("requestName: getNozzleInfo")
     @POST("u/?mn=GetPumpsLayout&pn=fdcServerApp&en=Applications.FDC.FdcServerHostApp")
     fun getNozzleInfo(@Body params: DefaultParams = DefaultParams(emptyList())): Call<Array<ResultPumpInfo>>

+ 32 - 32
app/src/main/java/com/doverfuelingsolutions/issp/fusion/FusionManager.kt

@@ -195,40 +195,40 @@ object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
      * @param pumpId -1 means all pumps, others mean specified pump.
      */
     suspend fun getAllUnsettledOrder(pumpId: Int = -1) = suspendCoroutine<DFSResult<List<DeviceClass>>> {
-            ThreadUtil.io {
-                val time1 = System.currentTimeMillis()
-                FdcClient.getDefault().sendGetAvailableFuelSaleTrxs(pumpId, { _, response ->
-                    val time2 = System.currentTimeMillis()
-                    DFSLog.v(
-                        "fetch uncleared orders of pump $pumpId (cost ${time2 - time1}ms)",
-                        response
-                    )
-                    coroutineIO.launch {
-                        if (response == null || response !is ServiceResponseGetAvailableFuelSaleTrxs) {
-                            it.resume(DFSResult.fail(R.string.fail_get_order))
-                        } else if (response.singleFdcData == null || response.singleDeviceClass == null) {
-                            it.resume(DFSResult.success(emptyList()))
-                        } else {
-                            val unsettledList =
-                                response.singleFdcData.deviceClasses.filter { !it.isClear }
-                            val resultDetailList = unsettledList
-                                .map { dc -> async { getOrderDetail(dc) } }
-                                .map { deferred -> deferred.await() }
-                                .filter { it.success }
-                                .mapNotNull {
-                                    it.data?.productName =
-                                        pumpList.find { pump -> pump.pumpId == it.data?.pumpNo }?.oilName
-                                            ?: ""
-                                    it.data
-                                }
-                            val time3 = System.currentTimeMillis()
-                            DFSLog.v("fetch uncleared orders detail cost ${time3 - time2}ms")
-                            it.resume(DFSResult.success(resultDetailList))
-                        }
+        ThreadUtil.io {
+            val time1 = System.currentTimeMillis()
+            FdcClient.getDefault().sendGetAvailableFuelSaleTrxs(pumpId, { _, response ->
+                val time2 = System.currentTimeMillis()
+                DFSLog.v(
+                    "fetch uncleared orders of pump $pumpId (cost ${time2 - time1}ms)",
+                    response
+                )
+                coroutineIO.launch {
+                    if (response == null || response !is ServiceResponseGetAvailableFuelSaleTrxs) {
+                        it.resume(DFSResult.fail(R.string.fail_get_order))
+                    } else if (response.singleFdcData == null || response.singleDeviceClass == null) {
+                        it.resume(DFSResult.success(emptyList()))
+                    } else {
+                        val unsettledList =
+                            response.singleFdcData.deviceClasses.filter { !it.isClear }
+                        val resultDetailList = unsettledList
+                            .map { dc -> async { getOrderDetail(dc) } }
+                            .map { deferred -> deferred.await() }
+                            .filter { it.success }
+                            .mapNotNull {
+                                it.data?.productName =
+                                    pumpList.find { pump -> pump.pumpId == it.data?.pumpNo }?.productName
+                                        ?: ""
+                                it.data
+                            }
+                        val time3 = System.currentTimeMillis()
+                        DFSLog.v("fetch uncleared orders detail cost ${time3 - time2}ms")
+                        it.resume(DFSResult.success(resultDetailList))
                     }
-                }, 8000)
-            }
+                }
+            }, 8000)
         }
+    }
 
     /**
      * Fetch order detail.

+ 1 - 1
app/src/main/java/com/doverfuelingsolutions/issp/view/fragment/FragmentOrderList.kt

@@ -43,7 +43,7 @@ class FragmentOrderList private constructor(private val pumpId: Int) : FragmentB
     private var selectedPosition: Int? = null
 
     private var selectedName = ""
-    private val oilNameList = FusionManager.pumpList.map { it.oilName }.filter { it.isNotBlank() }
+    private val oilNameList = FusionManager.pumpList.map { it.productName }.filter { it.isNotBlank() }
     private val chooseListAdapter = ChooseListAdapter(oilNameList, this::selectOilName, true)
 
     override fun onCreate(savedInstanceState: Bundle?) {

+ 12 - 2
app/src/main/java/com/doverfuelingsolutions/issp/view/fragment/FragmentPayType.kt

@@ -7,8 +7,8 @@ import android.view.ViewGroup
 import androidx.databinding.DataBindingUtil
 import androidx.lifecycle.lifecycleScope
 import com.doverfuelingsolutions.issp.R
-import com.doverfuelingsolutions.issp.api.SystemApi
 import com.doverfuelingsolutions.issp.api.FuelInfoApi
+import com.doverfuelingsolutions.issp.api.SystemApi
 import com.doverfuelingsolutions.issp.databinding.FragmentPayTypeBinding
 import com.doverfuelingsolutions.issp.fusion.FusionManager
 import com.doverfuelingsolutions.issp.utils.DFSToastUtil
@@ -54,6 +54,7 @@ class FragmentPayType private constructor(private val dc: DeviceClass): Fragment
         (requireActivity() as MainActivity).fragmentRouter.stopFragmentToolbarTimer()
 
         lifecycleScope.launch {
+            // 累计数信息
             val resultAccumulationInfo = FuelInfoApi.accumulationInfo(dc)
             if (resultAccumulationInfo.success && resultAccumulationInfo.data != null) {
                 dc.amountTotalizer = BigDecimal(resultAccumulationInfo.data.AmountTotalizer)
@@ -66,10 +67,19 @@ class FragmentPayType private constructor(private val dc: DeviceClass): Fragment
                 return@launch
             }
 
+            // 查询最新的油品ID(非BarcodeId)
+            val resultBarcodeInfo = SystemApi.barcodeName(dc.productNo1.toInt())
+            if (!resultBarcodeInfo.success || resultBarcodeInfo.data == null) {
+                dialog.dismiss()
+                // (requireActivity() as MainActivity).fragmentRouter.resumeFragmentToolbarTimer()
+                DFSToastUtil.fail("查询油品posItemUniqueId失败")
+                return@launch
+            }
+
             // 获取云订单(本地需要缓存,避免重复生成订单)
             var posTrx: PosTrx? = SPUtil.findWayneOrder(dc)
             if (posTrx == null) {
-                val resultReportPayment = SystemApi.generateOrder(dc)
+                val resultReportPayment = SystemApi.generateOrder(dc, resultBarcodeInfo.data.Id)
                 if (resultReportPayment.success && resultReportPayment.data != null && !resultReportPayment.data.id.isNullOrEmpty()) {
                     posTrx = resultReportPayment.data
                     SPUtil.addWayneOrder(dc, posTrx)