DoggyZhang 3 недель назад
Родитель
Сommit
8d45e0bb89
71 измененных файлов с 1378 добавлено и 202 удалено
  1. 12 11
      app/src/main/java/com/adealink/weparty/commonui/ext/ViewExt.kt
  2. 15 0
      app/src/main/java/com/adealink/weparty/debug/DebugActivity.kt
  3. 3 0
      app/src/main/java/com/adealink/weparty/debug/DebugPrefs.kt
  4. 7 0
      app/src/main/java/com/adealink/weparty/module/order/IOrderService.kt
  5. 24 0
      app/src/main/java/com/adealink/weparty/module/order/OrderModule.kt
  6. 60 0
      app/src/main/java/com/adealink/weparty/module/order/data/DiscountData.kt
  7. 2 0
      app/src/main/java/com/adealink/weparty/module/order/data/OrderDetailInfo.kt
  8. 7 0
      app/src/main/java/com/adealink/weparty/module/order/listener/IDiscountListener.kt
  9. 19 0
      app/src/main/java/com/adealink/weparty/module/playmate/widget/PriceView.kt
  10. 4 3
      app/src/main/java/com/adealink/weparty/module/widget/form/field/picture/adapter/PictureItemViewBinder.kt
  11. 7 1
      app/src/main/java/com/adealink/weparty/network/NetworkConfig.kt
  12. 7 1
      app/src/main/java/com/adealink/weparty/stat/StatConfig.kt
  13. 3 0
      app/src/main/java/com/adealink/weparty/ui/MainStartUpFragment.kt
  14. BIN
      app/src/main/res/drawable-xhdpi/common_price_discount_arrow_down_ic.png
  15. BIN
      app/src/main/res/drawable-xhdpi/common_price_discount_arrow_up_ic.png
  16. 19 0
      app/src/main/res/drawable/common_discount_price_bg.xml
  17. 19 0
      app/src/main/res/drawable/common_discount_price_down_bg.xml
  18. 18 0
      app/src/main/res/drawable/common_discount_price_up_bg.xml
  19. 9 0
      app/src/main/res/drawable/common_new_playmate_tag_bg.xml
  20. 32 0
      app/src/main/res/layout/activity_debug.xml
  21. 4 0
      app/src/main/res/values-in/strings.xml
  22. 4 0
      app/src/main/res/values-zh/strings.xml
  23. 1 0
      app/src/main/res/values/attrs.xml
  24. 3 0
      app/src/main/res/values/colors.xml
  25. 4 0
      app/src/main/res/values/strings.xml
  26. 35 0
      app/src/main/res/values/styles.xml
  27. 5 5
      module/account/src/main/res/values/strings.xml
  28. 3 2
      module/im/src/main/res/values-zh/strings.xml
  29. 41 5
      module/order/src/main/java/com/adealink/weparty/order/CreateOrderActivity.kt
  30. 15 1
      module/order/src/main/java/com/adealink/weparty/order/OrderServiceImpl.kt
  31. 3 3
      module/order/src/main/java/com/adealink/weparty/order/PlaymateOrderListActivity.kt
  32. 20 3
      module/order/src/main/java/com/adealink/weparty/order/adapter/PlaymateOrderListItemViewBinder.kt
  33. 11 1
      module/order/src/main/java/com/adealink/weparty/order/adapter/UserOrderListItemViewBinder.kt
  34. 6 0
      module/order/src/main/java/com/adealink/weparty/order/data/OrderData.kt
  35. 6 0
      module/order/src/main/java/com/adealink/weparty/order/datasource/remote/OrderHttpService.kt
  36. 13 1
      module/order/src/main/java/com/adealink/weparty/order/detail/OrderDetailActivity.kt
  37. 39 4
      module/order/src/main/java/com/adealink/weparty/order/dialog/CreateOrderDialog.kt
  38. 20 8
      module/order/src/main/java/com/adealink/weparty/order/dialog/CreateOrderFromCategoryDialog.kt
  39. 77 16
      module/order/src/main/java/com/adealink/weparty/order/dialog/adapter/CategoryItemViewBinder.kt
  40. 71 0
      module/order/src/main/java/com/adealink/weparty/order/manager/DiscountManger.kt
  41. 14 0
      module/order/src/main/java/com/adealink/weparty/order/manager/IDiscountManager.kt
  42. 40 4
      module/order/src/main/java/com/adealink/weparty/order/qrcode/QRCodeOrderActivity.kt
  43. 36 5
      module/order/src/main/java/com/adealink/weparty/order/qrcode/QRCodeOrderDialog.kt
  44. 1 0
      module/order/src/main/java/com/adealink/weparty/order/refund/RefundActivity.kt
  45. 1 0
      module/order/src/main/java/com/adealink/weparty/order/refund/comp/PlaymateRefundCustomerComp.kt
  46. 12 3
      module/order/src/main/java/com/adealink/weparty/order/util/UIUtil.kt
  47. 2 0
      module/order/src/main/java/com/adealink/weparty/order/viewmodel/OrderViewModel.kt
  48. 2 0
      module/order/src/main/java/com/adealink/weparty/order/viewmodel/UserOrderViewModel.kt
  49. 50 0
      module/order/src/main/res/layout/activity_create_order.xml
  50. 67 1
      module/order/src/main/res/layout/activity_order_detail.xml
  51. 50 0
      module/order/src/main/res/layout/activity_qrcode_order.xml
  52. 67 2
      module/order/src/main/res/layout/dialog_create_order.xml
  53. 67 2
      module/order/src/main/res/layout/dialog_qrcode_create_order.xml
  54. 13 1
      module/order/src/main/res/layout/layout_create_order_category_item.xml
  55. 8 5
      module/order/src/main/res/layout/layout_playmate_order_list_item.xml
  56. 15 1
      module/order/src/main/res/layout/layout_user_order_list_item.xml
  57. 2 1
      module/order/src/main/res/values-in/strings.xml
  58. 2 1
      module/order/src/main/res/values-zh/strings.xml
  59. 2 1
      module/order/src/main/res/values/strings.xml
  60. 4 0
      module/playmate/src/main/java/com/adealink/weparty/playmate/data/PlaymateListData.kt
  61. 2 0
      module/playmate/src/main/java/com/adealink/weparty/playmate/detail/PlaymateDetailActivity.kt
  62. 29 4
      module/playmate/src/main/java/com/adealink/weparty/playmate/detail/comp/PlaymateDetailBottomComp.kt
  63. 1 1
      module/playmate/src/main/java/com/adealink/weparty/playmate/list/GuestPlaymateListFragment.kt
  64. 2 0
      module/playmate/src/main/java/com/adealink/weparty/playmate/list/PlaymateHomeFragment.kt
  65. 1 1
      module/playmate/src/main/java/com/adealink/weparty/playmate/list/PlaymateListFragment.kt
  66. 143 69
      module/playmate/src/main/java/com/adealink/weparty/playmate/list/adapter/PlaymateListItemViewBinder.kt
  67. 1 0
      module/playmate/src/main/java/com/adealink/weparty/playmate/list/viewmodel/PlaymateListViewModel.kt
  68. 72 31
      module/playmate/src/main/res/layout/item_playmate_home_list.xml
  69. 3 0
      module/playmate/src/main/res/layout/layout_playmate_detail_bottom.xml
  70. 19 4
      module/playmate/src/main/res/layout/layout_playmate_detail_other_bottom.xml
  71. 2 0
      module/profile/src/main/res/values-zh/strings.xml

+ 12 - 11
app/src/main/java/com/adealink/weparty/commonui/ext/ViewExt.kt

@@ -21,14 +21,13 @@ import android.view.animation.Interpolator
 import androidx.core.view.ViewCompat
 import androidx.core.view.WindowCompat
 import androidx.core.view.WindowInsetsCompat
-import androidx.fragment.app.findFragment
+import androidx.core.view.doOnAttach
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.findViewTreeLifecycleOwner
 import androidx.recyclerview.widget.RecyclerView
 import com.adealink.frame.util.runOnUiThread
 import com.adealink.weparty.commonui.BaseActivity
-import com.adealink.weparty.commonui.BaseFragment
 
 fun View.getActivity(): BaseActivity? {
     var context = context
@@ -270,14 +269,16 @@ fun Window.fitSystemWindows(decorFitsSystemWindows: Boolean) {
     WindowCompat.setDecorFitsSystemWindows(this, decorFitsSystemWindows)
 }
 
-fun View.findFragmentActivityLifecycleOwner(): LifecycleOwner? {
-    val fragment = findFragment<BaseFragment>()
-    if (fragment != null) {
-        return fragment
+fun View.findViewTreeLifecycleOwnerWhenAttach(callback: (owner: LifecycleOwner?) -> Unit) {
+    if (isAttachedToWindow) {
+        callback.invoke(
+            findViewTreeLifecycleOwner()
+        )
+        return
     }
-    val activity = getActivity()
-    if (activity != null) {
-        return activity
+    doOnAttach {
+        callback.invoke(
+            findViewTreeLifecycleOwner()
+        )
     }
-    return null
-}
+}

+ 15 - 0
app/src/main/java/com/adealink/weparty/debug/DebugActivity.kt

@@ -24,6 +24,7 @@ import com.adealink.weparty.commonui.widget.floatview.WindowManagerProxy
 import com.adealink.weparty.commonui.widget.floatview.data.MODE_APPLICATION
 import com.adealink.weparty.config.approvingVersionManager
 import com.adealink.weparty.databinding.ActivityDebugBinding
+import com.adealink.weparty.debug.DebugPrefs.testDeviceId
 import com.adealink.weparty.log.viewmodel.LogViewModelFactory
 import com.adealink.weparty.module.account.AccountLocalService
 import com.adealink.weparty.module.account.AccountModule
@@ -190,6 +191,11 @@ class DebugActivity : BaseActivity(), OnReturnValue {
         binding.uiTestBtn.onClick {
             uiTest()
         }
+
+        binding.deviceidEd.setText(testDeviceId)
+        binding.saveDeviceidBtn.onClick {
+            saveDeviceId()
+        }
     }
 
     private fun saveWebUrlEd() {
@@ -309,6 +315,15 @@ class DebugActivity : BaseActivity(), OnReturnValue {
         startActivity(Intent(this, DebugUIActivity::class.java))
     }
 
+    private fun saveDeviceId() {
+        val deviceId = binding.deviceidEd.text?.trim()?.toString()
+        if (deviceId.isNullOrEmpty()) {
+            return
+        }
+        testDeviceId = deviceId
+        showToast("请杀死App重启")
+    }
+
     companion object {
         var sysMemoryUsageFloatView: SysMemoryUsageFloatView? = null
         var isShowMemoryFloatView = false

+ 3 - 0
app/src/main/java/com/adealink/weparty/debug/DebugPrefs.kt

@@ -30,4 +30,7 @@ object DebugPrefs : TypeDelegationPrefs(
 
     var showPerformanceFloatView: Boolean by PrefKey("key_show_performance_float_view", true)
 
+
+    var testDeviceId: String by PrefKey("key_test_device_id", "")
+
 }

+ 7 - 0
app/src/main/java/com/adealink/weparty/module/order/IOrderService.kt

@@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModelStoreOwner
 import com.adealink.frame.base.Rlt
 import com.adealink.weparty.aab.IService
 import com.adealink.weparty.module.order.data.OrderDetailInfo
+import com.adealink.weparty.module.order.data.OrderDiscountInfo
 import com.adealink.weparty.module.order.viewmodel.IOrderViewModel
 
 interface IOrderService : IService<IOrderService> {
@@ -11,4 +12,10 @@ interface IOrderService : IService<IOrderService> {
 
     suspend fun queryUnCompleteOrder(uid: String): Rlt<List<OrderDetailInfo>>
 
+    fun queryDiscountInfo()
+
+    suspend fun suspendQueryDiscountInfo(): Rlt<Any>
+
+    fun getDiscountInfo(): OrderDiscountInfo?
+
 }

+ 24 - 0
app/src/main/java/com/adealink/weparty/module/order/OrderModule.kt

@@ -6,6 +6,7 @@ import com.adealink.frame.aab.constant.AABModuleNotInitError
 import com.adealink.frame.base.Rlt
 import com.adealink.weparty.R
 import com.adealink.weparty.module.order.data.OrderDetailInfo
+import com.adealink.weparty.module.order.data.OrderDiscountInfo
 import com.adealink.weparty.module.order.viewmodel.IOrderViewModel
 
 object OrderModule : BaseDynamicModule<IOrderService>(IOrderService::class),
@@ -34,6 +35,17 @@ object OrderModule : BaseDynamicModule<IOrderService>(IOrderService::class),
             override suspend fun queryUnCompleteOrder(uid: String): Rlt<List<OrderDetailInfo>> {
                 return Rlt.Failed(AABModuleNotInitError())
             }
+
+            override fun queryDiscountInfo() {
+            }
+
+            override suspend fun suspendQueryDiscountInfo(): Rlt<Any> {
+                return Rlt.Failed(AABModuleNotInitError())
+            }
+
+            override fun getDiscountInfo(): OrderDiscountInfo? {
+                return null
+            }
         }
     }
 
@@ -48,4 +60,16 @@ object OrderModule : BaseDynamicModule<IOrderService>(IOrderService::class),
     override suspend fun queryUnCompleteOrder(uid: String): Rlt<List<OrderDetailInfo>> {
         return getService().queryUnCompleteOrder(uid)
     }
+
+    override fun queryDiscountInfo() {
+        getService().queryDiscountInfo()
+    }
+
+    override suspend fun suspendQueryDiscountInfo(): Rlt<Any> {
+        return getService().suspendQueryDiscountInfo()
+    }
+
+    override fun getDiscountInfo(): OrderDiscountInfo? {
+        return getService().getDiscountInfo()
+    }
 }

+ 60 - 0
app/src/main/java/com/adealink/weparty/module/order/data/DiscountData.kt

@@ -0,0 +1,60 @@
+package com.adealink.weparty.module.order.data
+
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.Observer
+import com.adealink.frame.mvvm.livedata.ExtMutableLiveData
+import com.adealink.weparty.module.order.OrderModule
+
+
+val discountLD = ExtMutableLiveData<OrderDiscountInfo?>()
+
+fun observeDiscount(owner: LifecycleOwner, observer: Observer<OrderDiscountInfo?>) {
+    discountLD.observeWithoutCache(owner, observer)
+}
+
+fun unObserveDiscount(owner: LifecycleOwner) {
+    discountLD.removeObservers(owner)
+}
+
+fun unObserveDiscount(observer: Observer<OrderDiscountInfo?>) {
+    discountLD.removeObserver(observer)
+}
+
+data class FinalPrice(
+    val originPrice: Float, //原始价格
+    val discountPrice: Float, //折扣价格
+    val discount: Float, //折扣
+    val discountPercent: Float, //折扣比例(0-100)
+) {
+    fun hasDiscount(): Boolean {
+        return discount > 0
+    }
+}
+
+fun getFinalPrice(originPrice: Float?): FinalPrice {
+    if (originPrice == null || originPrice <= 0) {
+        return FinalPrice(
+            0f, 0f, 0f, 0f
+        )
+    }
+    val discountInfo = OrderModule.getDiscountInfo()
+    if (discountInfo == null || !discountInfo.hasDiscount || discountInfo.targetPrice != originPrice) {
+        return FinalPrice(
+            originPrice, originPrice, 0f, 0f
+        )
+    }
+    val discountPrice = originPrice * discountInfo.discountRate
+    val discount = originPrice - discountPrice
+    return FinalPrice(
+        originPrice,
+        discountPrice,
+        discount,
+        (1 - discountInfo.discountRate) * 100
+    )
+}
+
+data class OrderDiscountInfo(
+    val hasDiscount: Boolean,
+    val discountRate: Float, //当前首单折扣率(例如0.1表示1折)
+    val targetPrice: Float, //可参与优惠的SKU单价(金币)
+)

+ 2 - 0
app/src/main/java/com/adealink/weparty/module/order/data/OrderDetailInfo.kt

@@ -158,6 +158,8 @@ data class OrderDetailInfo(
     @SerializedName("status") var status: Int, //订单状态
     @SerializedName("createTime") val createTime: Long, //下单时间(时间戳, 毫秒)
     @GsonNullable
+    @SerializedName("totalAmount") val totalAmount: Float?, //订单价格
+    @GsonNullable
     @SerializedName("customerRemark") val customerRemark: String?, //客户备注
     @GsonNullable
     @SerializedName("star") var star: Float?, //评分

+ 7 - 0
app/src/main/java/com/adealink/weparty/module/order/listener/IDiscountListener.kt

@@ -0,0 +1,7 @@
+package com.adealink.weparty.module.order.listener
+
+import com.adealink.frame.frame.IListener
+
+
+interface IDiscountListener : IListener {
+}

+ 19 - 0
app/src/main/java/com/adealink/weparty/module/playmate/widget/PriceView.kt

@@ -17,6 +17,7 @@ import com.adealink.weparty.commonui.widget.util.getColorX
 import com.adealink.weparty.databinding.LayoutPriceViewBinding
 import com.adealink.weparty.util.formatNumberStr
 
+
 @SuppressLint("SetTextI18n")
 class PriceView @JvmOverloads constructor(
     context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
@@ -34,6 +35,7 @@ class PriceView @JvmOverloads constructor(
     private var priceTextSize = 12
     private var priceTextBold = true
     private var priceTextColor = getColorX(R.color.price_text_color)
+    private var priceDiscountTextColor = getColorX(R.color.price_discount_text_color)
     private var unitTextSize = 12
     private var unitTextColor = getColorX(R.color.price_unit_text_color)
     private var priceTextGravity = TEXT_GRAVITY_CENTER
@@ -72,6 +74,10 @@ class PriceView @JvmOverloads constructor(
                 R.styleable.PriceView_price_text_color,
                 priceTextColor
             )
+            priceDiscountTextColor = getColor(
+                R.styleable.PriceView_price_discount_text_color,
+                priceDiscountTextColor
+            )
 
             unitTextSize = getDimensionPixelSize(
                 R.styleable.PriceView_unit_text_size,
@@ -120,6 +126,11 @@ class PriceView @JvmOverloads constructor(
     }
 
     fun setPrice(price: Float, unit: String?) {
+        binding.tvPrice.setTextColor(priceTextColor)
+        setPriceInner(price, unit)
+    }
+
+    private fun setPriceInner(price: Float, unit: String?) {
         binding.tvPrice.text = formatNumberStr(price, false)
         if (unit.isNullOrEmpty()) {
             binding.tvUnit.text = null
@@ -128,5 +139,13 @@ class PriceView @JvmOverloads constructor(
         }
     }
 
+    /**
+     * 设置折扣价格
+     */
+    fun setDiscount(price: Float, unit: String?) {
+        binding.tvPrice.setTextColor(priceDiscountTextColor)
+        setPriceInner(price, unit)
+    }
+
 
 }

+ 4 - 3
app/src/main/java/com/adealink/weparty/module/widget/form/field/picture/adapter/PictureItemViewBinder.kt

@@ -2,8 +2,8 @@ package com.adealink.weparty.module.widget.form.field.picture.adapter
 
 import android.view.LayoutInflater
 import android.view.ViewGroup
-import androidx.lifecycle.findViewTreeLifecycleOwner
 import com.adealink.frame.util.onClick
+import com.adealink.weparty.commonui.ext.findViewTreeLifecycleOwnerWhenAttach
 import com.adealink.weparty.commonui.recycleview.adapter.BindingViewHolder
 import com.adealink.weparty.commonui.recycleview.adapter.multitype.ItemViewBinder
 import com.adealink.weparty.databinding.WidgetPicturePictureItemBinding
@@ -33,8 +33,9 @@ class PictureItemViewBinder(
         } else {
             holder.binding.ivPicture.setImageUrl(null)
         }
-        holder.binding.root.findViewTreeLifecycleOwner()?.let {
-            holder.binding.vUploadProgress.observeProgress(it, item.data.mediaUpload)
+        holder.binding.root.findViewTreeLifecycleOwnerWhenAttach { owner ->
+            owner ?: return@findViewTreeLifecycleOwnerWhenAttach
+            holder.binding.vUploadProgress.observeProgress(owner, item.data.mediaUpload)
         }
     }
 

+ 7 - 1
app/src/main/java/com/adealink/weparty/network/NetworkConfig.kt

@@ -30,6 +30,7 @@ import com.adealink.weparty.error.UserBannedError
 import com.adealink.weparty.module.account.AccountModule
 import com.adealink.weparty.storage.file.FilePath
 import com.adealink.frame.util.getNetworkType
+import com.adealink.weparty.debug.DebugPrefs.testDeviceId
 import com.google.gson.Gson
 import okhttp3.Interceptor
 
@@ -88,7 +89,12 @@ class NetworkConfig : INetworkConfig {
     override val currActivityName: String
         get() = AppUtil.cacheCurrentActivity?.componentName?.className ?: ""
     override val deviceId: String
-        get() = App.instance.deviceIdService.getMemOldDeviceId()
+        get() = if (AppBase.isRelease) {
+            App.instance.deviceIdService.getMemOldDeviceId()
+        } else {
+            testDeviceId.ifEmpty { App.instance.deviceIdService.getMemOldDeviceId() }
+        }
+
     override val newDeviceId: String
         get() = App.instance.deviceIdService.getMemDeviceId()
     override val packageName: String

+ 7 - 1
app/src/main/java/com/adealink/weparty/stat/StatConfig.kt

@@ -12,7 +12,9 @@ import com.adealink.frame.util.safeToLong
 import com.adealink.weparty.App
 import com.adealink.weparty.R
 import com.adealink.weparty.channel.getChannel
+import com.adealink.weparty.debug.DebugPrefs.testDeviceId
 import com.adealink.weparty.module.profile.ProfileModule
+import kotlin.text.ifEmpty
 
 class StatConfig : IStatConfig {
 
@@ -27,7 +29,11 @@ class StatConfig : IStatConfig {
     override val uid: Long
         get() = ProfileModule.getMyUid().safeToLong()
     override val deviceId: String
-        get() = App.instance.deviceIdService.getLocalDeviceId()
+        get() = if (AppBase.isRelease) {
+            App.instance.deviceIdService.getMemOldDeviceId()
+        } else {
+            testDeviceId.ifEmpty { App.instance.deviceIdService.getMemOldDeviceId() }
+        }
     override val countryCode: String
         get() = getCountryCode()
     override val languageCode: String

+ 3 - 0
app/src/main/java/com/adealink/weparty/ui/MainStartUpFragment.kt

@@ -18,6 +18,7 @@ import com.adealink.weparty.commonui.ext.isUiValid
 import com.adealink.weparty.config.approvingVersionManager
 import com.adealink.weparty.location.viewmodel.LocationReporter
 import com.adealink.weparty.module.account.AccountModule
+import com.adealink.weparty.module.order.OrderModule
 import com.adealink.weparty.module.profile.ProfileModule
 import com.adealink.weparty.module.setting.SettingModule
 import com.adealink.weparty.module.wallet.WalletModule
@@ -130,6 +131,8 @@ class MainStartUpFragment : BaseFragment() {
         WalletModule.init()
         WalletModule.fetchCurrency()
         WalletModule.queryAndHandleUnDealPurchases()
+
+        OrderModule.queryDiscountInfo()
         Log.d(TAG, "minorLoad-end, cost:${SystemClock.elapsedRealtime() - startTs}ms")
     }
 

BIN
app/src/main/res/drawable-xhdpi/common_price_discount_arrow_down_ic.png


BIN
app/src/main/res/drawable-xhdpi/common_price_discount_arrow_up_ic.png


+ 19 - 0
app/src/main/res/drawable/common_discount_price_bg.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+    <!-- 方法1:使用矩形旋转 -->
+    <!--    <item-->
+    <!--        android:width="10dp"-->
+    <!--        android:height="4dp"-->
+    <!--        android:gravity="bottom|start"-->
+    <!--        android:start="12dp">-->
+    <!--        <bitmap android:src="@drawable/common_price_discount_arrow_down_ic" />-->
+    <!--    </item>-->
+
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:radius="4dp" />
+            <solid android:color="#FFFF6F32" />
+        </shape>
+    </item>
+</layer-list>

+ 19 - 0
app/src/main/res/drawable/common_discount_price_down_bg.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+    <!-- 方法1:使用矩形旋转 -->
+    <item
+        android:width="10dp"
+        android:height="4dp"
+        android:gravity="bottom|start"
+        android:start="12dp">
+        <bitmap android:src="@drawable/common_price_discount_arrow_down_ic" />
+    </item>
+
+    <item android:bottom="4dp">
+        <shape android:shape="rectangle">
+            <corners android:radius="4dp" />
+            <solid android:color="#FFFF6F32" />
+        </shape>
+    </item>
+</layer-list>

+ 18 - 0
app/src/main/res/drawable/common_discount_price_up_bg.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+    <!-- 方法1:使用矩形旋转 -->
+    <item
+        android:width="10dp"
+        android:height="4dp"
+        android:gravity="center_horizontal">
+        <bitmap android:src="@drawable/common_price_discount_arrow_up_ic" />
+    </item>
+
+    <item android:top="4dp">
+        <shape android:shape="rectangle">
+            <corners android:radius="4dp" />
+            <solid android:color="#FFFF6F32" />
+        </shape>
+    </item>
+</layer-list>

+ 9 - 0
app/src/main/res/drawable/common_new_playmate_tag_bg.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <gradient
+        android:centerColor="#66FFD35B"
+        android:endColor="#66FFFFFF"
+        android:startColor="#66FFD35B" />
+    <corners android:radius="30dp" />
+</shape>

+ 32 - 0
app/src/main/res/layout/activity_debug.xml

@@ -463,6 +463,38 @@
 
                 </androidx.appcompat.widget.LinearLayoutCompat>
 
+                <View
+                    android:layout_width="match_parent"
+                    android:layout_height="1dp"
+                    android:background="@color/color_FFE1E3E6" />
+
+                <androidx.appcompat.widget.LinearLayoutCompat
+                    android:id="@+id/device_test_cl"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal">
+
+                    <androidx.appcompat.widget.AppCompatEditText
+                        android:id="@+id/deviceid_ed"
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="center_vertical"
+                        android:layout_weight="1"
+                        android:hint="自定义DeviceId"
+                        android:textColor="@color/black"
+                        android:textColorHint="@color/color_333333"
+                        android:textSize="14sp" />
+
+                    <androidx.appcompat.widget.AppCompatButton
+                        android:id="@+id/save_deviceid_btn"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="center_vertical"
+                        android:gravity="center"
+                        android:text="保存" />
+
+                </androidx.appcompat.widget.LinearLayoutCompat>
+
             </androidx.appcompat.widget.LinearLayoutCompat>
 
         </androidx.appcompat.widget.LinearLayoutCompat>

+ 4 - 0
app/src/main/res/values-in/strings.xml

@@ -367,4 +367,8 @@
     <string name="common_expand">perluas</string>
     <string name="common_select_photo">Pilih foto</string>
     <string name="common_select_video">Pilih video</string>
+    <string name="common_price_discount">%s%% Diskon</string>
+    <string name="common_price_discount_new_only">Khusus Baru | %s%% Diskon</string>
+    <string name="common_discount">Diskon</string>
+    <string name="common_newbie">Khusus Baru</string>
 </resources>

+ 4 - 0
app/src/main/res/values-zh/strings.xml

@@ -367,4 +367,8 @@
     <string name="common_expand">展开</string>
     <string name="common_select_photo">选择照片</string>
     <string name="common_select_video">选择视频</string>
+    <string name="common_price_discount">%s%% 折扣</string>
+    <string name="common_price_discount_new_only">仅限新用户|%s%% 折扣</string>
+    <string name="common_discount">折扣</string>
+    <string name="common_newbie">新人</string>
 </resources>

+ 1 - 0
app/src/main/res/values/attrs.xml

@@ -615,6 +615,7 @@
         <attr name="price_text_size" format="dimension" />
         <attr name="price_text_bold" format="boolean" />
         <attr name="price_text_color" format="color" />
+        <attr name="price_discount_text_color" format="color" />
         <attr name="unit_text_size" format="dimension" />
         <attr name="unit_text_color" format="color" />
         <attr name="price_text_gravity">

+ 3 - 0
app/src/main/res/values/colors.xml

@@ -35,6 +35,7 @@
     <color name="roulette_outer_border">#FFFFFFFF</color>
 
     <color name="price_text_color">@color/color_FF0D0D0D</color>
+    <color name="price_discount_text_color">@color/color_FFFF6F32</color>
     <color name="price_unit_text_color">@color/color_FF8E8E8E</color>
 
     <color name="data_binding_span_at_somebody">#ff00dfd6</color>
@@ -1211,4 +1212,6 @@
     <color name="color_FFB1EF5D">#FFB1EF5D</color>
     <color name="color_80B1EF5D">#80B1EF5D</color>
     <color name="color_FFC4C4C4">#FFC4C4C4</color>
+    <color name="color_FFFF6F32">#FFFF6F32</color>
+    <color name="color_FFDE9D2E">#FFDE9D2E</color>
 </resources>

+ 4 - 0
app/src/main/res/values/strings.xml

@@ -374,4 +374,8 @@
     <string name="common_expand">expand</string>
     <string name="common_select_photo">Select photo</string>
     <string name="common_select_video">Select video</string>
+    <string name="common_price_discount">%s%% OFF</string>
+    <string name="common_price_discount_new_only">New Only | %s%% OFF</string>
+    <string name="common_discount">Discount</string>
+    <string name="common_newbie">Newbie</string>
 </resources>

+ 35 - 0
app/src/main/res/values/styles.xml

@@ -181,4 +181,39 @@
         <item name="android:layout_width">40dp</item>
         <item name="android:layout_height">24.5dp</item>
     </style>
+
+    <style name="CommonDiscountPriceUpView">
+        <item name="android:background">@drawable/common_discount_price_up_bg</item>
+        <item name="android:includeFontPadding">false</item>
+        <item name="android:layout_height">21dp</item>
+        <item name="android:textSize">11sp</item>
+        <item name="android:textColor">@color/white</item>
+        <item name="android:paddingTop">4dp</item>
+        <item name="android:gravity">center</item>
+        <item name="android:paddingStart">5dp</item>
+        <item name="android:paddingEnd">5dp</item>
+    </style>
+
+    <style name="CommonDiscountPriceDownView">
+        <item name="android:background">@drawable/common_discount_price_down_bg</item>
+        <item name="android:includeFontPadding">false</item>
+        <item name="android:layout_height">21dp</item>
+        <item name="android:textSize">11sp</item>
+        <item name="android:textColor">@color/white</item>
+        <item name="android:paddingBottom">4dp</item>
+        <item name="android:gravity">center</item>
+        <item name="android:paddingStart">5dp</item>
+        <item name="android:paddingEnd">5dp</item>
+    </style>
+
+    <style name="CommonDiscountPriceView">
+        <item name="android:background">@drawable/common_discount_price_bg</item>
+        <item name="android:includeFontPadding">false</item>
+        <item name="android:layout_height">21dp</item>
+        <item name="android:textSize">11sp</item>
+        <item name="android:textColor">@color/white</item>
+        <item name="android:gravity">center</item>
+        <item name="android:paddingStart">5dp</item>
+        <item name="android:paddingEnd">5dp</item>
+    </style>
 </resources>

+ 5 - 5
module/account/src/main/res/values/strings.xml

@@ -50,8 +50,8 @@
     <string name="account_register_go_home">Enter %s</string>
     <string name="account_login_tips">Login in embark on your exclusive journey</string>
     <string name="account_register_change_nickname">Change</string>
-    <string name="account_phone_login_title">Setelah masuk</string>
-    <string name="account_phone_login_desc">Mulai perjalanan bermain game eksklusif Anda</string>
-    <string name="account_other_login_way">Metode masuk lainnya</string>
-    <string name="account_phone_get_verify_code">Dapatkan kode verifikasi</string>
-</resources>
+    <string name="account_phone_login_title">After Login</string>
+    <string name="account_phone_login_desc">Start your exclusive gaming journey</string>
+    <string name="account_other_login_way">Other login methods</string>
+    <string name="account_phone_get_verify_code">Get Verification Code</string>
+</resources>

+ 3 - 2
module/im/src/main/res/values-zh/strings.xml

@@ -43,5 +43,6 @@
     <string name="im_unsupported_message_tips">不支持的消息类型,请升级APP</string>
     <string name="im_edit_note">备注</string>
     <string name="im_edit_subtitle">请输入备注名字</string>
-    <string name="im_order_go_voice">Go voice</string>
-</resources>
+    <string name="im_order_go_voice">语音通话</string>
+    <string name="im_create_order_title">立即下单,即可语音通话</string>
+</resources>

+ 41 - 5
module/order/src/main/java/com/adealink/weparty/order/CreateOrderActivity.kt

@@ -13,11 +13,16 @@ import com.adealink.frame.router.annotation.BindExtra
 import com.adealink.frame.router.annotation.RouterUri
 import com.adealink.frame.util.onClick
 import com.adealink.weparty.commonui.BaseActivity
+import com.adealink.weparty.commonui.ext.gone
+import com.adealink.weparty.commonui.ext.show
 import com.adealink.weparty.commonui.toast.util.showFailedToast
 import com.adealink.weparty.commonui.widget.BottomDialogFragment
 import com.adealink.weparty.commonui.widget.NumberIncreaseView
 import com.adealink.weparty.constant.ORDER_REMARK_MAX_LENGTH
 import com.adealink.weparty.module.order.Order
+import com.adealink.weparty.module.order.OrderModule
+import com.adealink.weparty.module.order.data.discountLD
+import com.adealink.weparty.module.order.data.getFinalPrice
 import com.adealink.weparty.module.playmate.PlaymateModule
 import com.adealink.weparty.module.playmate.data.PlaymateDetailData
 import com.adealink.weparty.order.databinding.ActivityCreateOrderBinding
@@ -25,7 +30,10 @@ import com.adealink.weparty.order.dialog.EditRemarkDialog
 import com.adealink.weparty.order.viewmodel.OrderViewModel
 import com.adealink.weparty.order.viewmodel.OrderViewModelFactory
 import com.adealink.weparty.util.applyImmersive
+import com.adealink.weparty.util.formatNumber
 import com.adealink.weparty.util.formatStar
+import kotlin.math.max
+import com.adealink.weparty.R as APP_R
 
 @RouterUri(path = [Order.Create.PATH], desc = "创建订单")
 class CreateOrderActivity : BaseActivity() {
@@ -66,7 +74,8 @@ class CreateOrderActivity : BaseActivity() {
                 updatePlaymateInfo()
             }
         })
-        binding.tvRemarkInput.hint = getCompatString(R.string.order_remark_hint, ORDER_REMARK_MAX_LENGTH.toString())
+        binding.tvRemarkInput.hint =
+            getCompatString(R.string.order_remark_hint, ORDER_REMARK_MAX_LENGTH.toString())
         binding.tvRemarkInput.onClick {
             showEditRemarkDialog()
         }
@@ -80,10 +89,14 @@ class CreateOrderActivity : BaseActivity() {
         viewModel.onOrderCreatedLD.observeWithoutCache(this) {
             finish()
         }
+        discountLD.observe(this) {
+            updatePrice()
+        }
     }
 
     override fun loadData() {
         super.loadData()
+        OrderModule.queryDiscountInfo()
         val playmateData = playmateData
         val playmateId = playmateId
         if (playmateData != null) {
@@ -118,10 +131,33 @@ class CreateOrderActivity : BaseActivity() {
             playmateData?.unit ?: ""
         )
 
-        binding.vBottom.vPrice.setPrice(
-            (playmateData?.price ?: 0f) * orderCount,
-            null
-        )
+        updatePrice()
+    }
+
+    private fun updatePrice() {
+        val data = playmateData
+        val finalPrice = getFinalPrice(data?.price)
+        if (finalPrice.hasDiscount()) {
+            binding.clDiscount.show()
+            binding.vDiscount.text = getCompatString(
+                APP_R.string.common_price_discount_new_only,
+                formatNumber(finalPrice.discountPercent, true)
+            )
+            binding.vPriceDiscount.setDiscount(
+                -finalPrice.discount, null
+            )
+            //首单第一个免费
+            binding.vBottom.vPrice.setPrice(
+                finalPrice.discountPrice + (playmateData?.price ?: 0f) * max(orderCount - 1, 0),
+                null
+            )
+        } else {
+            binding.clDiscount.gone()
+            binding.vBottom.vPrice.setPrice(
+                (playmateData?.price ?: 0f) * orderCount,
+                null
+            )
+        }
     }
 
     private fun showEditRemarkDialog() {

+ 15 - 1
module/order/src/main/java/com/adealink/weparty/order/OrderServiceImpl.kt

@@ -8,9 +8,11 @@ import com.adealink.frame.spi.RegisterService
 import com.adealink.weparty.App
 import com.adealink.weparty.module.order.IOrderService
 import com.adealink.weparty.module.order.data.OrderDetailInfo
+import com.adealink.weparty.module.order.data.OrderDiscountInfo
 import com.adealink.weparty.module.order.data.QueryUnCompleteOrderReq
 import com.adealink.weparty.module.order.viewmodel.IOrderViewModel
 import com.adealink.weparty.order.datasource.remote.OrderHttpService
+import com.adealink.weparty.order.manager.discountManager
 import com.adealink.weparty.order.viewmodel.OrderViewModel
 import com.adealink.weparty.order.viewmodel.OrderViewModelFactory
 
@@ -22,7 +24,7 @@ class OrderServiceImpl : IOrderService {
     }
 
     override fun onLogout() {
-
+        discountManager.clear()
     }
 
     override fun getService(): IOrderService {
@@ -47,4 +49,16 @@ class OrderServiceImpl : IOrderService {
         }
     }
 
+    override fun queryDiscountInfo() {
+        discountManager.queryDiscountInfo()
+    }
+
+    override suspend fun suspendQueryDiscountInfo(): Rlt<Any> {
+        return discountManager.suspendQueryDiscountInfo()
+    }
+
+    override fun getDiscountInfo(): OrderDiscountInfo? {
+        return discountManager.getDiscountInfo()
+    }
+
 }

+ 3 - 3
module/order/src/main/java/com/adealink/weparty/order/PlaymateOrderListActivity.kt

@@ -20,7 +20,7 @@ import com.adealink.weparty.commonui.recycleview.itemdecoration.VerticalSpaceIte
 import com.adealink.weparty.commonui.toast.util.showFailedToast
 import com.adealink.weparty.commonui.widget.CommonDialog
 import com.adealink.weparty.module.order.Order
-import com.adealink.weparty.order.adapter.PlaymateListItemViewBinder
+import com.adealink.weparty.order.adapter.PlaymateOrderListItemViewBinder
 import com.adealink.weparty.order.data.PlaymateOrderListItemData
 import com.adealink.weparty.order.databinding.ActivityPlaymateOrderListBinding
 import com.adealink.weparty.order.viewmodel.OrderViewModelFactory
@@ -32,7 +32,7 @@ import com.adealink.weparty.R as APP_R
 
 @RouterUri(path = [Order.PlaymateList.PATH], desc = "陪玩接单列表")
 class PlaymateOrderListActivity : BaseActivity(),
-    PlaymateListItemViewBinder.OnPlaymateOrderListener {
+    PlaymateOrderListItemViewBinder.OnPlaymateOrderListener {
 
     companion object {
         private const val TAG = "PlaymateOrderListActivity"
@@ -72,7 +72,7 @@ class PlaymateOrderListActivity : BaseActivity(),
             }
         })
 
-        listAdapter.register(PlaymateListItemViewBinder(this))
+        listAdapter.register(PlaymateOrderListItemViewBinder(this))
         binding.rvOrder.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
         binding.rvOrder.adapter = listAdapter
         binding.rvOrder.addItemDecoration(

+ 20 - 3
module/order/src/main/java/com/adealink/weparty/order/adapter/PlaymateListItemViewBinder.kt → module/order/src/main/java/com/adealink/weparty/order/adapter/PlaymateOrderListItemViewBinder.kt

@@ -6,20 +6,23 @@ import android.view.ViewGroup
 import com.adealink.frame.aab.util.getCompatString
 import com.adealink.frame.util.formatTime
 import com.adealink.frame.util.onClick
+import com.adealink.weparty.commonui.ext.dp
 import com.adealink.weparty.commonui.ext.gone
 import com.adealink.weparty.commonui.ext.show
+import com.adealink.weparty.commonui.ext.sp
 import com.adealink.weparty.commonui.recycleview.adapter.BindingViewHolder
 import com.adealink.weparty.commonui.recycleview.adapter.multitype.ItemViewBinder
 import com.adealink.weparty.module.order.data.OrderStatus
 import com.adealink.weparty.module.order.util.setUserOrderStatusText
+import com.adealink.weparty.order.R
 import com.adealink.weparty.order.data.PlaymateOrderListItemData
 import com.adealink.weparty.order.databinding.LayoutPlaymateOrderListItemBinding
+import com.adealink.weparty.order.util.getOrderAllCost
 import com.adealink.weparty.util.TIME_FORMAT_DMY_HMS
-import com.adealink.weparty.util.formatNumberStr
 import com.adealink.weparty.util.getGenderResource
 import com.adealink.weparty.R as APP_R
 
-class PlaymateListItemViewBinder(
+class PlaymateOrderListItemViewBinder(
     val listener: OnPlaymateOrderListener
 ) : ItemViewBinder<PlaymateOrderListItemData, BindingViewHolder<LayoutPlaymateOrderListItemBinding>>() {
 
@@ -34,7 +37,21 @@ class PlaymateListItemViewBinder(
         holder.binding.tvCategory.text = item.data.categoryName
         holder.binding.tvOrderId.text = item.data.orderId
 
-        holder.binding.tvPrice.text = formatNumberStr(item.data.price, false)
+        val totalAmount = item.data.totalAmount
+        val targetAmount = item.data.price * item.data.purchaseQty
+        if (totalAmount != null && totalAmount > 0 && totalAmount < targetAmount) {
+            holder.binding.vDiscount.show()
+        } else {
+            holder.binding.vDiscount.gone()
+        }
+        holder.binding.tvPrice.text = getOrderAllCost(
+            item.data.price,
+            item.data.purchaseQty,
+            totalAmount,
+            16.sp(),
+            14.4f.dp(),
+            R.string.order_all_cost
+        )
         holder.binding.tvCount.text = "${item.data.unit} x${item.data.purchaseQty}"
 
         holder.binding.ivAvatar.setImageUrl(item.data.avatar)

+ 11 - 1
module/order/src/main/java/com/adealink/weparty/order/adapter/UserOrderListItemViewBinder.kt

@@ -46,9 +46,18 @@ class UserOrderListItemViewBinder(
         holder.binding.tvDesc.text = item.data.categoryName
         holder.binding.tvPrice.text = formatNumberStr(item.data.price, false)
         holder.binding.tvCount.text = "${item.data.unit} x${item.data.purchaseQty}"
+
+        val totalAmount = item.data.totalAmount
+        val targetAmount = item.data.price * item.data.purchaseQty
+        if (totalAmount != null && totalAmount > 0 && totalAmount < targetAmount) {
+            holder.binding.vDiscount.show()
+        } else {
+            holder.binding.vDiscount.gone()
+        }
         holder.binding.tvOrderAllCost.text = getOrderAllCost(
             item.data.price,
             item.data.purchaseQty,
+            item.data.totalAmount,
             16.sp(),
             16.dp()
         )
@@ -61,7 +70,8 @@ class UserOrderListItemViewBinder(
                 holder.binding.btnCancel.gone()
                 holder.binding.btnEvaluate.gone()
                 holder.binding.tvRefunded.show()
-                holder.binding.tvRefunded.text = getCompatString(R.string.order_refunded_pending_review)
+                holder.binding.tvRefunded.text =
+                    getCompatString(R.string.order_refunded_pending_review)
             }
 
             status == OrderStatus.CREATE_ORDER -> {

+ 6 - 0
module/order/src/main/java/com/adealink/weparty/order/data/OrderData.kt

@@ -61,3 +61,9 @@ data class EvaluateOrderReq(
     @SerializedName("star") val star: String, //评分(仅支持, 0.5, 1, 1,5, 2, 2.5 ....)
     @SerializedName("comment") val comment: String, //评价
 )
+
+data class OrderDiscountData(
+    @SerializedName("hasChance") val hasDiscount: Boolean, //当前是否有首单折扣机会
+    @SerializedName("discountRate") val discountRate: Float, //当前首单折扣率(例如0.1表示1折)
+    @SerializedName("eligiblePrice") val price: Float, //可参与优惠的SKU单价(金币)
+)

+ 6 - 0
module/order/src/main/java/com/adealink/weparty/order/datasource/remote/OrderHttpService.kt

@@ -17,6 +17,7 @@ import com.adealink.weparty.order.data.CancelOrderReq
 import com.adealink.weparty.order.data.CompleteOrderReq
 import com.adealink.weparty.order.data.CreateOrderReq
 import com.adealink.weparty.order.data.EvaluateOrderReq
+import com.adealink.weparty.order.data.OrderDiscountData
 import com.adealink.weparty.order.data.PlaymateOrderListReq
 import com.adealink.weparty.order.data.PlaymateOrderListRes
 import com.adealink.weparty.order.data.UserOrderListReq
@@ -97,4 +98,9 @@ interface OrderHttpService {
     @POST("skill/order/qr/payment")
     suspend fun qrCodeOrderPay(@Body req: QRCodeOrderPayReq): Rlt<Res<Any>>
 
+
+    @POST("skill/firstOrder/discount/chance")
+    suspend fun orderDiscount(): Rlt<Res<OrderDiscountData>>
+
+
 }

+ 13 - 1
module/order/src/main/java/com/adealink/weparty/order/detail/OrderDetailActivity.kt

@@ -52,6 +52,7 @@ import com.adealink.weparty.order.viewmodel.OrderViewModelFactory
 import com.adealink.weparty.order.viewmodel.UserOrderViewModel
 import com.adealink.weparty.util.TIME_FORMAT_DMY_HMS
 import com.adealink.weparty.util.applyImmersive
+import com.adealink.weparty.util.formatNumber
 import com.adealink.weparty.util.goImagePreviewActivity
 import okhttp3.internal.format
 import kotlin.math.max
@@ -182,8 +183,18 @@ class OrderDetailActivity : BaseActivity() {
         binding.tvOrderName.text = data?.order?.nickname
 
         binding.tvProductTypeDetail.text = data?.order?.categoryName
-        binding.tvProductPriceDetail.text = data?.order?.price?.toString()
+        binding.tvProductPriceDetail.text = formatNumber(data?.order?.price ?: 0f, false)
         binding.tvOrderCountDetail.text = "${data?.order?.unit} x${data?.order?.purchaseQty}"
+
+        val totalAmount = data?.order?.totalAmount
+        val targetAmount = (data?.order?.price ?: 0f) * (data?.order?.purchaseQty ?: 0)
+        if (totalAmount != null && totalAmount > 0 && totalAmount < targetAmount) {
+            binding.clDiscount.show()
+            binding.tvOrderDiscountDetail.text = formatNumber(-(targetAmount - totalAmount), false)
+        } else {
+            binding.clDiscount.gone()
+        }
+
         if (data?.order?.customerRemark.isNullOrEmpty()) {
             binding.clOrderRemark.gone()
         } else {
@@ -197,6 +208,7 @@ class OrderDetailActivity : BaseActivity() {
             getOrderAllCost(
                 data?.order?.price ?: 0f,
                 data?.order?.purchaseQty ?: 0,
+                data?.order?.totalAmount,
                 16.sp(),
                 16.dp()
             )

+ 39 - 4
module/order/src/main/java/com/adealink/weparty/order/dialog/CreateOrderDialog.kt

@@ -20,7 +20,10 @@ import com.adealink.weparty.commonui.widget.NumberIncreaseView
 import com.adealink.weparty.constant.ORDER_REMARK_MAX_LENGTH
 import com.adealink.weparty.module.im.IM
 import com.adealink.weparty.module.order.Order
+import com.adealink.weparty.module.order.OrderModule
 import com.adealink.weparty.module.order.data.CreateOrderRes
+import com.adealink.weparty.module.order.data.discountLD
+import com.adealink.weparty.module.order.data.getFinalPrice
 import com.adealink.weparty.module.playmate.PlaymateModule
 import com.adealink.weparty.module.playmate.data.PlaymateDetailData
 import com.adealink.weparty.module.wallet.WalletModule
@@ -29,7 +32,10 @@ import com.adealink.weparty.order.R
 import com.adealink.weparty.order.databinding.DialogCreateOrderBinding
 import com.adealink.weparty.order.viewmodel.OrderViewModel
 import com.adealink.weparty.order.viewmodel.OrderViewModelFactory
+import com.adealink.weparty.util.formatNumber
 import com.adealink.weparty.util.formatNumberStr
+import kotlin.math.max
+import com.adealink.weparty.R as APP_R
 
 @RouterUri(path = [Order.CreateDialog.PATH], desc = "订单确认弹窗")
 class CreateOrderDialog : BottomDialogFragment(R.layout.dialog_create_order) {
@@ -95,7 +101,8 @@ class CreateOrderDialog : BottomDialogFragment(R.layout.dialog_create_order) {
         }
 
         binding.tvRemarkInput.text = remark
-        binding.tvRemarkInput.hint = getCompatString(R.string.order_remark_hint, ORDER_REMARK_MAX_LENGTH.toString())
+        binding.tvRemarkInput.hint =
+            getCompatString(R.string.order_remark_hint, ORDER_REMARK_MAX_LENGTH.toString())
         binding.tvRemarkInput.onClick {
             showEditRemarkDialog()
         }
@@ -112,6 +119,7 @@ class CreateOrderDialog : BottomDialogFragment(R.layout.dialog_create_order) {
 
     override fun loadData() {
         super.loadData()
+        OrderModule.queryDiscountInfo()
         walletViewModel?.refreshWalletData()
 
         val playmateData = playmateData
@@ -145,9 +153,33 @@ class CreateOrderDialog : BottomDialogFragment(R.layout.dialog_create_order) {
         binding.tvPrice.text =
             "${formatNumberStr((playmateData?.price ?: 0f), false)}/${playmateData?.unit ?: ""}"
 
-        binding.tvTotalDetail.text = formatNumberStr(
-            (playmateData?.price ?: 0f) * orderCount, false
-        )
+        updatePrice()
+    }
+
+    private fun updatePrice() {
+        val data = playmateData
+        val finalPrice = getFinalPrice(data?.price)
+        if (finalPrice.hasDiscount()) {
+            binding.clDiscount.show()
+            binding.vDiscount.text = getCompatString(
+                APP_R.string.common_price_discount_new_only,
+                formatNumber(finalPrice.discountPercent, true)
+            )
+            binding.vPriceDiscount.setDiscount(
+                -finalPrice.discount, null
+            )
+            //首单第一个免费
+            binding.tvTotalDetail.text = formatNumberStr(
+                finalPrice.discountPrice +
+                        (playmateData?.price ?: 0f) * max((orderCount - 1), 0), false
+            )
+
+        } else {
+            binding.clDiscount.gone()
+            binding.tvTotalDetail.text = formatNumberStr(
+                (playmateData?.price ?: 0f) * orderCount, false
+            )
+        }
     }
 
     override fun observeViewModel() {
@@ -155,6 +187,9 @@ class CreateOrderDialog : BottomDialogFragment(R.layout.dialog_create_order) {
         walletViewModel?.coinLD?.observe(viewLifecycleOwner) {
             binding.tvWalletCoin.text = formatNumberStr(it, false)
         }
+        discountLD.observe(this) {
+            updatePrice()
+        }
     }
 
     private fun showEditRemarkDialog() {

+ 20 - 8
module/order/src/main/java/com/adealink/weparty/order/dialog/CreateOrderFromCategoryDialog.kt

@@ -18,15 +18,19 @@ import com.adealink.weparty.commonui.toast.util.showFailedToast
 import com.adealink.weparty.commonui.widget.BottomDialogFragment
 import com.adealink.weparty.commonui.widget.NumberIncreaseView
 import com.adealink.weparty.module.order.Order
+import com.adealink.weparty.module.order.OrderModule
+import com.adealink.weparty.module.order.data.getFinalPrice
 import com.adealink.weparty.module.profile.ProfileModule
 import com.adealink.weparty.module.profile.data.UserPlaymateSkill
 import com.adealink.weparty.order.R
 import com.adealink.weparty.order.databinding.DialogCreateOrderFromCategoryBinding
 import com.adealink.weparty.order.dialog.adapter.CategoryItemViewBinder
 import com.adealink.weparty.order.dialog.adapter.CategoryListItemData
+import kotlin.math.max
 
 @RouterUri(path = [Order.CreateFromCategory.PATH], desc = "根据用户品列创建订单")
-class CreateOrderFromCategoryDialog : BottomDialogFragment(R.layout.dialog_create_order_from_category) {
+class CreateOrderFromCategoryDialog :
+    BottomDialogFragment(R.layout.dialog_create_order_from_category) {
 
     @BindExtra(Order.CreateFromCategory.EXTRA_TITLE)
     var title: String? = null
@@ -53,13 +57,18 @@ class CreateOrderFromCategoryDialog : BottomDialogFragment(R.layout.dialog_creat
         super.initViews()
         binding.tvTitle.text = title
         binding.rvCategory.adapter = listAdapter
-        binding.rvCategory.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
+        binding.rvCategory.layoutManager =
+            LinearLayoutManager(context, RecyclerView.VERTICAL, false)
         binding.rvCategory.addItemDecoration(VerticalSpaceItemDecoration(8.dp()))
-        listAdapter.register(CategoryItemViewBinder(object : CategoryItemViewBinder.CategoryChooseListener {
-            override fun onChoose(data: CategoryListItemData, positon: Int) {
-                onCategorySelect(data, positon)
-            }
-        }))
+        listAdapter.register(
+            CategoryItemViewBinder(
+                this@CreateOrderFromCategoryDialog,
+                object : CategoryItemViewBinder.CategoryChooseListener {
+                    override fun onChoose(data: CategoryListItemData, positon: Int) {
+                        onCategorySelect(data, positon)
+                    }
+                })
+        )
 
         binding.vNumber.setNumber(orderCount)
         binding.vNumber.setListener(object : NumberIncreaseView.OnNumberChangedListener {
@@ -81,6 +90,7 @@ class CreateOrderFromCategoryDialog : BottomDialogFragment(R.layout.dialog_creat
         if (uid.isNullOrEmpty()) {
             return
         }
+        OrderModule.queryDiscountInfo()
         profileViewModel?.pullUserInfoBy(uid, true)?.observe(viewLifecycleOwner) { rlt ->
             when (rlt) {
                 is Rlt.Failed -> {
@@ -111,8 +121,10 @@ class CreateOrderFromCategoryDialog : BottomDialogFragment(R.layout.dialog_creat
     }
 
     private fun updateOrderInfo() {
+        val originPrice = (selectItem?.data?.price ?: 0f)
+        val finalPrice = getFinalPrice(originPrice)
         binding.vPrice.setPrice(
-            (selectItem?.data?.price ?: 0f) * orderCount,
+            finalPrice.discountPrice + originPrice * max(orderCount - 1, 0),
             null
         )
     }

+ 77 - 16
module/order/src/main/java/com/adealink/weparty/order/dialog/adapter/CategoryItemViewBinder.kt

@@ -2,12 +2,23 @@ package com.adealink.weparty.order.dialog.adapter
 
 import android.view.LayoutInflater
 import android.view.ViewGroup
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.Observer
+import com.adealink.frame.aab.util.getCompatString
 import com.adealink.frame.util.onClick
+import com.adealink.weparty.commonui.ext.isViewValid
+import com.adealink.weparty.commonui.ext.show
 import com.adealink.weparty.commonui.recycleview.adapter.BindingViewHolder
 import com.adealink.weparty.commonui.recycleview.adapter.multitype.ItemViewBinder
+import com.adealink.weparty.module.order.data.OrderDiscountInfo
+import com.adealink.weparty.module.order.data.getFinalPrice
+import com.adealink.weparty.module.order.data.observeDiscount
+import com.adealink.weparty.module.order.data.unObserveDiscount
 import com.adealink.weparty.module.profile.data.UserPlaymateSkill
 import com.adealink.weparty.order.R
 import com.adealink.weparty.order.databinding.LayoutCreateOrderCategoryItemBinding
+import com.adealink.weparty.util.formatNumber
+import com.adealink.weparty.R as APP_R
 
 data class CategoryListItemData(
     val data: UserPlaymateSkill,
@@ -15,38 +26,88 @@ data class CategoryListItemData(
 )
 
 class CategoryItemViewBinder(
+    val owner: LifecycleOwner,
     val listener: CategoryChooseListener
 ) :
-    ItemViewBinder<CategoryListItemData, BindingViewHolder<LayoutCreateOrderCategoryItemBinding>>() {
+    ItemViewBinder<CategoryListItemData, CategoryItemViewBinder.ViewHolder>() {
 
     override fun onBindViewHolder(
-        holder: BindingViewHolder<LayoutCreateOrderCategoryItemBinding>,
+        holder: ViewHolder,
         item: CategoryListItemData,
     ) {
-        holder.binding.root.onClick {
-            listener.onChoose(item, holder.layoutPosition)
-        }
-        if (item.selected) {
-            holder.binding.root.setBackgroundResource(R.drawable.create_order_category_item_selected_bg)
-        } else {
-            holder.binding.root.setBackgroundResource(R.drawable.create_order_category_item_bg)
-        }
-        holder.binding.ivIcon.setImageUrl(item.data.icon)
-        holder.binding.tvTitle.text = item.data.name
-        holder.binding.vPrice.setPrice(item.data.price, item.data.unit)
+        holder.update(item)
     }
 
     override fun onCreateViewHolder(
         inflater: LayoutInflater,
         parent: ViewGroup,
-    ): BindingViewHolder<LayoutCreateOrderCategoryItemBinding> {
-        return BindingViewHolder(
+    ): ViewHolder {
+        return ViewHolder(
             LayoutCreateOrderCategoryItemBinding.inflate(
                 inflater,
                 parent,
                 false
             )
-        )
+        ).also {
+            it.initView()
+        }
+    }
+
+    override fun onViewAttachedToWindow(holder: ViewHolder) {
+        super.onViewAttachedToWindow(holder)
+        observeDiscount(owner, holder.discountObserver)
+    }
+
+    override fun onViewDetachedFromWindow(holder: ViewHolder) {
+        super.onViewDetachedFromWindow(holder)
+        unObserveDiscount(holder.discountObserver)
+    }
+
+    inner class ViewHolder(
+        binding: LayoutCreateOrderCategoryItemBinding,
+    ) : BindingViewHolder<LayoutCreateOrderCategoryItemBinding>(binding) {
+        private var item: CategoryListItemData? = null
+
+        fun initView() {
+            binding.root.onClick {
+                val item = this.item ?: return@onClick
+                listener.onChoose(item, layoutPosition)
+            }
+        }
+
+        fun update(item: CategoryListItemData) {
+            this.item = item
+
+            if (item.selected) {
+                binding.root.setBackgroundResource(R.drawable.create_order_category_item_selected_bg)
+            } else {
+                binding.root.setBackgroundResource(R.drawable.create_order_category_item_bg)
+            }
+            binding.ivIcon.setImageUrl(item.data.icon)
+            binding.tvTitle.text = item.data.name
+            binding.vPrice.setPrice(item.data.price, item.data.unit)
+
+            updatePrice()
+        }
+
+        val discountObserver = object : Observer<OrderDiscountInfo?> {
+
+            override fun onChanged(value: OrderDiscountInfo?) {
+                updatePrice()
+            }
+        }
+
+        private fun updatePrice() {
+            if (!isViewValid()) {
+                return
+            }
+            val finalPrice = getFinalPrice(item?.data?.price ?: 0f)
+            binding.vDiscount.show(finalPrice.hasDiscount())
+            binding.vDiscount.text = getCompatString(
+                APP_R.string.common_price_discount,
+                formatNumber(finalPrice.discountPercent, true)
+            )
+        }
     }
 
     interface CategoryChooseListener {

+ 71 - 0
module/order/src/main/java/com/adealink/weparty/order/manager/DiscountManger.kt

@@ -0,0 +1,71 @@
+package com.adealink.weparty.order.manager
+
+import com.adealink.frame.base.Rlt
+import com.adealink.frame.frame.BaseFrame
+import com.adealink.weparty.App
+import com.adealink.weparty.module.order.data.OrderDiscountInfo
+import com.adealink.weparty.module.order.data.discountLD
+import com.adealink.weparty.module.order.listener.IDiscountListener
+import com.adealink.weparty.order.data.OrderDiscountData
+import com.adealink.weparty.order.datasource.remote.OrderHttpService
+import kotlinx.coroutines.launch
+
+val discountManager: IDiscountManager by lazy { DiscountManger() }
+
+class DiscountManger : BaseFrame<IDiscountListener>(), IDiscountManager {
+
+    private var discountInfo: OrderDiscountInfo? = null
+
+    private val orderHttpService by lazy {
+        App.instance.networkService.getHttpService(OrderHttpService::class.java)
+    }
+
+    override fun queryDiscountInfo() {
+        launch {
+            suspendQueryDiscountInfo()
+        }
+    }
+
+    override suspend fun suspendQueryDiscountInfo(): Rlt<Any> {
+        val rlt = orderHttpService.orderDiscount()
+        when (rlt) {
+            is Rlt.Failed -> {
+                //Ntd.
+                updateDisCountInfo(null)
+            }
+
+            is Rlt.Success -> {
+                updateDisCountInfo(rlt.data.data)
+            }
+        }
+        return rlt
+    }
+
+    private fun updateDisCountInfo(data: OrderDiscountData?) {
+        if (data == null) {
+            discountInfo = null
+            notifyDiscountChanged()
+            return
+        }
+        discountInfo = OrderDiscountInfo(
+            data.hasDiscount,
+            data.discountRate,
+            data.price
+        )
+        notifyDiscountChanged()
+    }
+
+    override fun getDiscountInfo(): OrderDiscountInfo? {
+        return discountInfo
+    }
+
+    override fun clear() {
+        super.clear()
+        discountInfo = null
+        notifyDiscountChanged()
+    }
+
+    private fun notifyDiscountChanged() {
+        discountLD.postValue(discountInfo)
+    }
+}

+ 14 - 0
module/order/src/main/java/com/adealink/weparty/order/manager/IDiscountManager.kt

@@ -0,0 +1,14 @@
+package com.adealink.weparty.order.manager
+
+import com.adealink.frame.base.Rlt
+import com.adealink.frame.frame.IBaseFrame
+import com.adealink.weparty.module.order.data.OrderDiscountInfo
+import com.adealink.weparty.module.order.listener.IDiscountListener
+
+interface IDiscountManager : IBaseFrame<IDiscountListener> {
+
+    fun queryDiscountInfo()
+    suspend fun suspendQueryDiscountInfo(): Rlt<Any>
+
+    fun getDiscountInfo(): OrderDiscountInfo?
+}

+ 40 - 4
module/order/src/main/java/com/adealink/weparty/order/qrcode/QRCodeOrderActivity.kt

@@ -18,16 +18,22 @@ import com.adealink.weparty.commonui.toast.util.showToast
 import com.adealink.weparty.commonui.widget.NumberIncreaseView
 import com.adealink.weparty.constant.ORDER_REMARK_MAX_LENGTH
 import com.adealink.weparty.module.order.Order
+import com.adealink.weparty.module.order.OrderModule
 import com.adealink.weparty.module.order.data.QRCodeOrder
 import com.adealink.weparty.module.order.data.QRCodeOrderDetail
+import com.adealink.weparty.module.order.data.discountLD
+import com.adealink.weparty.module.order.data.getFinalPrice
 import com.adealink.weparty.module.playmate.Playmate
 import com.adealink.weparty.order.R
 import com.adealink.weparty.order.databinding.ActivityQrcodeOrderBinding
 import com.adealink.weparty.order.viewmodel.OrderViewModelFactory
 import com.adealink.weparty.order.viewmodel.QRCodeOrderViewModel
 import com.adealink.weparty.util.applyImmersive
+import com.adealink.weparty.util.formatNumber
 import com.adealink.weparty.util.formatNumberStr
 import com.adealink.weparty.util.formatStar
+import kotlin.math.max
+import com.adealink.weparty.R as APP_R
 
 @RouterUri(
     path = [
@@ -75,8 +81,16 @@ class QRCodeOrderActivity : BaseActivity() {
         }
     }
 
+    override fun observeViewModel() {
+        super.observeViewModel()
+        discountLD.observe(this) {
+            updatePrice()
+        }
+    }
+
     override fun loadData() {
         super.loadData()
+        OrderModule.queryDiscountInfo()
         val qrCode = qrCode
         if (qrCode.isNullOrEmpty()) {
             showToast(R.string.order_qrcode_invalid)
@@ -123,10 +137,32 @@ class QRCodeOrderActivity : BaseActivity() {
             binding.vNumber.setNumber(orderCount)
             binding.tvNumber.gone()
         }
-        binding.vBottom.vPrice.setPrice(
-            (orderDetail?.price ?: 0f) * orderCount,
-            ""
-        )
+        updatePrice()
+    }
+
+    private fun updatePrice(){
+        val finalPrice = getFinalPrice(orderDetail?.price)
+        if (finalPrice.hasDiscount()) {
+            binding.clDiscount.show()
+            binding.vDiscount.text = getCompatString(
+                APP_R.string.common_price_discount_new_only,
+                formatNumber(finalPrice.discountPercent, true)
+            )
+            binding.vPriceDiscount.setDiscount(
+                -finalPrice.discount, null
+            )
+            //首单第一个免费
+            binding.vBottom.vPrice.setPrice(
+                finalPrice.discountPrice + (orderDetail?.price ?: 0f) * max(orderCount - 1, 0),
+                null
+            )
+        } else {
+            binding.clDiscount.gone()
+            binding.vBottom.vPrice.setPrice(
+                (orderDetail?.price ?: 0f) * orderCount,
+                null
+            )
+        }
     }
 
     private fun goCategoryId() {

+ 36 - 5
module/order/src/main/java/com/adealink/weparty/order/qrcode/QRCodeOrderDialog.kt

@@ -2,24 +2,31 @@ package com.adealink.weparty.order.qrcode
 
 import android.annotation.SuppressLint
 import androidx.fragment.app.viewModels
+import com.adealink.frame.aab.util.getCompatString
 import com.adealink.frame.base.Rlt
 import com.adealink.frame.base.fastLazy
 import com.adealink.frame.mvvm.view.viewBinding
 import com.adealink.frame.router.Router
 import com.adealink.frame.util.onClick
+import com.adealink.weparty.commonui.ext.gone
 import com.adealink.weparty.commonui.ext.show
 import com.adealink.weparty.commonui.toast.util.showToast
 import com.adealink.weparty.commonui.widget.BottomDialogFragment
 import com.adealink.weparty.module.im.IM
+import com.adealink.weparty.module.order.OrderModule
 import com.adealink.weparty.module.order.data.QRCodeOrder
 import com.adealink.weparty.module.order.data.QRCodeOrderDetail
+import com.adealink.weparty.module.order.data.getFinalPrice
 import com.adealink.weparty.module.wallet.WalletModule
 import com.adealink.weparty.module.wallet.data.Currency
 import com.adealink.weparty.order.R
 import com.adealink.weparty.order.databinding.DialogQrcodeCreateOrderBinding
 import com.adealink.weparty.order.viewmodel.OrderViewModelFactory
 import com.adealink.weparty.order.viewmodel.QRCodeOrderViewModel
+import com.adealink.weparty.util.formatNumber
 import com.adealink.weparty.util.formatNumberStr
+import kotlin.math.max
+import com.adealink.weparty.R as APP_R
 
 class QRCodeOrderDialog : BottomDialogFragment(R.layout.dialog_qrcode_create_order) {
 
@@ -51,20 +58,43 @@ class QRCodeOrderDialog : BottomDialogFragment(R.layout.dialog_qrcode_create_ord
         binding.tvCategory.text = orderData?.categoryName
         binding.ivAvatar.setImageUrl(orderData?.avatar)
         binding.tvName.text = orderData?.nickname
+
         binding.tvPrice.text =
             "${formatNumberStr((orderData?.price ?: 0f), false)}/${orderData?.unit ?: ""}"
-
         binding.tvCount.show()
         binding.tvCount.text = orderCount.toString()
 
-        binding.tvTotalDetail.text = formatNumberStr(
-            (orderData?.price ?: 0f) * (orderCount ?: 0), false
-        )
-
         binding.tvRemarkInput.setText(remark)
         binding.btnPay.onClick {
             pay()
         }
+        updatePrice()
+    }
+
+    private fun updatePrice(){
+
+        val finalPrice = getFinalPrice(orderData?.price)
+        if (finalPrice.hasDiscount()) {
+            binding.clDiscount.show()
+            binding.vDiscount.text = getCompatString(
+                APP_R.string.common_price_discount_new_only,
+                formatNumber(finalPrice.discountPercent, true)
+            )
+            binding.vPriceDiscount.setDiscount(
+                -finalPrice.discount, null
+            )
+            //首单第一个免费
+            binding.tvTotalDetail.text = formatNumberStr(
+                finalPrice.discountPrice +
+                        (orderData?.price ?: 0f) * max((orderCount ?: 0) - 1, 0), false
+            )
+
+        } else {
+            binding.clDiscount.gone()
+            binding.tvTotalDetail.text = formatNumberStr(
+                (orderData?.price ?: 0f) * (orderCount ?: 0), false
+            )
+        }
     }
 
     override fun observeViewModel() {
@@ -76,6 +106,7 @@ class QRCodeOrderDialog : BottomDialogFragment(R.layout.dialog_qrcode_create_ord
 
     override fun loadData() {
         super.loadData()
+        OrderModule.queryDiscountInfo()
         walletViewModel?.refreshWalletData()
     }
 

+ 1 - 0
module/order/src/main/java/com/adealink/weparty/order/refund/RefundActivity.kt

@@ -213,6 +213,7 @@ class RefundActivity : BaseActivity() {
             getOrderAllCost(
                 data?.order?.price ?: 0f,
                 data?.order?.purchaseQty ?: 0,
+                data?.order?.totalAmount,
                 12.sp(),
                 12.dp()
             )

+ 1 - 0
module/order/src/main/java/com/adealink/weparty/order/refund/comp/PlaymateRefundCustomerComp.kt

@@ -87,6 +87,7 @@ class PlaymateRefundCustomerComp(
         binding.tvTotalCostDetail.text = getOrderAllCost(
             data.order.price,
             data.order.purchaseQty,
+            data.order.totalAmount,
             12.sp(),
             12.dp()
         )

+ 12 - 3
module/order/src/main/java/com/adealink/weparty/order/util/UIUtil.kt

@@ -4,6 +4,8 @@ import android.os.Build
 import android.text.SpannableStringBuilder
 import android.text.style.AbsoluteSizeSpan
 import android.text.style.TypefaceSpan
+import androidx.annotation.IdRes
+import androidx.annotation.StringRes
 import com.adealink.frame.aab.util.getCompatDrawable
 import com.adealink.frame.aab.util.getCompatString
 import com.adealink.frame.ext.findAndSetSpan
@@ -14,9 +16,16 @@ import com.adealink.weparty.util.formatNumberStr
 import com.adealink.weparty.R as APP_R
 
 
-fun getOrderAllCost(price: Float, count: Int, priceTextSize: Int, iconSize: Int): SpannableStringBuilder {
-    val cost = formatNumberStr(price * count, omitted = false)
-    val text = getCompatString(R.string.order_all_cost, cost)
+fun getOrderAllCost(
+    price: Float,
+    count: Int,
+    totalCost: Float?,
+    priceTextSize: Int,
+    iconSize: Int,
+    @StringRes allCostTextId: Int = R.string.order_all_total_cost
+): SpannableStringBuilder {
+    val cost = formatNumberStr(totalCost ?: (price * count), omitted = false)
+    val text = getCompatString(allCostTextId, cost)
     return SpannableStringBuilder(text).apply {
         findAndSetSpan(
             CenterImageSpan(getCompatDrawable(APP_R.drawable.common_wallet_coin_ic).apply {

+ 2 - 0
module/order/src/main/java/com/adealink/weparty/order/viewmodel/OrderViewModel.kt

@@ -18,6 +18,7 @@ import com.adealink.weparty.module.order.data.QueryUnCompleteOrderReq
 import com.adealink.weparty.module.order.viewmodel.IOrderViewModel
 import com.adealink.weparty.order.data.CreateOrderReq
 import com.adealink.weparty.order.datasource.remote.OrderHttpService
+import com.adealink.weparty.order.manager.discountManager
 import kotlinx.coroutines.launch
 
 class OrderViewModel : BaseViewModel(), IOrderViewModel {
@@ -65,6 +66,7 @@ class OrderViewModel : BaseViewModel(), IOrderViewModel {
 
                 is Rlt.Success -> {
                     val data = rlt.data.data
+                    discountManager.queryDiscountInfo()
                     if (data == null) {
                         liveData.send(Rlt.Failed(CommonDataNullError()))
                     } else {

+ 2 - 0
module/order/src/main/java/com/adealink/weparty/order/viewmodel/UserOrderViewModel.kt

@@ -8,6 +8,7 @@ import com.adealink.frame.mvvm.livedata.OnceMutableLiveData
 import com.adealink.frame.mvvm.viewmodel.BaseViewModel
 import com.adealink.frame.network.data.PageReq
 import com.adealink.weparty.App
+import com.adealink.weparty.module.order.OrderModule
 import com.adealink.weparty.module.order.data.OrderAction
 import com.adealink.weparty.module.order.data.OrderDetailData
 import com.adealink.weparty.module.order.data.OrderDetailInfo
@@ -85,6 +86,7 @@ class UserOrderViewModel : BaseViewModel() {
                 }
 
                 is Rlt.Success -> {
+                    OrderModule.queryDiscountInfo()
                     val nextData = queryOrderDetail(data.orderId)
                     if (nextData != null) {
                         orderDetailLD.send(nextData)

+ 50 - 0
module/order/src/main/res/layout/activity_create_order.xml

@@ -220,6 +220,56 @@
 
                 </androidx.constraintlayout.widget.ConstraintLayout>
 
+                <androidx.constraintlayout.widget.ConstraintLayout
+                    android:id="@+id/cl_discount"
+                    android:layout_width="match_parent"
+                    android:layout_height="38dp"
+                    android:visibility="gone"
+                    app:layout_constraintTop_toBottomOf="@id/cl_count"
+                    tools:visibility="visible">
+
+                    <androidx.appcompat.widget.AppCompatTextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginHorizontal="8dp"
+                        android:ellipsize="end"
+                        android:fontFamily="@font/poppins_semibold"
+                        android:includeFontPadding="false"
+                        android:singleLine="true"
+                        android:text="@string/common_discount"
+                        android:textColor="@color/color_FF1D2129"
+                        android:textSize="12sp"
+                        app:layout_constrainedWidth="true"
+                        app:layout_constraintBottom_toBottomOf="parent"
+                        app:layout_constraintEnd_toStartOf="@id/v_price_discount"
+                        app:layout_constraintHorizontal_bias="0"
+                        app:layout_constraintStart_toStartOf="parent"
+                        app:layout_constraintTop_toTopOf="parent" />
+
+                    <androidx.appcompat.widget.AppCompatTextView
+                        android:id="@+id/v_discount"
+                        style="@style/CommonDiscountPriceView"
+                        android:layout_width="wrap_content"
+                        android:layout_marginEnd="4dp"
+                        app:layout_constraintBottom_toBottomOf="@id/v_price_discount"
+                        app:layout_constraintEnd_toStartOf="@id/v_price_discount"
+                        app:layout_constraintTop_toTopOf="@id/v_price_discount"
+                        tools:text="New Only | 90% OFF" />
+
+                    <com.adealink.weparty.module.playmate.widget.PriceView
+                        android:id="@+id/v_price_discount"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="4dp"
+                        app:layout_constraintBottom_toBottomOf="parent"
+                        app:layout_constraintEnd_toEndOf="parent"
+                        app:layout_constraintTop_toTopOf="parent"
+                        app:price_icon_size="16dp"
+                        app:price_text_bold="true"
+                        app:price_text_size="16sp" />
+
+                </androidx.constraintlayout.widget.ConstraintLayout>
+
             </androidx.constraintlayout.widget.ConstraintLayout>
 
             <androidx.constraintlayout.widget.ConstraintLayout

+ 67 - 1
module/order/src/main/res/layout/activity_order_detail.xml

@@ -318,6 +318,72 @@
 
                 </androidx.constraintlayout.widget.ConstraintLayout>
 
+                <!-- 折扣 -->
+                <androidx.constraintlayout.widget.ConstraintLayout
+                    android:id="@+id/cl_discount"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:minHeight="30dp"
+                    android:paddingVertical="6dp"
+                    app:layout_constraintTop_toBottomOf="@id/cl_order_count">
+
+                    <androidx.appcompat.widget.AppCompatTextView
+                        android:id="@+id/tv_order_discount"
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:includeFontPadding="false"
+                        android:text="@string/common_discount"
+                        android:textColor="@color/color_FF1D2129"
+                        android:textSize="12sp"
+                        app:layout_constraintBottom_toBottomOf="parent"
+                        app:layout_constraintEnd_toStartOf="@id/cl_order_discount_detail"
+                        app:layout_constraintHorizontal_weight="2"
+                        app:layout_constraintStart_toStartOf="parent"
+                        app:layout_constraintTop_toTopOf="parent" />
+
+                    <androidx.constraintlayout.widget.ConstraintLayout
+                        android:id="@+id/cl_order_discount_detail"
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        app:layout_constraintBottom_toBottomOf="parent"
+                        app:layout_constraintEnd_toEndOf="parent"
+                        app:layout_constraintHorizontal_bias="1"
+                        app:layout_constraintHorizontal_weight="3"
+                        app:layout_constraintStart_toEndOf="@id/tv_order_discount"
+                        app:layout_constraintTop_toTopOf="parent">
+
+                        <androidx.appcompat.widget.AppCompatImageView
+                            android:id="@+id/iv_order_discount_detail"
+                            android:layout_width="11dp"
+                            android:layout_height="11dp"
+                            app:layout_constraintBottom_toBottomOf="parent"
+                            app:layout_constraintEnd_toStartOf="@id/tv_order_discount_detail"
+                            app:layout_constraintHorizontal_bias="1"
+                            app:layout_constraintHorizontal_chainStyle="packed"
+                            app:layout_constraintStart_toStartOf="parent"
+                            app:layout_constraintTop_toTopOf="parent"
+                            app:srcCompat="@drawable/common_wallet_coin_ic" />
+
+                        <androidx.appcompat.widget.AppCompatTextView
+                            android:id="@+id/tv_order_discount_detail"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_marginStart="2dp"
+                            android:gravity="end"
+                            android:includeFontPadding="false"
+                            android:textColor="@color/color_FF1D2129"
+                            android:textSize="12sp"
+                            app:layout_constrainedWidth="true"
+                            app:layout_constraintBottom_toBottomOf="parent"
+                            app:layout_constraintEnd_toEndOf="parent"
+                            app:layout_constraintStart_toEndOf="@id/iv_order_discount_detail"
+                            app:layout_constraintTop_toTopOf="parent"
+                            tools:text="-1000" />
+
+                    </androidx.constraintlayout.widget.ConstraintLayout>
+
+                </androidx.constraintlayout.widget.ConstraintLayout>
+
                 <!-- 备注 -->
                 <androidx.constraintlayout.widget.ConstraintLayout
                     android:id="@+id/cl_order_remark"
@@ -325,7 +391,7 @@
                     android:layout_height="wrap_content"
                     android:minHeight="30dp"
                     android:paddingVertical="6dp"
-                    app:layout_constraintTop_toBottomOf="@id/cl_order_count">
+                    app:layout_constraintTop_toBottomOf="@id/cl_discount">
 
                     <androidx.appcompat.widget.AppCompatTextView
                         android:id="@+id/tv_order_remark"

+ 50 - 0
module/order/src/main/res/layout/activity_qrcode_order.xml

@@ -247,6 +247,56 @@
 
                 </androidx.constraintlayout.widget.ConstraintLayout>
 
+                <androidx.constraintlayout.widget.ConstraintLayout
+                    android:id="@+id/cl_discount"
+                    android:layout_width="match_parent"
+                    android:layout_height="38dp"
+                    android:visibility="gone"
+                    app:layout_constraintTop_toBottomOf="@id/cl_count"
+                    tools:visibility="visible">
+
+                    <androidx.appcompat.widget.AppCompatTextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginHorizontal="8dp"
+                        android:ellipsize="end"
+                        android:fontFamily="@font/poppins_semibold"
+                        android:includeFontPadding="false"
+                        android:singleLine="true"
+                        android:text="@string/common_discount"
+                        android:textColor="@color/color_FF1D2129"
+                        android:textSize="12sp"
+                        app:layout_constrainedWidth="true"
+                        app:layout_constraintBottom_toBottomOf="parent"
+                        app:layout_constraintEnd_toStartOf="@id/v_price_discount"
+                        app:layout_constraintHorizontal_bias="0"
+                        app:layout_constraintStart_toStartOf="parent"
+                        app:layout_constraintTop_toTopOf="parent" />
+
+                    <androidx.appcompat.widget.AppCompatTextView
+                        android:id="@+id/v_discount"
+                        style="@style/CommonDiscountPriceView"
+                        android:layout_width="wrap_content"
+                        android:layout_marginEnd="4dp"
+                        app:layout_constraintBottom_toBottomOf="@id/v_price_discount"
+                        app:layout_constraintEnd_toStartOf="@id/v_price_discount"
+                        app:layout_constraintTop_toTopOf="@id/v_price_discount"
+                        tools:text="New Only | 90% OFF" />
+
+                    <com.adealink.weparty.module.playmate.widget.PriceView
+                        android:id="@+id/v_price_discount"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="4dp"
+                        app:layout_constraintBottom_toBottomOf="parent"
+                        app:layout_constraintEnd_toEndOf="parent"
+                        app:layout_constraintTop_toTopOf="parent"
+                        app:price_icon_size="16dp"
+                        app:price_text_bold="true"
+                        app:price_text_size="16sp" />
+
+                </androidx.constraintlayout.widget.ConstraintLayout>
+
             </androidx.constraintlayout.widget.ConstraintLayout>
 
             <androidx.constraintlayout.widget.ConstraintLayout

+ 67 - 2
module/order/src/main/res/layout/dialog_create_order.xml

@@ -71,12 +71,29 @@
             android:paddingHorizontal="12dp"
             app:layout_constraintTop_toBottomOf="@id/tv_name">
 
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/tv_price_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:fontFamily="@font/poppins_semibold"
+                android:gravity="center"
+                android:includeFontPadding="false"
+                android:singleLine="true"
+                android:text="@string/order_product_price"
+                android:textColor="@color/color_FF1D2129"
+                android:textSize="14sp"
+                app:layout_constrainedWidth="true"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
             <androidx.appcompat.widget.AppCompatImageView
                 android:id="@+id/iv_coin"
                 android:layout_width="12dp"
                 android:layout_height="12dp"
+                android:layout_marginStart="6dp"
                 app:layout_constraintBottom_toBottomOf="parent"
-                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintStart_toEndOf="@id/tv_price_title"
                 app:layout_constraintTop_toTopOf="parent"
                 app:srcCompat="@drawable/common_wallet_coin_ic" />
 
@@ -119,12 +136,60 @@
 
         </androidx.constraintlayout.widget.ConstraintLayout>
 
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/cl_discount"
+            android:layout_width="match_parent"
+            android:layout_height="49dp"
+            android:layout_marginTop="13dp"
+            android:paddingHorizontal="12dp"
+            android:visibility="gone"
+            app:layout_constraintTop_toBottomOf="@id/cl_price"
+            tools:visibility="visible">
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/tv_discount_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:fontFamily="@font/poppins_semibold"
+                android:gravity="center"
+                android:includeFontPadding="false"
+                android:singleLine="true"
+                android:text="@string/common_discount"
+                android:textColor="@color/color_FF1D2129"
+                android:textSize="14sp"
+                app:layout_constrainedWidth="true"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/v_discount"
+                style="@style/CommonDiscountPriceView"
+                android:layout_width="wrap_content"
+                android:layout_marginStart="6dp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toEndOf="@id/tv_discount_title"
+                app:layout_constraintTop_toTopOf="parent"
+                tools:text="New Only | 90% OFF" />
+
+            <com.adealink.weparty.module.playmate.widget.PriceView
+                android:id="@+id/v_price_discount"
+                style="@style/CommonPriceView"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintTop_toTopOf="parent"
+                app:price_icon_size="16dp"
+                app:price_text_bold="true"
+                app:price_text_size="16sp" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
         <androidx.constraintlayout.widget.ConstraintLayout
             android:id="@+id/cl_total"
             android:layout_width="match_parent"
             android:layout_height="49dp"
             android:paddingHorizontal="12dp"
-            app:layout_constraintTop_toBottomOf="@id/cl_price">
+            app:layout_constraintTop_toBottomOf="@id/cl_discount">
 
             <androidx.appcompat.widget.AppCompatTextView
                 android:id="@+id/tv_total"

+ 67 - 2
module/order/src/main/res/layout/dialog_qrcode_create_order.xml

@@ -68,12 +68,29 @@
             android:paddingHorizontal="12dp"
             app:layout_constraintTop_toBottomOf="@id/tv_name">
 
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/tv_price_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:fontFamily="@font/poppins_semibold"
+                android:gravity="center"
+                android:includeFontPadding="false"
+                android:singleLine="true"
+                android:text="@string/order_product_price"
+                android:textColor="@color/color_FF1D2129"
+                android:textSize="14sp"
+                app:layout_constrainedWidth="true"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
             <androidx.appcompat.widget.AppCompatImageView
                 android:id="@+id/iv_coin"
                 android:layout_width="12dp"
                 android:layout_height="12dp"
+                android:layout_marginStart="6dp"
                 app:layout_constraintBottom_toBottomOf="parent"
-                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintStart_toEndOf="@id/tv_price_title"
                 app:layout_constraintTop_toTopOf="parent"
                 app:srcCompat="@drawable/common_wallet_coin_ic" />
 
@@ -107,12 +124,60 @@
 
         </androidx.constraintlayout.widget.ConstraintLayout>
 
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/cl_discount"
+            android:layout_width="match_parent"
+            android:layout_height="49dp"
+            android:layout_marginTop="13dp"
+            android:paddingHorizontal="12dp"
+            android:visibility="gone"
+            app:layout_constraintTop_toBottomOf="@id/cl_price"
+            tools:visibility="visible">
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/tv_discount_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:fontFamily="@font/poppins_semibold"
+                android:gravity="center"
+                android:includeFontPadding="false"
+                android:singleLine="true"
+                android:text="@string/common_discount"
+                android:textColor="@color/color_FF1D2129"
+                android:textSize="14sp"
+                app:layout_constrainedWidth="true"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/v_discount"
+                style="@style/CommonDiscountPriceView"
+                android:layout_width="wrap_content"
+                android:layout_marginStart="6dp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toEndOf="@id/tv_discount_title"
+                app:layout_constraintTop_toTopOf="parent"
+                tools:text="New Only | 90% OFF" />
+
+            <com.adealink.weparty.module.playmate.widget.PriceView
+                android:id="@+id/v_price_discount"
+                style="@style/CommonPriceView"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintTop_toTopOf="parent"
+                app:price_icon_size="16dp"
+                app:price_text_bold="true"
+                app:price_text_size="16sp" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
         <androidx.constraintlayout.widget.ConstraintLayout
             android:id="@+id/cl_total"
             android:layout_width="match_parent"
             android:layout_height="50dp"
             android:paddingHorizontal="12dp"
-            app:layout_constraintTop_toBottomOf="@id/cl_price">
+            app:layout_constraintTop_toBottomOf="@id/cl_discount">
 
             <androidx.appcompat.widget.AppCompatTextView
                 android:id="@+id/tv_total"

+ 13 - 1
module/order/src/main/res/layout/layout_create_order_category_item.xml

@@ -31,12 +31,24 @@
         android:textSize="15sp"
         app:layout_constrainedWidth="true"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/v_price"
+        app:layout_constraintEnd_toStartOf="@id/v_discount"
         app:layout_constraintHorizontal_bias="0"
         app:layout_constraintStart_toEndOf="@id/iv_icon"
         app:layout_constraintTop_toTopOf="parent"
         tools:text="Ludo King" />
 
+    <androidx.appcompat.widget.AppCompatTextView
+        android:id="@+id/v_discount"
+        style="@style/CommonDiscountPriceView"
+        android:layout_width="wrap_content"
+        android:layout_marginEnd="4dp"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="@id/v_price"
+        app:layout_constraintEnd_toStartOf="@id/v_price"
+        app:layout_constraintTop_toTopOf="@id/v_price"
+        tools:text="90% OFF"
+        tools:visibility="visible" />
+
     <com.adealink.weparty.module.playmate.widget.PriceView
         android:id="@+id/v_price"
         android:layout_width="wrap_content"

+ 8 - 5
module/order/src/main/res/layout/layout_playmate_order_list_item.xml

@@ -120,14 +120,17 @@
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintTop_toTopOf="parent">
 
-            <androidx.appcompat.widget.AppCompatImageView
-                android:layout_width="14dp"
-                android:layout_height="14dp"
-                android:layout_marginEnd="2dp"
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/v_discount"
+                style="@style/CommonDiscountPriceView"
+                android:layout_width="wrap_content"
+                android:layout_marginEnd="4dp"
+                android:text="@string/common_discount"
+                android:visibility="gone"
                 app:layout_constraintBottom_toBottomOf="@id/tv_price"
                 app:layout_constraintEnd_toStartOf="@id/tv_price"
                 app:layout_constraintTop_toTopOf="@id/tv_price"
-                app:srcCompat="@drawable/common_wallet_coin_ic" />
+                tools:visibility="visible" />
 
             <androidx.appcompat.widget.AppCompatTextView
                 android:id="@+id/tv_price"

+ 15 - 1
module/order/src/main/res/layout/layout_user_order_list_item.xml

@@ -235,11 +235,13 @@
             android:layout_width="wrap_content"
             android:layout_height="30dp"
             android:layout_marginEnd="8dp"
+            android:ellipsize="end"
             android:gravity="start|center_vertical"
             android:includeFontPadding="false"
             android:minWidth="80dp"
             android:paddingHorizontal="6dp"
             android:paddingVertical="3dp"
+            android:singleLine="true"
             android:text="@string/order_refunded_success"
             android:textColor="@color/color_FF86909C"
             android:textSize="11sp"
@@ -255,7 +257,19 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             app:barrierDirection="start"
-            app:constraint_referenced_ids="tv_order_all_cost" />
+            app:constraint_referenced_ids="v_discount,tv_order_all_cost" />
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/v_discount"
+            style="@style/CommonDiscountPriceView"
+            android:layout_width="wrap_content"
+            android:layout_marginEnd="4dp"
+            android:text="@string/common_discount"
+            android:visibility="gone"
+            app:layout_constraintBottom_toBottomOf="@id/tv_order_all_cost"
+            app:layout_constraintEnd_toStartOf="@id/tv_order_all_cost"
+            app:layout_constraintTop_toTopOf="@id/tv_order_all_cost"
+            tools:visibility="visible" />
 
         <androidx.appcompat.widget.AppCompatTextView
             android:id="@+id/tv_order_all_cost"

+ 2 - 1
module/order/src/main/res/values-in/strings.xml

@@ -10,7 +10,8 @@
     <string name="order_buy_count">Kuantitas</string>
     <string name="order_id">Nomor Pesanan</string>
     <string name="order_time">Waktu Pembelian</string>
-    <string name="order_all_cost">Total [currency] %s</string>
+    <string name="order_all_total_cost">Total [currency] %s</string>
+    <string name="order_all_cost">[currency] %s</string>
     <string name="order_evaluate_title">Apakah Anda puas dengan playmate ini?</string>
     <string name="order_evaluate_left_title">Keseluruhan</string>
     <string name="order_evaluate_right_title">Nilai</string>

+ 2 - 1
module/order/src/main/res/values-zh/strings.xml

@@ -10,7 +10,8 @@
     <string name="order_buy_count">购买数量</string>
     <string name="order_id">订单编号</string>
     <string name="order_time">购买时间</string>
-    <string name="order_all_cost">共计[currency]%s</string>
+    <string name="order_all_total_cost">共计[currency] %s</string>
+    <string name="order_all_cost">[currency]%s</string>
     <string name="order_evaluate_title">您对陪玩师满意吗?</string>
     <string name="order_evaluate_left_title">Overall</string>
     <string name="order_evaluate_right_title">Rate</string>

+ 2 - 1
module/order/src/main/res/values/strings.xml

@@ -10,7 +10,8 @@
     <string name="order_buy_count">Quantity</string>
     <string name="order_id">Order Number</string>
     <string name="order_time">Purchase Time</string>
-    <string name="order_all_cost">Total [currency] %s</string>
+    <string name="order_all_total_cost">Total [currency] %s</string>
+    <string name="order_all_cost">[currency] %s</string>
     <string name="order_evaluate_title">Are you satisfied with the playmate?</string>
     <string name="order_evaluate_left_title">Overall</string>
     <string name="order_evaluate_right_title">Rate</string>

+ 4 - 0
module/playmate/src/main/java/com/adealink/weparty/playmate/data/PlaymateListData.kt

@@ -108,6 +108,10 @@ data class PlaymateListData(
     @SerializedName("online") val online: Boolean, //是否在线
     @SerializedName("distance") val distance: Float, //距离(单位:km)
 
+    @GsonNullable
+    @SerializedName("playmateCreatedAt") val playmateCreatedAt: Long?, //成为陪玩师时间
+    @GsonNullable
+    @SerializedName("newcomer") val newcomer: Boolean?, //是否新人陪玩师
 )
 
 data class PlaymateListItemData(

+ 2 - 0
module/playmate/src/main/java/com/adealink/weparty/playmate/detail/PlaymateDetailActivity.kt

@@ -11,6 +11,7 @@ import com.adealink.frame.util.naviBarHeight
 import com.adealink.weparty.commonui.BaseActivity
 import com.adealink.weparty.commonui.ext.dp
 import com.adealink.weparty.commonui.toast.util.showFailedToast
+import com.adealink.weparty.module.order.OrderModule
 import com.adealink.weparty.module.playmate.Playmate
 import com.adealink.weparty.module.playmate.event.PlaymateStatEvent
 import com.adealink.weparty.module.profile.ProfileModule
@@ -61,6 +62,7 @@ class PlaymateDetailActivity : BaseActivity() {
 
     override fun loadData() {
         super.loadData()
+        OrderModule.queryDiscountInfo()
         detailViewModel.setPlaymate(playmateID, playmateName)
         detailViewModel.loadPlaymateDetail(playmateID)
     }

+ 29 - 4
module/playmate/src/main/java/com/adealink/weparty/playmate/detail/comp/PlaymateDetailBottomComp.kt

@@ -2,6 +2,7 @@ package com.adealink.weparty.playmate.detail.comp
 
 import androidx.lifecycle.LifecycleOwner
 import com.adealink.frame.aab.util.getCompatDimension
+import com.adealink.frame.aab.util.getCompatString
 import com.adealink.frame.mvvm.view.ViewComponent
 import com.adealink.frame.mvvm.viewmodel.activityViewModels
 import com.adealink.frame.router.Router
@@ -12,6 +13,8 @@ import com.adealink.weparty.commonui.ext.gone
 import com.adealink.weparty.commonui.ext.show
 import com.adealink.weparty.module.im.IM
 import com.adealink.weparty.module.order.Order
+import com.adealink.weparty.module.order.data.discountLD
+import com.adealink.weparty.module.order.data.getFinalPrice
 import com.adealink.weparty.module.playmate.Playmate
 import com.adealink.weparty.module.playmate.data.PlaymateDetailData
 import com.adealink.weparty.module.profile.ProfileModule
@@ -19,6 +22,8 @@ import com.adealink.weparty.playmate.databinding.LayoutPlaymateDetailBottomBindi
 import com.adealink.weparty.playmate.detail.dialog.PlaymateDetailSettingDialog
 import com.adealink.weparty.playmate.detail.viewmodel.PlaymateDetailViewModel
 import com.adealink.weparty.playmate.viewmodel.PlaymateViewModelFactory
+import com.adealink.weparty.util.formatNumber
+import com.adealink.weparty.R as APP_R
 
 class PlaymateDetailBottomComp(
     lifecycleOwner: LifecycleOwner,
@@ -26,6 +31,7 @@ class PlaymateDetailBottomComp(
 ) : ViewComponent(lifecycleOwner) {
 
     private val detailViewModel by activityViewModels<PlaymateDetailViewModel> { PlaymateViewModelFactory() }
+    private var data: PlaymateDetailData? = null
 
     override fun onCreate() {
         super.onCreate()
@@ -58,21 +64,26 @@ class PlaymateDetailBottomComp(
 
     private fun observeViewModel() {
         detailViewModel.detailLD.observe(viewLifecycleOwner) {
+            this@PlaymateDetailBottomComp.data = it
             if (it?.uid == ProfileModule.getMyUid()) {
                 binding.root.show()
                 binding.vSelf.root.show()
                 binding.vOther.root.gone()
-                updateSelfBottom(it)
+                updateSelfBottom()
                 return@observe
             }
             binding.root.show()
             binding.vSelf.root.gone()
             binding.vOther.root.show()
-            updateOtherBottom(it)
+            updateOtherBottom()
+        }
+        discountLD.observe(viewLifecycleOwner) {
+            updateOtherBottom()
         }
     }
 
-    private fun updateSelfBottom(data: PlaymateDetailData?) {
+    private fun updateSelfBottom() {
+        val data = data
         if (data == null) {
             binding.vSelf.vPrice.gone()
         } else {
@@ -81,13 +92,27 @@ class PlaymateDetailBottomComp(
         }
     }
 
-    private fun updateOtherBottom(data: PlaymateDetailData?) {
+    private fun updateOtherBottom() {
+        val data = data
+        val finalPrice = getFinalPrice(data?.price)
         if (data == null) {
             binding.vOther.vPrice.gone()
+            return
+        }
+        if (finalPrice.hasDiscount()) {
+            binding.vOther.vPriceDiscount.show()
+            binding.vOther.vPriceDiscount.text = getCompatString(
+                APP_R.string.common_price_discount_new_only,
+                formatNumber(finalPrice.discountPercent, true)
+            )
+            binding.vOther.vPrice.show()
+            binding.vOther.vPrice.setPrice(finalPrice.discountPrice, data.unit)
         } else {
+            binding.vOther.vPriceDiscount.gone()
             binding.vOther.vPrice.show()
             binding.vOther.vPrice.setPrice(data.price, data.unit)
         }
+
     }
 
     private fun createOrder() {

+ 1 - 1
module/playmate/src/main/java/com/adealink/weparty/playmate/list/GuestPlaymateListFragment.kt

@@ -120,7 +120,7 @@ class GuestPlaymateListFragment : BaseFragment(R.layout.fragment_playmate_list),
             }
         })
 
-        listAdapter.register(PlaymateListItemViewBinder(this))
+        listAdapter.register(PlaymateListItemViewBinder(this, this))
         binding.vList.adapter = listAdapter
         binding.vList.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
         binding.vList.addItemDecoration(VerticalSpaceItemDecoration(10.dp()))

+ 2 - 0
module/playmate/src/main/java/com/adealink/weparty/playmate/list/PlaymateHomeFragment.kt

@@ -13,6 +13,7 @@ import com.adealink.weparty.commonui.ext.gone
 import com.adealink.weparty.commonui.ext.show
 import com.adealink.weparty.commonui.toast.util.showFailedToast
 import com.adealink.weparty.databinding.LayoutHomeTabBinding
+import com.adealink.weparty.module.order.OrderModule
 import com.adealink.weparty.module.playmate.Playmate
 import com.adealink.weparty.playmate.R
 import com.adealink.weparty.playmate.databinding.FragmentPlaymateHomeBinding
@@ -53,6 +54,7 @@ class PlaymateHomeFragment : BaseFragment(R.layout.fragment_playmate_home) {
     }
 
     override fun loadData() {
+        OrderModule.queryDiscountInfo()
         playmateViewModel.pullAllCategory()
     }
 

+ 1 - 1
module/playmate/src/main/java/com/adealink/weparty/playmate/list/PlaymateListFragment.kt

@@ -163,7 +163,7 @@ class PlaymateListFragment : BaseFragment(R.layout.fragment_playmate_list),
             }
         })
 
-        listAdapter.register(PlaymateListItemViewBinder(this))
+        listAdapter.register(PlaymateListItemViewBinder(this, this))
         binding.vList.adapter = listAdapter
         binding.vList.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
         binding.vList.addItemDecoration(

+ 143 - 69
module/playmate/src/main/java/com/adealink/weparty/playmate/list/adapter/PlaymateListItemViewBinder.kt

@@ -2,103 +2,177 @@ package com.adealink.weparty.playmate.list.adapter
 
 import android.view.LayoutInflater
 import android.view.ViewGroup
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.Observer
 import com.adealink.frame.aab.util.getCompatString
+import com.adealink.frame.log.Log
 import com.adealink.frame.util.onClick
 import com.adealink.weparty.commonui.ext.gone
 import com.adealink.weparty.commonui.ext.hide
+import com.adealink.weparty.commonui.ext.isViewValid
 import com.adealink.weparty.commonui.ext.show
 import com.adealink.weparty.commonui.recycleview.adapter.BindingViewHolder
 import com.adealink.weparty.commonui.recycleview.adapter.multitype.ItemViewBinder
+import com.adealink.weparty.module.order.data.OrderDiscountInfo
+import com.adealink.weparty.module.order.data.getFinalPrice
+import com.adealink.weparty.module.order.data.observeDiscount
+import com.adealink.weparty.module.order.data.unObserveDiscount
 import com.adealink.weparty.playmate.R
 import com.adealink.weparty.playmate.data.PlaymateListData
 import com.adealink.weparty.playmate.data.PlaymateListItemData
 import com.adealink.weparty.playmate.databinding.ItemPlaymateHomeListBinding
 import com.adealink.weparty.util.formatDistanceStr
+import com.adealink.weparty.util.formatNumber
 import com.adealink.weparty.util.formatStar
+import com.adealink.weparty.R as APP_R
 
-class PlaymateListItemViewBinder(val listener: OnPlaymateListListener) :
-    ItemViewBinder<PlaymateListItemData, BindingViewHolder<ItemPlaymateHomeListBinding>>() {
+class PlaymateListItemViewBinder(
+    val lifecycleOwner: LifecycleOwner,
+    val listener: OnPlaymateListListener
+) : ItemViewBinder<PlaymateListItemData, PlaymateListItemViewBinder.ViewHolder>() {
 
-    override fun onBindViewHolder(
-        holder: BindingViewHolder<ItemPlaymateHomeListBinding>,
-        item: PlaymateListItemData,
-    ) {
-        holder.binding.root.onClick {
-            listener.goCategoryDetail(item.data)
-        }
-
-        holder.binding.ivAvatar.setImageUrl(item.data.avatar)
-        if (item.data.voice.isEmpty()) {
-            holder.binding.vVoice.gone()
-            holder.binding.vVoice.setSoundUrl(null)
-        } else {
-            holder.binding.vVoice.show()
-            holder.binding.vVoice.setSoundUrl(item.data.voice)
-        }
-        if (item.data.online) {
-            holder.binding.vOnline.show()
-            holder.binding.vOnline.onStart()
-        } else {
-            holder.binding.vOnline.gone()
-            holder.binding.vOnline.onStop()
-        }
-
-        holder.binding.tvName.text = item.data.nickname
-        holder.binding.vGender.setAge(item.data.age)
-        holder.binding.vGender.setGender(item.data.gender)
-        holder.binding.tvLocation.text = formatDistanceStr(item.data.distance)
-        holder.binding.tvStar.text = formatStar(item.data.star)
-        holder.binding.tvComment.text =
-            getCompatString(R.string.playmate_comment_count, item.data.commentCount.toString())
-        holder.binding.ivCategory.setImageUrl(item.data.categoryIcon)
-        holder.binding.tvCategory.text = item.data.categoryName
-        if (item.data.summary.isEmpty()) {
-            holder.binding.tvSummary.gone()
-        } else {
-            holder.binding.tvSummary.show()
-            holder.binding.tvSummary.text = item.data.summary
-        }
-        item.data.photos.getOrNull(0)?.takeIf { it.isNotEmpty() }?.let {
-            holder.binding.ivPhoto1.show()
-            holder.binding.ivPhoto1.setImageUrl(it)
-        } ?: let {
-            holder.binding.ivPhoto1.hide()
-        }
-
-        item.data.photos.getOrNull(1)?.takeIf { it.isNotEmpty() }?.let {
-            holder.binding.ivPhoto2.show()
-            holder.binding.ivPhoto2.setImageUrl(it)
-        } ?: let {
-            holder.binding.ivPhoto2.hide()
-        }
-
-        item.data.photos.getOrNull(2)?.takeIf { it.isNotEmpty() }?.let {
-            holder.binding.ivPhoto3.show()
-            holder.binding.ivPhoto3.setImageUrl(it)
-        } ?: let {
-            holder.binding.ivPhoto3.hide()
-        }
-
-        holder.binding.vPrice.setPrice(item.data.price, item.data.unit)
-    }
 
     override fun onCreateViewHolder(
         inflater: LayoutInflater,
         parent: ViewGroup,
-    ): BindingViewHolder<ItemPlaymateHomeListBinding> {
-        return BindingViewHolder(ItemPlaymateHomeListBinding.inflate(inflater, parent, false))
+    ): ViewHolder {
+        return ViewHolder(ItemPlaymateHomeListBinding.inflate(inflater, parent, false)).also {
+            it.initView()
+        }
     }
 
-    override fun onViewDetachedFromWindow(holder: BindingViewHolder<ItemPlaymateHomeListBinding>) {
+    override fun onViewDetachedFromWindow(holder: ViewHolder) {
         super.onViewDetachedFromWindow(holder)
         holder.binding.vVoice.stopPlay()
         holder.binding.vOnline.onStop()
     }
 
-    override fun onViewRecycled(holder: BindingViewHolder<ItemPlaymateHomeListBinding>) {
+    override fun onViewAttachedToWindow(holder: ViewHolder) {
+        super.onViewAttachedToWindow(holder)
+        observeDiscount(lifecycleOwner, holder.discountObserver)
+    }
+
+    override fun onViewRecycled(holder: ViewHolder) {
+        super.onViewRecycled(holder)
         holder.binding.vVoice.stopPlay()
         holder.binding.vOnline.onStop()
-        super.onViewRecycled(holder)
+        unObserveDiscount(holder.discountObserver)
+    }
+
+    override fun onBindViewHolder(
+        holder: ViewHolder,
+        item: PlaymateListItemData
+    ) {
+        holder.update(item)
+    }
+
+    inner class ViewHolder(
+        rootBinding: ItemPlaymateHomeListBinding,
+    ) : BindingViewHolder<ItemPlaymateHomeListBinding>(rootBinding) {
+        private var item: PlaymateListItemData? = null
+
+        fun initView() {
+            binding.root.onClick {
+                val item = item ?: return@onClick
+                listener.goCategoryDetail(item.data)
+            }
+        }
+
+        fun update(item: PlaymateListItemData) {
+            this.item = item
+            binding.ivAvatar.setImageUrl(item.data.avatar)
+            if (item.data.voice.isEmpty()) {
+                binding.vVoice.gone()
+                binding.vVoice.setSoundUrl(null)
+            } else {
+                binding.vVoice.show()
+                binding.vVoice.setSoundUrl(item.data.voice)
+            }
+            if (item.data.online) {
+                binding.vOnline.show()
+                binding.vOnline.onStart()
+            } else {
+                binding.vOnline.gone()
+                binding.vOnline.onStop()
+            }
+
+            binding.tvName.text = item.data.nickname
+            binding.vGender.setAge(item.data.age)
+            binding.vGender.setGender(item.data.gender)
+            binding.tvLocation.text = formatDistanceStr(item.data.distance)
+
+            if (item.data.newcomer == true) {
+                binding.clNew.show()
+                binding.clComment.gone()
+            } else {
+                binding.clNew.gone()
+                binding.clComment.show()
+                binding.tvStar.text = formatStar(item.data.star)
+                binding.tvComment.text =
+                    getCompatString(
+                        R.string.playmate_comment_count,
+                        item.data.commentCount.toString()
+                    )
+            }
+
+            binding.ivCategory.setImageUrl(item.data.categoryIcon)
+            binding.tvCategory.text = item.data.categoryName
+            if (item.data.summary.isEmpty()) {
+                binding.tvSummary.gone()
+            } else {
+                binding.tvSummary.show()
+                binding.tvSummary.text = item.data.summary
+            }
+            item.data.photos.getOrNull(0)?.takeIf { it.isNotEmpty() }?.let {
+                binding.ivPhoto1.show()
+                binding.ivPhoto1.setImageUrl(it)
+            } ?: let {
+                binding.ivPhoto1.hide()
+            }
+
+            item.data.photos.getOrNull(1)?.takeIf { it.isNotEmpty() }?.let {
+                binding.ivPhoto2.show()
+                binding.ivPhoto2.setImageUrl(it)
+            } ?: let {
+                binding.ivPhoto2.hide()
+            }
+
+            item.data.photos.getOrNull(2)?.takeIf { it.isNotEmpty() }?.let {
+                binding.ivPhoto3.show()
+                binding.ivPhoto3.setImageUrl(it)
+            } ?: let {
+                binding.ivPhoto3.hide()
+            }
+
+            notifyPriceChanged()
+        }
+
+        val discountObserver = object : Observer<OrderDiscountInfo?> {
+
+            override fun onChanged(value: OrderDiscountInfo?) {
+                notifyPriceChanged()
+            }
+        }
+
+        private fun notifyPriceChanged() {
+            if (!this@ViewHolder.isViewValid()) {
+                return
+            }
+            val item = this@ViewHolder.item
+            val finalPrice = getFinalPrice(item?.data?.price ?: 0f)
+            if (finalPrice.hasDiscount()) {
+                //折扣价
+                binding.vPrice.setDiscount(finalPrice.discountPrice, item?.data?.unit)
+                binding.vPriceDiscount.show()
+                binding.vPriceDiscount.text = getCompatString(
+                    APP_R.string.common_price_discount,
+                    formatNumber(finalPrice.discountPercent, true)
+                )
+            } else {
+                binding.vPrice.setPrice(item?.data?.price ?: 0f, item?.data?.unit)
+                binding.vPriceDiscount.gone()
+            }
+        }
     }
 
     interface OnPlaymateListListener {

+ 1 - 0
module/playmate/src/main/java/com/adealink/weparty/playmate/list/viewmodel/PlaymateListViewModel.kt

@@ -6,6 +6,7 @@ import com.adealink.frame.mvvm.livedata.ExtMutableLiveData
 import com.adealink.frame.mvvm.viewmodel.BaseViewModel
 import com.adealink.frame.network.data.PageReq
 import com.adealink.weparty.App
+import com.adealink.weparty.module.order.OrderModule
 import com.adealink.weparty.network.data.NoMoreDataError
 import com.adealink.weparty.playmate.data.PlaymateListData
 import com.adealink.weparty.playmate.data.PlaymateListOtherOptions

+ 72 - 31
module/playmate/src/main/res/layout/item_playmate_home_list.xml

@@ -56,6 +56,17 @@
         app:price_text_size="14sp"
         app:unit_text_size="12sp" />
 
+    <androidx.appcompat.widget.AppCompatTextView
+        android:id="@+id/v_price_discount"
+        style="@style/CommonDiscountPriceUpView"
+        android:layout_width="wrap_content"
+        android:visibility="gone"
+        app:layout_constraintEnd_toEndOf="@id/v_price"
+        app:layout_constraintStart_toStartOf="@id/v_price"
+        app:layout_constraintTop_toBottomOf="@id/v_price"
+        tools:text="90% OFF"
+        tools:visibility="visible" />
+
     <androidx.constraintlayout.widget.Barrier
         android:id="@+id/v_barrier_start"
         android:layout_width="wrap_content"
@@ -126,43 +137,73 @@
     </androidx.constraintlayout.widget.ConstraintLayout>
 
     <!--评分-->
-    <androidx.appcompat.widget.AppCompatImageView
-        android:id="@+id/iv_star"
-        android:layout_width="14dp"
-        android:layout_height="14dp"
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/cl_comment"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         android:layout_marginStart="12dp"
-        android:layout_marginTop="5dp"
+        android:layout_marginTop="3dp"
         app:layout_constraintStart_toEndOf="@id/v_barrier_start"
-        app:layout_constraintTop_toBottomOf="@id/tv_name"
-        app:srcCompat="@drawable/common_evaluate_star_selected_ic" />
+        app:layout_constraintTop_toBottomOf="@id/tv_name">
 
-    <androidx.appcompat.widget.AppCompatTextView
-        android:id="@+id/tv_star"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="3dp"
-        android:includeFontPadding="false"
-        android:singleLine="true"
-        android:textColor="@color/color_FF1D2129"
-        android:textSize="12sp"
-        android:textStyle="bold"
-        app:layout_constraintBottom_toBottomOf="@id/iv_star"
-        app:layout_constraintStart_toEndOf="@id/iv_star"
-        app:layout_constraintTop_toTopOf="@id/iv_star"
-        tools:text="4.2" />
+        <androidx.appcompat.widget.AppCompatImageView
+            android:id="@+id/iv_star"
+            android:layout_width="14dp"
+            android:layout_height="14dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:srcCompat="@drawable/common_evaluate_star_selected_ic" />
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/tv_star"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="3dp"
+            android:includeFontPadding="false"
+            android:singleLine="true"
+            android:textColor="@color/color_FF1D2129"
+            android:textSize="12sp"
+            android:textStyle="bold"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toEndOf="@id/iv_star"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:text="4.2" />
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/tv_comment"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="3dp"
+            android:includeFontPadding="false"
+            android:singleLine="true"
+            android:textColor="@color/color_FF86909C"
+            android:textSize="11sp"
+            app:layout_constraintBaseline_toBaselineOf="@id/tv_star"
+            app:layout_constraintStart_toEndOf="@id/tv_star"
+            tools:text="(234评价)" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
 
     <androidx.appcompat.widget.AppCompatTextView
-        android:id="@+id/tv_comment"
+        android:id="@+id/cl_new"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="3dp"
+        android:layout_height="15dp"
+        android:layout_marginStart="12dp"
+        android:layout_marginTop="3dp"
+        android:background="@drawable/common_new_playmate_tag_bg"
+        android:gravity="center"
         android:includeFontPadding="false"
+        android:paddingStart="6dp"
+        android:paddingEnd="14dp"
         android:singleLine="true"
-        android:textColor="@color/color_FF86909C"
+        android:text="@string/common_newbie"
+        android:textColor="@color/color_FFDE9D2E"
         android:textSize="11sp"
-        app:layout_constraintBaseline_toBaselineOf="@id/tv_star"
-        app:layout_constraintStart_toEndOf="@id/tv_star"
-        tools:text="(234评价)" />
+        android:visibility="gone"
+        app:layout_constraintStart_toEndOf="@id/v_barrier_start"
+        app:layout_constraintTop_toBottomOf="@id/cl_comment"
+        tools:visibility="visible" />
 
     <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/cl_category"
@@ -171,10 +212,10 @@
         android:layout_marginTop="3dp"
         android:background="@drawable/playmate_list_item_category_bg"
         android:paddingVertical="2dp"
-        android:paddingStart="2dp"
+        android:paddingStart="12dp"
         android:paddingEnd="6dp"
-        app:layout_constraintStart_toStartOf="@id/iv_star"
-        app:layout_constraintTop_toBottomOf="@id/iv_star">
+        app:layout_constraintStart_toEndOf="@id/v_barrier_start"
+        app:layout_constraintTop_toBottomOf="@id/cl_new">
 
         <com.adealink.frame.image.view.NetworkImageView
             android:id="@+id/iv_category"

+ 3 - 0
module/playmate/src/main/res/layout/layout_playmate_detail_bottom.xml

@@ -5,6 +5,7 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:background="@drawable/common_bottom_white_mask_bg"
+    android:clipChildren="false"
     android:paddingHorizontal="16dp"
     android:paddingTop="16dp"
     tools:paddingBottom="36dp">
@@ -14,6 +15,7 @@
         layout="@layout/layout_playmate_detail_other_bottom"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:clipChildren="false"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent" />
@@ -23,6 +25,7 @@
         layout="@layout/layout_playmate_detail_self_bottom"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:clipChildren="false"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@id/v_other" />

+ 19 - 4
module/playmate/src/main/res/layout/layout_playmate_detail_other_bottom.xml

@@ -1,20 +1,35 @@
 <?xml version="1.0" encoding="utf-8"?>
 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    android:clipChildren="false">
 
     <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/cl_order"
         android:layout_width="0dp"
         android:layout_height="@dimen/playmate_detail_bottom_bar_height"
-        android:layout_marginTop="16dp"
+        android:layout_marginTop="40dp"
         android:layout_marginEnd="10dp"
+        android:clipChildren="false"
+        app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toStartOf="@id/btn_chat"
         app:layout_constraintHorizontal_bias="0"
         app:layout_constraintStart_toStartOf="parent">
 
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/v_price_discount"
+            style="@style/CommonDiscountPriceDownView"
+            android:layout_width="wrap_content"
+            android:visibility="gone"
+            app:layout_constraintBottom_toTopOf="@id/cl_price"
+            app:layout_constraintEnd_toEndOf="@id/cl_price"
+            app:layout_constraintStart_toStartOf="@id/cl_price"
+            tools:text="New Only | 90% OFF"
+            tools:visibility="visible" />
+
         <androidx.constraintlayout.widget.ConstraintLayout
             android:id="@+id/cl_price"
             android:layout_width="0dp"
@@ -82,9 +97,9 @@
         app:button_left_drawable_margin="4dp"
         app:button_left_drawable_width="24dp"
         app:common_button_type="normal2"
-        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintBottom_toBottomOf="@id/cl_order"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintTop_toTopOf="@id/cl_order"
         app:text="@string/common_chat"
         app:textColor="@color/white"
         app:textSize="16sp" />

+ 2 - 0
module/profile/src/main/res/values-zh/strings.xml

@@ -60,6 +60,8 @@
     <string name="profile_edit_talent_voice_desc">用声音介绍你自己</string>
     <string name="profile_edit_nickname_empty">昵称不能为空</string>
     <string name="profile_edit_talent_voice_title">设置声音</string>
+    <string name="profile_edit_talent_voice_record">点击开始录音</string>
+    <string name="profile_edit_talent_voice_stop">点击结束录音</string>
     <string name="profile_edit_talent_voice_delete_record">删除并重新录制</string>
     <string name="profile_follow_list_title">%s 关注</string>
     <string name="profile_fans_list_title">%s 粉丝</string>