use serde::Deserialize;
#[derive(Debug, Clone, Deserialize)]
pub struct VipConvertResponse {
#[serde(default, rename = "returnCode")]
pub return_code: Option<String>,
#[serde(default)]
pub result: Option<VipConvertResult>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VipConvertResult {
#[serde(default, rename = "urlInfoList")]
pub url_info_list: Option<Vec<VipUrlInfo>>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VipUrlInfo {
#[serde(default)]
pub url: Option<String>,
#[serde(default, rename = "longUrl")]
pub long_url: Option<String>,
#[serde(default, rename = "noEvokeUrl")]
pub no_evoke_url: Option<String>,
#[serde(default, rename = "noEvokeLongUrl")]
pub no_evoke_long_url: Option<String>,
#[serde(default, rename = "vipQuickAppUrl")]
pub vip_quick_app_url: Option<String>,
#[serde(default, rename = "vipWxUrl")]
pub vip_wx_url: Option<String>,
#[serde(default, rename = "deeplinkUrl")]
pub deeplink_url: Option<String>,
#[serde(default, rename = "ulUrl")]
pub ul_url: Option<String>,
#[serde(default)]
pub source: Option<String>,
#[serde(default, rename = "traFrom")]
pub tra_from: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VipAuthorizeResponse {
#[serde(default)]
pub status: Option<i32>,
#[serde(default)]
pub url: Option<String>,
#[serde(default)]
pub msg: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VipAuthorizeListResponse {
#[serde(default)]
pub status: Option<i32>,
#[serde(default)]
pub data: Option<Vec<VipAuthorizeInfo>>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VipAuthorizeInfo {
#[serde(default)]
pub sid: Option<String>,
#[serde(default)]
pub account: Option<String>,
#[serde(default)]
pub user_id: Option<String>,
#[serde(default)]
pub user_nick: Option<String>,
#[serde(default)]
pub auth_time: Option<String>,
#[serde(default)]
pub expire_time: Option<String>,
#[serde(default)]
pub access_token: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VipOrderResponse {
#[serde(default, rename = "returnCode")]
pub return_code: Option<String>,
#[serde(default)]
pub result: Option<VipOrderResult>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VipOrderResult {
#[serde(default, rename = "orderInfoList")]
pub order_info_list: Option<Vec<VipOrderInfo>>,
#[serde(default)]
pub total: Option<i64>,
#[serde(default)]
pub page: Option<i32>,
#[serde(default, rename = "pageSize")]
pub page_size: Option<i32>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VipOrderInfo {
#[serde(default, rename = "orderSn")]
pub order_sn: Option<String>,
#[serde(default, rename = "goodsId")]
pub goods_id: Option<String>,
#[serde(default, rename = "goodsName")]
pub goods_name: Option<String>,
#[serde(default, rename = "goodsThumb")]
pub goods_thumb: Option<String>,
#[serde(default, rename = "goodsCount")]
pub goods_count: Option<i32>,
#[serde(default, rename = "goodsPrice")]
pub goods_price: Option<String>,
#[serde(default, rename = "orderAmount")]
pub order_amount: Option<String>,
#[serde(default)]
pub commission: Option<String>,
#[serde(default, rename = "commissionRate")]
pub commission_rate: Option<String>,
#[serde(default)]
pub status: Option<i32>,
#[serde(default, rename = "statusName")]
pub status_name: Option<String>,
#[serde(default, rename = "orderTime")]
pub order_time: Option<String>,
#[serde(default, rename = "updateTime")]
pub update_time: Option<String>,
#[serde(default, rename = "chanTag")]
pub chan_tag: Option<String>,
#[serde(default, rename = "statParam")]
pub stat_param: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VipGoodsDetailResponse {
#[serde(default, rename = "returnCode")]
pub return_code: Option<String>,
#[serde(default)]
pub result: Option<Vec<VipGoodsDetail>>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VipGoodsDetail {
#[serde(default, rename = "goodsId")]
pub goods_id: Option<String>,
#[serde(default, rename = "goodsName")]
pub goods_name: Option<String>,
#[serde(default, rename = "marketPrice")]
pub market_price: Option<String>,
#[serde(default, rename = "vipPrice")]
pub vip_price: Option<String>,
#[serde(default)]
pub discount: Option<String>,
#[serde(default, rename = "commissionRate")]
pub commission_rate: Option<String>,
#[serde(default)]
pub commission: Option<String>,
#[serde(default, rename = "goodsMainPicture")]
pub goods_main_picture: Option<String>,
#[serde(default, rename = "goodsThumbUrl")]
pub goods_thumb_url: Option<String>,
#[serde(default, rename = "goodsCarouselPictures")]
pub goods_carousel_pictures: Option<Vec<String>>,
#[serde(default, rename = "goodsDetailPictures")]
pub goods_detail_pictures: Option<Vec<String>>,
#[serde(default, rename = "destUrl")]
pub dest_url: Option<String>,
#[serde(default, rename = "brandName")]
pub brand_name: Option<String>,
#[serde(default, rename = "brandId")]
pub brand_id: Option<i64>,
#[serde(default, rename = "brandLogoFull")]
pub brand_logo_full: Option<String>,
#[serde(default, rename = "brandStoreSn")]
pub brand_store_sn: Option<String>,
#[serde(default, rename = "cat1stId")]
pub cat1st_id: Option<i64>,
#[serde(default, rename = "cat1stName")]
pub cat1st_name: Option<String>,
#[serde(default, rename = "cat2ndId")]
pub cat2nd_id: Option<i64>,
#[serde(default, rename = "cat2ndName")]
pub cat2nd_name: Option<String>,
#[serde(default, rename = "categoryId")]
pub category_id: Option<i64>,
#[serde(default, rename = "categoryName")]
pub category_name: Option<String>,
#[serde(default, rename = "haiTao")]
pub hai_tao: Option<i32>,
#[serde(default, rename = "sourceType")]
pub source_type: Option<i32>,
#[serde(default)]
pub status: Option<i32>,
#[serde(default, rename = "spuId")]
pub spu_id: Option<String>,
#[serde(default, rename = "sellTimeFrom")]
pub sell_time_from: Option<i64>,
#[serde(default, rename = "sellTimeTo")]
pub sell_time_to: Option<i64>,
#[serde(default, rename = "schemeStartTime")]
pub scheme_start_time: Option<i64>,
#[serde(default, rename = "schemeEndTime")]
pub scheme_end_time: Option<i64>,
#[serde(default, rename = "storeInfo")]
pub store_info: Option<VipStoreInfo>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VipStoreInfo {
#[serde(default, rename = "storeId")]
pub store_id: Option<String>,
#[serde(default, rename = "storeName")]
pub store_name: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VipSearchGoodsResponse {
#[serde(default, rename = "returnCode")]
pub return_code: Option<String>,
#[serde(default)]
pub result: Option<VipSearchResult>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VipSearchResult {
#[serde(default)]
pub total: Option<i64>,
#[serde(default, rename = "goodsInfoList")]
pub goods_info_list: Option<Vec<VipGoodsInfo>>,
#[serde(default)]
pub page: Option<i32>,
#[serde(default, rename = "pageSize")]
pub page_size: Option<i32>,
#[serde(default, rename = "sortFields")]
pub sort_fields: Option<Vec<VipSortField>>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VipGoodsInfo {
#[serde(default, rename = "goodsId")]
pub goods_id: Option<String>,
#[serde(default, rename = "goodsName")]
pub goods_name: Option<String>,
#[serde(default, rename = "marketPrice")]
pub market_price: Option<String>,
#[serde(default, rename = "vipPrice")]
pub vip_price: Option<String>,
#[serde(default)]
pub discount: Option<String>,
#[serde(default, rename = "commissionRate")]
pub commission_rate: Option<String>,
#[serde(default)]
pub commission: Option<String>,
#[serde(default, rename = "goodsMainPicture")]
pub goods_main_picture: Option<String>,
#[serde(default, rename = "goodsThumbUrl")]
pub goods_thumb_url: Option<String>,
#[serde(default, rename = "destUrl")]
pub dest_url: Option<String>,
#[serde(default, rename = "brandName")]
pub brand_name: Option<String>,
#[serde(default, rename = "brandId")]
pub brand_id: Option<i64>,
#[serde(default, rename = "brandLogoFull")]
pub brand_logo_full: Option<String>,
#[serde(default, rename = "brandStoreSn")]
pub brand_store_sn: Option<String>,
#[serde(default, rename = "cat1stId")]
pub cat1st_id: Option<i64>,
#[serde(default, rename = "cat1stName")]
pub cat1st_name: Option<String>,
#[serde(default, rename = "cat2ndId")]
pub cat2nd_id: Option<i64>,
#[serde(default, rename = "cat2ndName")]
pub cat2nd_name: Option<String>,
#[serde(default, rename = "categoryId")]
pub category_id: Option<i64>,
#[serde(default, rename = "categoryName")]
pub category_name: Option<String>,
#[serde(default, rename = "haiTao")]
pub hai_tao: Option<i32>,
#[serde(default, rename = "sourceType")]
pub source_type: Option<i32>,
#[serde(default)]
pub status: Option<i32>,
#[serde(default, rename = "spuId")]
pub spu_id: Option<String>,
#[serde(default, rename = "sellTimeFrom")]
pub sell_time_from: Option<i64>,
#[serde(default, rename = "sellTimeTo")]
pub sell_time_to: Option<i64>,
#[serde(default, rename = "schemeStartTime")]
pub scheme_start_time: Option<i64>,
#[serde(default, rename = "schemeEndTime")]
pub scheme_end_time: Option<i64>,
#[serde(default, rename = "storeInfo")]
pub store_info: Option<VipStoreInfo>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VipSortField {
#[serde(default, rename = "fieldName")]
pub field_name: Option<String>,
#[serde(default, rename = "fieldDesc")]
pub field_desc: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vip_convert_response_deserialize() {
let json = r#"{
"returnCode": "0",
"result": {
"urlInfoList": [
{
"url": "https://t.vip.com/mlCYWuKuVNA",
"longUrl": "https://t.vip.com/redirect.php?url=xxx",
"noEvokeUrl": "https://t.vip.com/mlCYWuKuVNA?wq=1",
"vipWxUrl": "pages/productDetail/productDetail?goodsId=123",
"deeplinkUrl": "vipshop://showGoodsDetail?pid=123",
"source": "https://m.vip.com/product-123.html"
}
]
}
}"#;
let response: VipConvertResponse = serde_json::from_str(json).unwrap();
assert_eq!(response.return_code, Some("0".to_string()));
assert!(response.result.is_some());
let result = response.result.unwrap();
assert!(result.url_info_list.is_some());
let urls = result.url_info_list.unwrap();
assert_eq!(urls.len(), 1);
assert_eq!(
urls[0].url,
Some("https://t.vip.com/mlCYWuKuVNA".to_string())
);
}
#[test]
fn test_vip_authorize_list_response_deserialize() {
let json = r#"{
"status": 200,
"data": [
{
"sid": "123",
"account": "test@example.com",
"user_id": "456",
"auth_time": "2023/11/12 15:55:49",
"expire_time": "2024/11/12 15:55:49",
"access_token": "token123"
}
]
}"#;
let response: VipAuthorizeListResponse = serde_json::from_str(json).unwrap();
assert_eq!(response.status, Some(200));
assert!(response.data.is_some());
let data = response.data.unwrap();
assert_eq!(data.len(), 1);
assert_eq!(data[0].sid, Some("123".to_string()));
}
#[test]
fn test_vip_order_response_deserialize() {
let json = r#"{
"returnCode": "0",
"result": {
"orderInfoList": [
{
"orderSn": "123456789",
"goodsId": "6919173790682972930",
"goodsName": "测试商品",
"status": 2,
"orderAmount": "99.00",
"commission": "4.95"
}
],
"total": 1,
"page": 1,
"pageSize": 20
}
}"#;
let response: VipOrderResponse = serde_json::from_str(json).unwrap();
assert_eq!(response.return_code, Some("0".to_string()));
assert!(response.result.is_some());
let result = response.result.unwrap();
assert_eq!(result.total, Some(1));
assert!(result.order_info_list.is_some());
let orders = result.order_info_list.unwrap();
assert_eq!(orders.len(), 1);
assert_eq!(orders[0].order_sn, Some("123456789".to_string()));
}
#[test]
fn test_vip_goods_detail_response_deserialize() {
let json = r#"{
"returnCode": "0",
"result": [
{
"goodsId": "6919173790682972930",
"goodsName": "测试商品",
"marketPrice": "539.00",
"vipPrice": "179.00",
"discount": "0.33",
"commissionRate": "5",
"commission": "8.95",
"brandName": "测试品牌",
"cat1stName": "男装",
"status": 1
}
]
}"#;
let response: VipGoodsDetailResponse = serde_json::from_str(json).unwrap();
assert_eq!(response.return_code, Some("0".to_string()));
assert!(response.result.is_some());
let result = response.result.unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].goods_id, Some("6919173790682972930".to_string()));
assert_eq!(result[0].market_price, Some("539.00".to_string()));
}
#[test]
fn test_vip_search_goods_response_deserialize() {
let json = r#"{
"returnCode": "0",
"result": {
"total": 3250,
"goodsInfoList": [
{
"goodsId": "6918901004295698011",
"goodsName": "测试球鞋",
"marketPrice": "339.00",
"vipPrice": "179.00",
"discount": "0.68",
"commissionRate": "3",
"commission": "5.37",
"brandName": "乔丹",
"cat1stName": "运动户外",
"status": 1
}
],
"page": 1,
"pageSize": 20,
"sortFields": [
{"fieldName": "PRICE", "fieldDesc": "价格排序"},
{"fieldName": "DISCOUNT", "fieldDesc": "折扣排序"}
]
}
}"#;
let response: VipSearchGoodsResponse = serde_json::from_str(json).unwrap();
assert_eq!(response.return_code, Some("0".to_string()));
assert!(response.result.is_some());
let result = response.result.unwrap();
assert_eq!(result.total, Some(3250));
assert!(result.goods_info_list.is_some());
let goods = result.goods_info_list.unwrap();
assert_eq!(goods.len(), 1);
assert_eq!(goods[0].goods_name, Some("测试球鞋".to_string()));
}
#[test]
fn test_response_with_missing_fields() {
let json = r#"{"returnCode": "0"}"#;
let response: VipConvertResponse = serde_json::from_str(json).unwrap();
assert_eq!(response.return_code, Some("0".to_string()));
assert!(response.result.is_none());
}
}