diff --git a/BaseModule/src/main/java/com/xscm/moduleutil/bean/DecorateDetailBean.kt b/BaseModule/src/main/java/com/xscm/moduleutil/bean/DecorateDetailBean.kt new file mode 100644 index 00000000..a55bfe6e --- /dev/null +++ b/BaseModule/src/main/java/com/xscm/moduleutil/bean/DecorateDetailBean.kt @@ -0,0 +1,174 @@ +package com.xscm.moduleutil.bean + +import com.google.gson.annotations.SerializedName +import java.util.ArrayList + +/** + * 项目名称:羽声语音 + * 时间:2026/1/4 10:22 + * 用途:装扮价格详情 + */ +class DecorateDetailBean { + // 用户信息(服务端返回的user_info字段) + @SerializedName("user_info") + var userInfo: UserInfoDecorate? = UserInfoDecorate() + + // 装饰商品核心信息(服务端返回的decorate字段) + @SerializedName("decorate") + var decorate: Decorate? = null + + /** + * 用户信息内部类(对应服务端user_info) + */ + class UserInfoDecorate { + @SerializedName("user_id") + var userId: Int = 0 + + @SerializedName("user_coin") + var userCoin: String = "" + } + + /** + * 装饰商品核心信息(对应服务端decorate,直接解析服务端返回数据) + * 注意:与之前适配器的Item模型解耦,该类仅负责接收服务端数据,不承担适配器布局类型职责 + */ + class Decorate { + @SerializedName("title") + var title: String = "" // 商品名称(如“粉色花头”) + @SerializedName("price") + var price : String ="" + @SerializedName("base_image") + var base_image : String ="" + @SerializedName("price_list") + var priceList: List = ArrayList() // 价格/时长列表(服务端返回数组) + } + + /** + * 价格/时长明细bean(对应服务端price_list中的单个对象) + * 可直接解析服务端返回的每个价格项数据,同时适配适配器的PriceItem模型 + */ + class PriceListBean { + @SerializedName("price") + var price: String = "" // 现价 + + @SerializedName("original_price") + var originalPrice: String = "" // 原价 + + @SerializedName("discount") + var discount: String = "" // 折扣(如“5.0”) + + @SerializedName("day") + var day: Int = 0 // 有效天数 + + @SerializedName("month") + var month: String = "" // 有效月数 + + @SerializedName("end_time") + var endTime: String = "" // 有效期截止时间 + } + + // ---------------------- 适配器适配相关:转换方法 + 适配器所需模型 ---------------------- + /** + * 适配器的Item数据模型(密封类,区分单行/多选项) + * 与服务端数据模型解耦,专门用于RecyclerView适配器布局 + */ + sealed class DecorateAdapterItem { + // 单行信息类型(如“商品价格”“有效期至”) + data class SingleItem( + val label: String, // 左侧标签文字 + val content: String // 右侧内容文字 + ) : DecorateAdapterItem() + + // 购买时长多选项类型(承载所有时长选项) + data class MultiOptionItem( + val options: List // 直接复用PriceListBean(已适配服务端数据) + ) : DecorateAdapterItem() + + // type=12专用:购买次数(加减按钮) + data class BuyCountItem( + val initialCount: Int, + val unitPrice: String + ) : DecorateAdapterItem() + } + + /** + * 转换方法:将服务端数据(Decorate)转换为适配器所需数据列表(List) + * 实现服务端数据与适配器的桥接,方便适配器直接使用 + * @param defaultSelectedPos 默认选中的时长选项下标(默认0,即第一个选项) + */ + fun convertToAdapterData( + decorate: Decorate?, + defaultSelectedPos: Int = 0 + ): List { + val adapterDataList = mutableListOf() + if (decorate == null ) { + return adapterDataList + } + + if ( decorate.priceList.isEmpty()){ +// ---------- type=12:解析单个字段(无price_list),新增购买次数、商品总价 ---------- + val unitPrice = decorate.price// 直接取Decorate的singlePrice(服务端返回的单价) + val unitPriceStr = unitPrice // 格式化单价,避免小数异常 + + + // 2. 商品单价(单行项,取decorate.singlePrice) + adapterDataList.add( + DecorateAdapterItem.SingleItem( + label = "商品单价", + content = unitPriceStr + ) + ) + + // 3. 购买次数(type=12专用,初始数量1,传入单价用于计算总价) + adapterDataList.add( + DecorateAdapterItem.BuyCountItem( + initialCount = 1, + unitPrice = unitPrice + ) + ) + + // 4. 商品总价(单行项,初始:单价×1) + adapterDataList.add( + DecorateAdapterItem.SingleItem( + label = "商品总价", + content = unitPriceStr // 初始总价=单价×1 + ) + ) + + }else { + + // 安全获取默认选中项(防止下标越界) + val selectedPos = if (defaultSelectedPos in decorate.priceList.indices) { + defaultSelectedPos + } else { + 0 + } + val selectedPriceItem = decorate.priceList[selectedPos] + + // 1. 添加“商品价格”单行项(取选中项的现价) + adapterDataList.add( + DecorateAdapterItem.SingleItem( + label = "商品价格", + content = "${selectedPriceItem.price}" // 拼接货币符号,优化展示 + ) + ) + + // 2. 添加“有效期至”单行项(取选中项的截止时间) + adapterDataList.add( + DecorateAdapterItem.SingleItem( + label = "有效期至", + content = selectedPriceItem.endTime + ) + ) + + // 3. 添加“购买时长”多选项(承载所有价格/时长列表) + adapterDataList.add( + DecorateAdapterItem.MultiOptionItem( + options = decorate.priceList + ) + ) + + } + return adapterDataList + } +} \ No newline at end of file diff --git a/BaseModule/src/main/java/com/xscm/moduleutil/bean/PersonaltyListBean.kt b/BaseModule/src/main/java/com/xscm/moduleutil/bean/PersonaltyListBean.kt index 53edcfa0..8355aabe 100644 --- a/BaseModule/src/main/java/com/xscm/moduleutil/bean/PersonaltyListBean.kt +++ b/BaseModule/src/main/java/com/xscm/moduleutil/bean/PersonaltyListBean.kt @@ -14,7 +14,7 @@ class PersonaltyListBean { var price: Int = 0 // 实际价格(金币) var special_num: Int = 0 // 靓号 var original_price: Int = 0 // 原价 - var discount: Int = 0 // 折扣 + var discount: Double = 0.0 // 折扣 var discount_str: String = "" // 折扣字段 diff --git a/BaseModule/src/main/java/com/xscm/moduleutil/http/ApiServer.java b/BaseModule/src/main/java/com/xscm/moduleutil/http/ApiServer.java index 7bba1a05..e9f4645a 100644 --- a/BaseModule/src/main/java/com/xscm/moduleutil/http/ApiServer.java +++ b/BaseModule/src/main/java/com/xscm/moduleutil/http/ApiServer.java @@ -13,7 +13,6 @@ import com.xscm.moduleutil.widget.Constants; import java.util.List; import io.reactivex.Observable; -import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.Field; @@ -470,6 +469,13 @@ public interface ApiServer { @GET(Constants.GET_DECORATE) Call>> getDecorateList(@Query("type") String type); + @GET(Constants.GET_DECORATE_DETAIL) + Call> getDecorateDetail(@Query("did") String id); + + @FormUrlEncoded + @POST(Constants.POST_PAY_DECORATE) + Call> payDecorate(@Field("did") String id, @Field("day") String day,@Field("num") String num); + @FormUrlEncoded @POST(Constants.POST_GZ) Call> userGuanz(@Field("user_id") String userId, @Field("type") String type); diff --git a/BaseModule/src/main/java/com/xscm/moduleutil/http/RetrofitClient.java b/BaseModule/src/main/java/com/xscm/moduleutil/http/RetrofitClient.java index d38cb173..a7c7d677 100644 --- a/BaseModule/src/main/java/com/xscm/moduleutil/http/RetrofitClient.java +++ b/BaseModule/src/main/java/com/xscm/moduleutil/http/RetrofitClient.java @@ -41,7 +41,6 @@ import java.security.SecureRandom; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; -import java.util.EventListener; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -2721,6 +2720,53 @@ public class RetrofitClient { }); } + public void payDecorate(String did, String day, String num, BaseObserver observer) { + sApiServer.payDecorate(did, day, num).enqueue(new Callback>() { + + @Override + public void onResponse(Call> call, Response> response) { + onNextRetu(response, observer); + } + + @Override + public void onFailure(Call> call, Throwable t) { + LogUtils.e("payDecorate", t.getMessage()); + } + }); + + } + + public void getDecorateDetail(String did, BaseObserver observer) { + sApiServer.getDecorateDetail(did).enqueue(new Callback>() { + + @Override + public void onResponse(Call> call, Response> response) { + if (response.code() == 200) { + BaseModel listBaseModel = response.body(); + if (listBaseModel.getCode() == 1) { + observer.onNext(listBaseModel.getData()); + } else if (listBaseModel.getCode() == 301) { + setCode301(listBaseModel.getMsg()); + } else if (listBaseModel.getCode() == 0) { + observer.onNext(new DecorateDetailBean()); + ToastUtils.showShort(listBaseModel.getMsg()); + } + + } else { + observer.onNext(new DecorateDetailBean()); + ToastUtils.showLong("请求装饰详情错误", response.code()); + LogUtils.e("getDecorateDetail", response.message()); + } + } + + @Override + public void onFailure(Call> call, Throwable t) { + LogUtils.e("getDecorateDetail", t.getMessage()); + } + }); + + } + public void getDecorateList(String type, BaseObserver> observer) { sApiServer.getDecorateList(type).enqueue(new Callback>>() { @Override diff --git a/BaseModule/src/main/java/com/xscm/moduleutil/widget/AmountView.java b/BaseModule/src/main/java/com/xscm/moduleutil/widget/AmountView.java new file mode 100644 index 00000000..cef36625 --- /dev/null +++ b/BaseModule/src/main/java/com/xscm/moduleutil/widget/AmountView.java @@ -0,0 +1,131 @@ +package com.xscm.moduleutil.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; + +import com.xscm.moduleutil.R; + +/** + * 项目名称:羽声语音 + * 时间:2026/1/4 16:09 + * 用途:自定义组件:购买数量,带减少增加按钮 + */ +public class AmountView extends LinearLayout implements View.OnClickListener, TextWatcher { + + private static final String TAG = "AmountView"; + private int amount = 1; //购买数量 + private int goods_storage = Integer.MAX_VALUE; //商品库存 + + private OnAmountChangeListener mListener; + + private EditText etAmount; + private Button btnDecrease; + private Button btnIncrease; + + public AmountView(Context context) { + this(context, null); + } + + public AmountView(Context context, AttributeSet attrs) { + super(context, attrs); + + LayoutInflater.from(context).inflate(R.layout.view_amount, this); + etAmount = (EditText) findViewById(R.id.etAmount); + btnDecrease = (Button) findViewById(R.id.btnDecrease); + btnIncrease = (Button) findViewById(R.id.btnIncrease); + btnDecrease.setOnClickListener(this); + btnIncrease.setOnClickListener(this); + etAmount.addTextChangedListener(this); + + TypedArray obtainStyledAttributes = getContext().obtainStyledAttributes(attrs, R.styleable.AmountView); + int btnWidth = obtainStyledAttributes.getDimensionPixelSize(R.styleable.AmountView_btnWidth, LayoutParams.WRAP_CONTENT); + int tvWidth = obtainStyledAttributes.getDimensionPixelSize(R.styleable.AmountView_tvWidth, 80); + int tvTextSize = obtainStyledAttributes.getDimensionPixelSize(R.styleable.AmountView_tvTextSize, 0); + int btnTextSize = obtainStyledAttributes.getDimensionPixelSize(R.styleable.AmountView_btnTextSize, 0); + obtainStyledAttributes.recycle(); + + LayoutParams btnParams = new LayoutParams(btnWidth, LayoutParams.MATCH_PARENT); + btnDecrease.setLayoutParams(btnParams); + btnIncrease.setLayoutParams(btnParams); + if (btnTextSize != 0) { + btnDecrease.setTextSize(TypedValue.COMPLEX_UNIT_PX, btnTextSize); + btnIncrease.setTextSize(TypedValue.COMPLEX_UNIT_PX, btnTextSize); + } + + LayoutParams textParams = new LayoutParams(tvWidth, LayoutParams.MATCH_PARENT); + etAmount.setLayoutParams(textParams); + if (tvTextSize != 0) { + etAmount.setTextSize(tvTextSize); + } + } + + public void setOnAmountChangeListener(OnAmountChangeListener onAmountChangeListener) { + this.mListener = onAmountChangeListener; + } + + public void setGoods_storage(int goods_storage) { + this.goods_storage = goods_storage; + } + + @Override + public void onClick(View v) { + int i = v.getId(); + if (i == R.id.btnDecrease) { + if (amount > 1) { + amount--; + etAmount.setText(amount + ""); + } + } else if (i == R.id.btnIncrease) { + if (amount < goods_storage) { + amount++; + etAmount.setText(amount + ""); + } + } + + etAmount.clearFocus(); + + if (mListener != null) { + mListener.onAmountChange(this, amount); + } + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + if (s.toString().isEmpty()) + return; + amount = Integer.valueOf(s.toString()); + if (amount > goods_storage) { + etAmount.setText(goods_storage + ""); + return; + } + + if (mListener != null) { + mListener.onAmountChange(this, amount); + } + } + + + public interface OnAmountChangeListener { + void onAmountChange(View view, int amount); + } + +} \ No newline at end of file diff --git a/BaseModule/src/main/java/com/xscm/moduleutil/widget/Constants.java b/BaseModule/src/main/java/com/xscm/moduleutil/widget/Constants.java index 1caf0b77..6df28040 100644 --- a/BaseModule/src/main/java/com/xscm/moduleutil/widget/Constants.java +++ b/BaseModule/src/main/java/com/xscm/moduleutil/widget/Constants.java @@ -259,6 +259,8 @@ public class Constants { public static final String LIKE_ALBUM = "/api/User/like_album";//相册点赞 public static final String GET_PERSONALTY = "/api/Decorate/get_type_list";//装扮类型列表 public static final String GET_DECORATE = "/api/Decorate/user_decorate";//装扮详情 + public static final String GET_DECORATE_DETAIL = "/api/Decorate/get_decorate_detail";//装饰价格详情 + public static final String POST_PAY_DECORATE = "/api/Decorate/pay_decorate";//购买装扮 public static final String SET_USER_DECORATE = "/api/Decorate/set_user_decorate";//用户装扮 public static final String JOIN_ROOM = "/api/Room/join_room";//加入房间 public static final String BEFORE_JOIN_ROOM_CHECK = "/api/Room/before_join_room_check";//加入房间前检查 diff --git a/BaseModule/src/main/res/drawable/bg_amount_layout.xml b/BaseModule/src/main/res/drawable/bg_amount_layout.xml new file mode 100644 index 00000000..e686cf04 --- /dev/null +++ b/BaseModule/src/main/res/drawable/bg_amount_layout.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/BaseModule/src/main/res/drawable/btn_amount.xml b/BaseModule/src/main/res/drawable/btn_amount.xml new file mode 100644 index 00000000..9059d647 --- /dev/null +++ b/BaseModule/src/main/res/drawable/btn_amount.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/BaseModule/src/main/res/drawable/divider.xml b/BaseModule/src/main/res/drawable/divider.xml new file mode 100644 index 00000000..06387897 --- /dev/null +++ b/BaseModule/src/main/res/drawable/divider.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/BaseModule/src/main/res/layout/view_amount.xml b/BaseModule/src/main/res/layout/view_amount.xml new file mode 100644 index 00000000..de5b36b8 --- /dev/null +++ b/BaseModule/src/main/res/layout/view_amount.xml @@ -0,0 +1,39 @@ + + + +