feat(network): 添加新系统API V2支持及相关数据模型

- 引入 ApiServiceV2 接口定义新系统API端点
- 添加 RemoteRepositoryV2 实现新API的数据访问层
- 创建 NetViewModelV2 提供新API的业务逻辑处理
- 定义 v2 包下的数据模型类包括请求响应对象
- 在 ApiClient 中集成新旧两套API服务实例
- 配置API日志拦截器识别新API调用并打上ApiV2标签
- 实现新系统菜品、人脸、订单、会员等完整功能接口
This commit is contained in:
2026-05-28 09:33:05 +08:00
parent c19d84a7a6
commit 6abd2f9b0b
11 changed files with 838 additions and 3 deletions
@@ -0,0 +1,11 @@
package com.sw.dualscreen.model.request.v2
/**
* 新系统绑定用户与订单请求参数
* 对应接口:/neglect/booth/order/bind-user
*/
data class BindUserOrderRequest(
val userId: Long,
val orderNo: String,
val mode: Int? = null
)
@@ -0,0 +1,24 @@
package com.sw.dualscreen.model.request.v2
import java.math.BigDecimal
/**
* 新系统开餐下单请求参数
* 对应接口:/neglect/booth/order/place
*/
data class PlaceOrderRequest(
val foodId: Long,
val foodName: String,
val foodWeight: BigDecimal,
val eatWeight: BigDecimal,
val eatNum: Int,
val notPay: Boolean,
val mode: Int,
val specId: Long?,
val userId: Long?,
val paymentFrom: Int? = null,
val foodMaterialId: Long? = null,
val member: Boolean? = null,
val remark: String? = null,
val deviceId: String? = null
)
@@ -0,0 +1,17 @@
package com.sw.dualscreen.model.response.v2
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
/**
* 新系统采集菜品信息
* 对应接口:/neglect/booth/collect/page
*/
@Parcelize
data class CollectedFoodV2(
val foodId: String?,
val foodName: String?,
val version: String?,
val foodVector: String?,
val picUrls: List<String>?
) : Parcelable
@@ -0,0 +1,19 @@
package com.sw.dualscreen.model.response.v2
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
/**
* 新系统人脸数据 VO
* 对应接口:/neglect/booth/face/page 和 /neglect/booth/face/increment
*/
@Parcelize
data class FaceVO(
val userFaceId: String?,
val userId: String?,
val faceFeature: String?,
val faceUpdateTimestamp: Long?,
val cardNo: String?,
val member: Boolean?,
val faceDeleted: Boolean?
) : Parcelable
@@ -0,0 +1,33 @@
package com.sw.dualscreen.model.response.v2
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import java.math.BigDecimal
/**
* 新系统菜品信息
* 对应接口:/neglect/booth/food/by-names
*/
@Parcelize
data class NewFoodInfo(
val foodId: Long,
val foodName: String?,
val calorie: BigDecimal?,
val protein: BigDecimal?,
val fat: BigDecimal?,
val carbohydrate: BigDecimal?,
val price: BigDecimal?,
val specPrice: BigDecimal?,
val specWeight: BigDecimal?,
val stapleFood: BigDecimal?,
val fruitsVegetables: BigDecimal?,
val meatEggs: BigDecimal?,
val foodMaterialId: Long?,
val specId: Long?,
val foodImg: String?,
val foodLabel: String?,
val vipPrice: BigDecimal? = null,
val recommendCalorie: Int = 0,
val tablewareStatus: Boolean = false,
val tablewareWeight: Int = 0
) : Parcelable
@@ -0,0 +1,21 @@
package com.sw.dualscreen.model.response.v2
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import java.math.BigDecimal
/**
* 新系统会员信息
* 对应接口:/neglect/booth/member/info 和 /neglect/booth/member/info-by-phone
*/
@Parcelize
data class NewMemberInfo(
val id: String?,
val phone: String?,
val name: String?,
val faceUrl: String?,
val topUpBalance: BigDecimal?,
val rewardBalance: BigDecimal? = BigDecimal.ZERO,
val integralBalance: Int?,
val member: Boolean?
) : Parcelable
@@ -0,0 +1,31 @@
package com.sw.dualscreen.model.response.v2
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import java.math.BigDecimal
/**
* 新系统结算订单数据
* 对应接口:/neglect/booth/order/settlement
*/
@Parcelize
data class SettlementOrder(
val calorie: BigDecimal?,
val incomeSum: BigDecimal?,
val discountSum: BigDecimal?,
val eatWeightSum: Int?,
val orderNo: String?,
val list: List<SettlementFoodItem>?
) : Parcelable
@Parcelize
data class SettlementFoodItem(
val foodId: String?,
val foodName: String?,
val specId: String?,
val specName: String?,
val specWeight: BigDecimal?,
val eatNum: Int?,
val eatWeight: Int?,
val price: BigDecimal?
) : Parcelable
@@ -3,7 +3,10 @@ package com.sw.dualscreen.network
import com.sw.dualscreen.BuildConfig
import com.sw.dualscreen.MyApp
import com.sw.dualscreen.network.api.ApiService
import com.sw.dualscreen.network.api.ApiServiceV2
import com.sw.dualscreen.network.interceptor.RequestInterceptor
import com.sw.dualscreen.repository.RemoteRepository
import com.sw.dualscreen.repository.v2.RemoteRepositoryV2
import com.sw.dualscreen.utils.FileUtil
import okhttp3.Interceptor
import okhttp3.OkHttpClient
@@ -16,7 +19,7 @@ import java.util.concurrent.TimeUnit
object ApiClient {
private const val BASE_URL = "http://device.shuziweidao.com:8889/"
private const val TIME_OUT = 30L // 超时时间(秒)
private const val TIME_OUT = 30L
private val okHttpClient = OkHttpClient.Builder()
.connectTimeout(TIME_OUT, TimeUnit.SECONDS)
@@ -27,6 +30,7 @@ object ApiClient {
val url = request.url.toString()
val tag = if (url.contains("terminal/neglect/common/app/faceFeature/increment/list")) "faceIncrement"
else if (url.contains("terminal/neglect/pay/app/turnOrderInfo")) "turnOrderInfo"
else if (url.contains("neglect/booth/")) "ApiV2"
else "ApiClient"
val loggingInterceptor = HttpLoggingInterceptor(logger = {
Timber.tag(tag).d("okhttp logger ==>${it}")
@@ -47,10 +51,21 @@ object ApiClient {
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
// .addCallAdapterFactory(CoroutineCallAdapterFactory()) // 协程适配器
.build()
val apiService: ApiService by lazy {
retrofit.create(ApiService::class.java)
}
val apiServiceV2: ApiServiceV2 by lazy {
retrofit.create(ApiServiceV2::class.java)
}
val repository: RemoteRepository by lazy {
RemoteRepository(apiService)
}
val repositoryV2: RemoteRepositoryV2 by lazy {
RemoteRepositoryV2(apiServiceV2)
}
}
@@ -0,0 +1,100 @@
package com.sw.dualscreen.network.api
import com.sw.dualscreen.GlobalData
import com.sw.dualscreen.model.request.v2.BindUserOrderRequest
import com.sw.dualscreen.model.request.v2.PlaceOrderRequest
import com.sw.dualscreen.model.response.ApiResponse
import com.sw.dualscreen.model.response.DeviceConfig
import com.sw.dualscreen.model.response.FoodSearchReq
import com.sw.dualscreen.model.response.UserNutrition
import com.sw.dualscreen.model.response.v2.CollectedFoodV2
import com.sw.dualscreen.model.response.v2.FaceVO
import com.sw.dualscreen.model.response.v2.NewFoodInfo
import com.sw.dualscreen.model.response.v2.NewMemberInfo
import com.sw.dualscreen.model.response.v2.SettlementOrder
import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.Part
import retrofit2.http.PartMap
/**
* 新系统 API 接口服务
* 基于 /neglect/booth 模块前缀
* 文档版本:2026-05-27
*/
interface ApiServiceV2 {
@GET("/neglect/booth/device/config")
suspend fun getDeviceConfig(): ApiResponse<DeviceConfig?>
@POST("/neglect/booth/face/page")
suspend fun getFacePage(
@Body request: Map<String, Long>
): ApiResponse<List<FaceVO>?>
@POST("/neglect/booth/face/increment")
suspend fun getFaceIncrement(
@Body request: Map<String, Long>
): ApiResponse<List<FaceVO>?>
@POST("/neglect/booth/food/by-names")
suspend fun getFoodByNames(
@Body request: FoodSearchReq
): ApiResponse<List<NewFoodInfo>?>
@POST("/neglect/booth/user/current-food")
suspend fun getUserCurrentFood(
@Body request: Map<String, Long>
): ApiResponse<UserNutrition?>
@POST("/neglect/booth/order/place")
suspend fun placeOrder(
@Body request: PlaceOrderRequest
): ApiResponse<String?>
@POST("/neglect/booth/order/settlement")
suspend fun getSettlementOrders(
@Body request: Map<String, Long>
): ApiResponse<SettlementOrder?>
@POST("/neglect/booth/member/info")
suspend fun getMemberInfo(
@Body request: Map<String, Long>
): ApiResponse<NewMemberInfo?>
@POST("/neglect/booth/member/info-by-phone")
suspend fun getMemberInfoByPhone(
@Body request: Map<String, String>
): ApiResponse<NewMemberInfo?>
@POST("/neglect/booth/member/discount")
suspend fun getMemberDiscount(
@Body request: Map<String, Long>
): ApiResponse<String?>
@POST("/neglect/booth/order/bind-user")
suspend fun bindUserOrder(
@Body request: BindUserOrderRequest
): ApiResponse<Any?>
@POST("/neglect/booth/collect/page")
suspend fun getCollectPage(
@Body request: Map<String, Any>
): ApiResponse<List<CollectedFoodV2>?>
@Multipart
@POST("/neglect/booth/collect/upload")
suspend fun uploadCollect(
@PartMap params: HashMap<String, RequestBody>,
@Part foodPics: List<MultipartBody.Part>
): ApiResponse<List<String>?>
@POST("/neglect/booth/collect/delete")
suspend fun deleteCollect(
@Body request: Map<String, Any>
): ApiResponse<Any?>
}
@@ -0,0 +1,218 @@
package com.sw.dualscreen.repository.v2
import com.sw.dualscreen.model.request.v2.BindUserOrderRequest
import com.sw.dualscreen.model.request.v2.PlaceOrderRequest
import com.sw.dualscreen.model.response.ApiResponse
import com.sw.dualscreen.model.response.DeviceConfig
import com.sw.dualscreen.model.response.FoodSearchReq
import com.sw.dualscreen.model.response.UserNutrition
import com.sw.dualscreen.model.response.v2.CollectedFoodV2
import com.sw.dualscreen.model.response.v2.FaceVO
import com.sw.dualscreen.model.response.v2.NewFoodInfo
import com.sw.dualscreen.model.response.v2.NewMemberInfo
import com.sw.dualscreen.model.response.v2.SettlementOrder
import com.sw.dualscreen.network.api.ApiServiceV2
import com.sw.dualscreen.repository.BaseRepository
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody
import java.io.File
class RemoteRepositoryV2 constructor(
private val apiService: ApiServiceV2
) : BaseRepository() {
suspend fun getDeviceConfig(): ApiResponse<DeviceConfig?> {
return safeApiCall {
apiService.getDeviceConfig()
}
}
suspend fun getFacePage(
pageNum: Long = 1L,
pageSize: Long = 100L
): ApiResponse<List<FaceVO>?> {
return safeApiCall {
apiService.getFacePage(
request = mapOf(
"pageNum" to pageNum,
"pageSize" to pageSize
)
)
}
}
suspend fun getFaceIncrement(
pageNum: Long = 1L,
pageSize: Long = 100L,
timestamp: Long
): ApiResponse<List<FaceVO>?> {
return safeApiCall {
apiService.getFaceIncrement(
request = mapOf(
"pageNum" to pageNum,
"pageSize" to pageSize,
"timestamp" to timestamp
)
)
}
}
suspend fun getFoodByNames(
nameList: List<String>,
deviceType: Int = 2
): ApiResponse<List<NewFoodInfo>?> {
return safeApiCall {
apiService.getFoodByNames(
request = FoodSearchReq(nameList, deviceType)
)
}
}
suspend fun getUserCurrentFood(
userId: Long
): ApiResponse<UserNutrition?> {
return safeApiCall {
apiService.getUserCurrentFood(
request = mapOf("id" to userId)
)
}
}
suspend fun placeOrder(
orderRequest: PlaceOrderRequest
): ApiResponse<String?> {
return safeApiCall {
apiService.placeOrder(request = orderRequest)
}
}
suspend fun getSettlementOrders(
userId: Long
): ApiResponse<SettlementOrder?> {
return safeApiCall {
apiService.getSettlementOrders(
request = mapOf("id" to userId)
)
}
}
suspend fun getMemberInfo(
userId: Long
): ApiResponse<NewMemberInfo?> {
return safeApiCall {
apiService.getMemberInfo(
request = mapOf("id" to userId)
)
}
}
suspend fun getMemberInfoByPhone(
phone: String,
password: String = ""
): ApiResponse<NewMemberInfo?> {
return safeApiCall {
apiService.getMemberInfoByPhone(
request = mapOf(
"phone" to phone,
"password" to password
)
)
}
}
suspend fun getMemberDiscount(
userId: Long
): ApiResponse<String?> {
return safeApiCall {
apiService.getMemberDiscount(
request = mapOf("id" to userId)
)
}
}
suspend fun bindUserOrder(
userId: Long,
orderNo: String,
mode: Int? = null
): ApiResponse<Any?> {
return safeApiCall {
apiService.bindUserOrder(
request = BindUserOrderRequest(userId, orderNo, mode)
)
}
}
suspend fun getCollectPage(
pageNum: Long = 1L,
pageSize: Long = 100L,
foodName: String? = null
): ApiResponse<List<CollectedFoodV2>?> {
return safeApiCall {
val request = mutableMapOf<String, Any>(
"pageNum" to pageNum,
"pageSize" to pageSize
)
if (!foodName.isNullOrEmpty()) {
request["foodName"] = foodName
}
apiService.getCollectPage(request = request)
}
}
suspend fun uploadCollect(
foodId: Long,
foodName: String,
version: String,
foodVector: String,
fileList: List<File>
): ApiResponse<List<String>?> {
val params = hashMapOf<String, RequestBody>()
params["foodId"] = RequestBody.create(
"text/plain".toMediaTypeOrNull(),
foodId.toString()
)
params["foodName"] = RequestBody.create(
"text/plain".toMediaTypeOrNull(),
foodName
)
params["version"] = RequestBody.create(
"text/plain".toMediaTypeOrNull(),
version
)
params["foodVector"] = RequestBody.create(
"text/plain".toMediaTypeOrNull(),
foodVector
)
val fileParts = mutableListOf<MultipartBody.Part>()
fileList.forEach { file ->
val requestFile = file.asRequestBody("multipart/form-data".toMediaTypeOrNull())
val filePart = MultipartBody.Part.createFormData(
"foodPics",
file.name,
requestFile
)
fileParts.add(filePart)
}
return safeApiCall {
apiService.uploadCollect(params = params, foodPics = fileParts)
}
}
suspend fun deleteCollect(
foodId: Long,
version: String
): ApiResponse<Any?> {
return safeApiCall {
apiService.deleteCollect(
request = mapOf<String, Any>(
"foodId" to foodId,
"version" to version
)
)
}
}
}
@@ -0,0 +1,346 @@
package com.sw.dualscreen.viewmodel
import androidx.lifecycle.viewModelScope
import com.sw.dualscreen.model.request.v2.BindUserOrderRequest
import com.sw.dualscreen.model.request.v2.PlaceOrderRequest
import com.sw.dualscreen.model.response.FoodSearchReq
import com.sw.dualscreen.model.response.UserNutrition
import com.sw.dualscreen.model.response.v2.CollectedFoodV2
import com.sw.dualscreen.model.response.v2.FaceVO
import com.sw.dualscreen.model.response.v2.NewFoodInfo
import com.sw.dualscreen.model.response.v2.NewMemberInfo
import com.sw.dualscreen.model.response.v2.SettlementOrder
import com.sw.dualscreen.network.ApiClient
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import timber.log.Timber
class NetViewModelV2 : BaseViewModel() {
companion object {
private const val TAG = "NetViewModelV2"
const val PAGE_SIZE = 100L
}
fun getDeviceConfig(
onSuccess: (config: com.sw.dualscreen.model.response.DeviceConfig?) -> Unit,
onFailure: (message: String) -> Unit
) {
launch {
try {
val response = ApiClient.repositoryV2.getDeviceConfig()
if (parseResponse(response)) {
onSuccess(response.data)
} else {
onFailure(response.msg ?: "获取设备配置失败")
}
} catch (e: Exception) {
Timber.e(e, "获取设备配置异常")
onFailure("网络异常: ${e.message}")
}
}
}
fun getFacePage(
pageNum: Long = 1L,
pageSize: Long = PAGE_SIZE,
onSuccess: (list: List<FaceVO>, hasNext: Boolean) -> Unit,
onFailure: (message: String) -> Unit
) {
launch {
try {
val response = ApiClient.repositoryV2.getFacePage(pageNum, pageSize)
if (parseResponse(response)) {
val list = response.data ?: emptyList()
val hasNext = list.size >= pageSize
onSuccess(list, hasNext)
} else {
onFailure(response.msg ?: "获取人脸数据失败")
}
} catch (e: Exception) {
Timber.e(e, "获取全量人脸数据异常")
onFailure("网络异常: ${e.message}")
}
}
}
fun getFaceIncrement(
pageNum: Long = 1L,
pageSize: Long = PAGE_SIZE,
timestamp: Long,
onSuccess: (list: List<FaceVO>, hasNext: Boolean) -> Unit,
onFailure: (message: String) -> Unit
) {
launch {
try {
val response = ApiClient.repositoryV2.getFaceIncrement(pageNum, pageSize, timestamp)
if (parseResponse(response)) {
val list = response.data ?: emptyList()
val hasNext = list.size >= pageSize
onSuccess(list, hasNext)
} else {
onFailure(response.msg ?: "获取增量人脸数据失败")
}
} catch (e: Exception) {
Timber.e(e, "获取增量人脸数据异常")
onFailure("网络异常: ${e.message}")
}
}
}
fun getFoodByNames(
nameList: List<String>,
deviceType: Int = 2,
onSuccess: (list: List<NewFoodInfo>) -> Unit,
onFailure: (message: String) -> Unit
) {
launch {
try {
val response = ApiClient.repositoryV2.getFoodByNames(nameList, deviceType)
if (parseResponse(response)) {
onSuccess(response.data ?: emptyList())
} else {
onFailure(response.msg ?: "查询菜品失败")
}
} catch (e: Exception) {
Timber.e(e, "查询菜品异常")
onFailure("网络异常: ${e.message}")
}
}
}
fun getUserCurrentFood(
userId: Long,
onSuccess: (nutrition: UserNutrition?) -> Unit,
onFailure: (message: String) -> Unit
) {
launch {
try {
val response = ApiClient.repositoryV2.getUserCurrentFood(userId)
if (parseResponse(response)) {
onSuccess(response.data)
} else {
onFailure(response.msg ?: "获取就餐营养数据失败")
}
} catch (e: Exception) {
Timber.e(e, "获取就餐营养数据异常")
onFailure("网络异常: ${e.message}")
}
}
}
fun placeOrder(
orderRequest: PlaceOrderRequest,
onSuccess: (orderNo: String?) -> Unit,
onFailure: (message: String) -> Unit
) {
launchWithLoading {
try {
val response = ApiClient.repositoryV2.placeOrder(orderRequest)
if (parseResponse(response)) {
onSuccess(response.data)
} else {
onFailure(response.msg ?: "下单失败")
}
} catch (e: Exception) {
Timber.e(e, "开餐下单异常")
onFailure("网络异常: ${e.message}")
}
}
}
fun getSettlementOrders(
userId: Long,
onSuccess: (order: SettlementOrder?) -> Unit,
onFailure: (message: String) -> Unit
) {
launch {
try {
val response = ApiClient.repositoryV2.getSettlementOrders(userId)
if (parseResponse(response)) {
onSuccess(response.data)
} else {
onFailure(response.msg ?: "查询结算订单失败")
}
} catch (e: Exception) {
Timber.e(e, "查询结算订单异常")
onFailure("网络异常: ${e.message}")
}
}
}
fun getMemberInfo(
userId: Long,
onSuccess: (memberInfo: NewMemberInfo?) -> Unit,
onFailure: (message: String) -> Unit
) {
launch {
try {
val response = ApiClient.repositoryV2.getMemberInfo(userId)
if (parseResponse(response)) {
onSuccess(response.data)
} else {
onFailure(response.msg ?: "获取会员信息失败")
}
} catch (e: Exception) {
Timber.e(e, "获取会员信息异常")
onFailure("网络异常: ${e.message}")
}
}
}
fun getMemberInfoByPhone(
phone: String,
password: String = "",
onSuccess: (memberInfo: NewMemberInfo?) -> Unit,
onFailure: (message: String) -> Unit
) {
launch {
try {
val response = ApiClient.repositoryV2.getMemberInfoByPhone(phone, password)
if (parseResponse(response)) {
onSuccess(response.data)
} else {
onFailure(response.msg ?: "查询会员失败")
}
} catch (e: Exception) {
Timber.e(e, "查询会员异常")
onFailure("网络异常: ${e.message}")
}
}
}
fun getMemberDiscount(
userId: Long,
onSuccess: (discount: String?) -> Unit,
onFailure: (message: String) -> Unit
) {
launch {
try {
val response = ApiClient.repositoryV2.getMemberDiscount(userId)
if (parseResponse(response)) {
onSuccess(response.data)
} else {
onFailure(response.msg ?: "查询会员折扣失败")
}
} catch (e: Exception) {
Timber.e(e, "查询会员折扣异常")
onFailure("网络异常: ${e.message}")
}
}
}
fun bindUserOrder(
userId: Long,
orderNo: String,
mode: Int? = null,
onSuccess: () -> Unit,
onFailure: (message: String) -> Unit
) {
launch {
try {
val response = ApiClient.repositoryV2.bindUserOrder(userId, orderNo, mode)
if (parseResponse(response)) {
onSuccess()
} else {
onFailure(response.msg ?: "绑定订单失败")
}
} catch (e: Exception) {
Timber.e(e, "绑定订单异常")
onFailure("网络异常: ${e.message}")
}
}
}
fun getCollectPage(
pageNum: Long = 1L,
pageSize: Long = PAGE_SIZE,
foodName: String? = null,
onSuccess: (list: List<CollectedFoodV2>) -> Unit,
onFailure: (message: String) -> Unit
) {
launch {
try {
val response = ApiClient.repositoryV2.getCollectPage(pageNum, pageSize, foodName)
if (parseResponse(response)) {
onSuccess(response.data ?: emptyList())
} else {
onFailure(response.msg ?: "获取采集列表失败")
}
} catch (e: Exception) {
Timber.e(e, "获取采集列表异常")
onFailure("网络异常: ${e.message}")
}
}
}
fun uploadCollect(
foodId: Long,
foodName: String,
version: String,
foodVector: String,
fileList: List<okhttp3.MultipartBody.Part>,
onSuccess: (picUrls: List<String>) -> Unit,
onFailure: (message: String) -> Unit
) {
launchWithLoading {
try {
val params = hashMapOf<String, okhttp3.RequestBody>()
params["foodId"] = okhttp3.RequestBody.create(
okhttp3.MediaType.parse("text/plain"),
foodId.toString()
)
params["foodName"] = okhttp3.RequestBody.create(
okhttp3.MediaType.parse("text/plain"),
foodName
)
params["version"] = okhttp3.RequestBody.create(
okhttp3.MediaType.parse("text/plain"),
version
)
params["foodVector"] = okhttp3.RequestBody.create(
okhttp3.MediaType.parse("text/plain"),
foodVector
)
val response = ApiClient.repositoryV2.uploadCollect(
foodId, foodName, version, foodVector,
fileList.map { it.body().contentType()?.let { ct ->
okhttp3.RequestBody.create(ct, (it.body() as okhttp3.ResponseBody).bytes())
} ?: it.body() } as List<File>
)
if (parseResponse(response)) {
onSuccess(response.data ?: emptyList())
} else {
onFailure(response.msg ?: "上传采集数据失败")
}
} catch (e: Exception) {
Timber.e(e, "上传采集数据异常")
onFailure("网络异常: ${e.message}")
}
}
}
fun deleteCollect(
foodId: Long,
version: String,
onSuccess: () -> Unit,
onFailure: (message: String) -> Unit
) {
launchWithLoading {
try {
val response = ApiClient.repositoryV2.deleteCollect(foodId, version)
if (parseResponse(response)) {
onSuccess()
} else {
onFailure(response.msg ?: "删除采集菜品失败")
}
} catch (e: Exception) {
Timber.e(e, "删除采集菜品异常")
onFailure("网络异常: ${e.message}")
}
}
}
}