Skip to main content

fishpi_sdk/model/
redpacket.rs

1use std::str::FromStr;
2
3use serde_json::Value;
4
5use crate::{impl_str_enum, utils::error::Error};
6
7/// 猜拳类型
8#[derive(Clone, Debug)]
9#[repr(u8)]
10pub enum GestureType {
11    /// 石
12    Rock = 0,
13    /// 剪刀
14    Scissors = 1,
15    /// 布
16    Paper = 2,
17}
18
19/// 红包类型
20#[derive(Clone, Debug)]
21pub enum RedPacketType {
22    /// 拼手气
23    Random,
24    /// 平分
25    Average,
26    /// 专属
27    Specify,
28    /// 心跳
29    Heartbeat,
30    /// 猜拳
31    RockPaperScissors,
32}
33
34/// 红包数据
35#[derive(Clone, Debug)]
36pub struct RedPacket {
37    /// 红包类型
38    pub r#type: RedPacketType,
39    /// 红包积分
40    pub money: u32,
41    /// 红包个数
42    pub count: u32,
43    /// 祝福语
44    pub msg: String,
45    /// 接收者, 专属红包有效
46    pub recivers: Vec<String>,
47    /// 出拳, 猜拳红包有效
48    pub gesture: Option<GestureType>,
49}
50
51/// 红包领取者信息
52#[derive(Clone, Debug)]
53#[allow(non_snake_case)]
54pub struct RedPacketGot {
55    /// 用户ID
56    pub userId: String,
57    /// 用户名
58    pub userName: String,
59    /// 用户头像
60    pub avatar: String,
61    /// 领取到的积分
62    pub userMoney: u32,
63    /// 领取时间
64    pub time: String,
65}
66
67/// 红包历史信息
68#[derive(Clone, Debug)]
69#[allow(non_snake_case)]
70pub struct RedPacketMessage {
71    /// 消息类型,固定为redPacket
72    pub msgType: String,
73    /// 红包数
74    pub count: u32,
75    /// 领取数
76    pub got: u32,
77    /// 内含积分
78    pub money: u32,
79    /// 祝福语
80    pub msg: String,
81    /// 发送者ID
82    pub senderId: String,
83    /// 出拳,猜拳红包有效
84    pub GestureType: Option<GestureType>,
85    /// 接收者,专属红包有效
86    pub recivers: Vec<String>,
87    /// 已领取者列表
88    pub who: Vec<RedPacketGot>,
89}
90
91/// 红包基本信息
92#[derive(Clone, Debug)]
93#[allow(non_snake_case)]
94pub struct RedPacketBase {
95    /// 数量
96    pub count: u32,
97    /// 猜拳类型
98    pub gesture: Option<GestureType>,
99    /// 领取数
100    pub got: u32,
101    /// 祝福语
102    pub msg: String,
103    /// 发送者用户名
104    pub userName: String,
105    /// 用户头像
106    pub userAvatarURL: String,
107}
108
109/// 红包信息
110#[derive(Clone, Debug)]
111pub struct RedPacketInfo {
112    pub info: RedPacketBase,
113    pub recivers: Vec<String>,
114    pub who: Vec<RedPacketGot>,
115}
116
117fn parse_string_list(data: &Value, primary_key: &str, fallback_key: &str) -> Vec<String> {
118    data.get(primary_key)
119        .or_else(|| data.get(fallback_key))
120        .and_then(|v| v.as_array())
121        .map(|arr| {
122            arr.iter()
123                .filter_map(|v| v.as_str().map(ToString::to_string))
124                .collect()
125        })
126        .unwrap_or_default()
127}
128
129fn parse_gesture(
130    data: &Value,
131    primary_key: &str,
132    fallback_key: &str,
133    err_ctx: &str,
134) -> Result<Option<GestureType>, Error> {
135    let gesture = data
136        .get(primary_key)
137        .and_then(|v| v.as_str())
138        .or_else(|| data.get(fallback_key).and_then(|v| v.as_str()));
139
140    match gesture {
141        Some(gesture_str) => GestureType::from_str(gesture_str)
142            .map(Some)
143            .map_err(|_| Error::Parse(format!("Invalid gesture in {}", err_ctx))),
144        None => Ok(None),
145    }
146}
147
148fn parse_who_list(data: &Value) -> Result<Vec<RedPacketGot>, Error> {
149    let Some(who_array) = data.get("who").and_then(|v| v.as_array()) else {
150        return Ok(Vec::new());
151    };
152
153    let mut got_list = Vec::with_capacity(who_array.len());
154    for item in who_array {
155        let user_money = item
156            .get("userMoney")
157            .or_else(|| item.get("money"))
158            .and_then(|v| {
159                v.as_u64()
160                    .or_else(|| v.as_i64().and_then(|n| if n >= 0 { Some(n as u64) } else { None }))
161                    .or_else(|| v.as_str().and_then(|s| s.parse::<u64>().ok()))
162            })
163            .unwrap_or(0) as u32;
164        got_list.push(RedPacketGot {
165            userId: item["userId"]
166                .as_str()
167                .ok_or_else(|| Error::Parse("Missing userId in who".to_string()))?
168                .to_string(),
169            userName: item["userName"]
170                .as_str()
171                .ok_or_else(|| Error::Parse("Missing userName in who".to_string()))?
172                .to_string(),
173            avatar: item["avatar"]
174                .as_str()
175                .ok_or_else(|| Error::Parse("Missing avatar in who".to_string()))?
176                .to_string(),
177            userMoney: user_money,
178            time: item["time"]
179                .as_str()
180                .ok_or_else(|| Error::Parse("Missing time in who".to_string()))?
181                .to_string(),
182        });
183    }
184
185    Ok(got_list)
186}
187
188/// 红包状态信息
189#[derive(Clone, Debug)]
190#[allow(non_snake_case)]
191pub struct RedPacketStatusMsg {
192    pub oId: String,
193    pub count: u32,
194    pub got: u32,
195    pub whoGive: String,
196    pub whoGot: Vec<String>,
197    pub avatarURL20: String,
198    pub avatarURL48: String,
199    pub avatarURL210: String,
200}
201
202impl RedPacketStatusMsg {
203    pub fn from_value(data: &Value) -> Result<Self, Error> {
204        Ok(RedPacketStatusMsg {
205            oId: data["oId"]
206                .as_str()
207                .ok_or_else(|| Error::Parse("Missing oId in RedPacketStatusMsg".to_string()))?
208                .to_string(),
209            count: data["count"].as_u64().ok_or_else(|| {
210                Error::Parse("Missing or invalid count in RedPacketStatusMsg".to_string())
211            })? as u32,
212            got: data["got"].as_u64().ok_or_else(|| {
213                Error::Parse("Missing or invalid got in RedPacketStatusMsg".to_string())
214            })? as u32,
215            whoGive: data["whoGive"]
216                .as_str()
217                .ok_or_else(|| Error::Parse("Missing whoGive in RedPacketStatusMsg".to_string()))?
218                .to_string(),
219            whoGot: if let Some(who_got_array) = data["whoGot"].as_array() {
220                who_got_array
221                    .iter()
222                    .filter_map(|v| v.as_str().map(|s| s.to_string()))
223                    .collect()
224            } else {
225                data["whoGot"]
226                    .as_str()
227                    .map(|s| vec![s.to_string()])
228                    .unwrap_or_default()
229            },
230            avatarURL20: data["userAvatarURL20"]
231                .as_str()
232                .ok_or_else(|| {
233                    Error::Parse("Missing userAvatarURL20 in RedPacketStatusMsg".to_string())
234                })?
235                .to_string(),
236            avatarURL48: data["userAvatarURL48"]
237                .as_str()
238                .ok_or_else(|| {
239                    Error::Parse("Missing userAvatarURL48 in RedPacketStatusMsg".to_string())
240                })?
241                .to_string(),
242            avatarURL210: data["userAvatarURL210"]
243                .as_str()
244                .ok_or_else(|| {
245                    Error::Parse("Missing userAvatarURL210 in RedPacketStatusMsg".to_string())
246                })?
247                .to_string(),
248        })
249    }
250}
251
252impl Default for RedPacket {
253    fn default() -> Self {
254        RedPacket {
255            r#type: RedPacketType::Random,
256            money: 32,
257            count: 1,
258            msg: "摸鱼者, 事竟成!".to_string(),
259            recivers: Vec::new(),
260            gesture: None,
261        }
262    }
263}
264
265impl RedPacket {
266    pub fn from_value(data: &Value) -> Result<Self, Error> {
267        Ok(RedPacket {
268            r#type: RedPacketType::from_str(
269                data["type"]
270                    .as_str()
271                    .ok_or_else(|| Error::Parse("Missing type in RedPacket".to_string()))?,
272            )
273            .map_err(|_| Error::Parse("Invalid type in RedPacket".to_string()))?,
274            money: data["money"]
275                .as_u64()
276                .ok_or_else(|| Error::Parse("Missing or invalid money in RedPacket".to_string()))?
277                as u32,
278            count: data["count"]
279                .as_u64()
280                .ok_or_else(|| Error::Parse("Missing or invalid count in RedPacket".to_string()))?
281                as u32,
282            msg: data["msg"]
283                .as_str()
284                .ok_or_else(|| Error::Parse("Missing msg in RedPacket".to_string()))?
285                .to_string(),
286            recivers: parse_string_list(data, "recivers", "receivers"),
287            gesture: parse_gesture(data, "gesture", "GestureType", "RedPacket")?,
288        })
289    }
290}
291
292impl RedPacketMessage {
293    pub fn from_value(data: &Value) -> Result<Self, Error> {
294        Ok(RedPacketMessage {
295            msgType: data["msgType"]
296                .as_str()
297                .ok_or_else(|| Error::Parse("Missing msgType in RedPacketMessage".to_string()))?
298                .to_string(),
299            count: data["count"].as_u64().ok_or_else(|| {
300                Error::Parse("Missing or invalid count in RedPacketMessage".to_string())
301            })? as u32,
302            got: data["got"].as_u64().ok_or_else(|| {
303                Error::Parse("Missing or invalid got in RedPacketMessage".to_string())
304            })? as u32,
305            money: data["money"].as_u64().ok_or_else(|| {
306                Error::Parse("Missing or invalid money in RedPacketMessage".to_string())
307            })? as u32,
308            msg: data["msg"]
309                .as_str()
310                .ok_or_else(|| Error::Parse("Missing msg in RedPacketMessage".to_string()))?
311                .to_string(),
312            senderId: data["senderId"]
313                .as_str()
314                .ok_or_else(|| Error::Parse("Missing senderId in RedPacketMessage".to_string()))?
315                .to_string(),
316            GestureType: parse_gesture(data, "gesture", "GestureType", "RedPacketMessage")?,
317            recivers: parse_string_list(data, "recivers", "receivers"),
318            who: parse_who_list(data)?,
319        })
320    }
321}
322
323impl RedPacketBase {
324    pub fn from_value(data: &Value) -> Result<Self, Error> {
325        Ok(RedPacketBase {
326            count: data["count"].as_u64().ok_or_else(|| {
327                Error::Parse("Missing or invalid count in RedPacketBase".to_string())
328            })? as u32,
329            gesture: parse_gesture(data, "gesture", "GestureType", "RedPacketBase")?,
330            got: data["got"].as_u64().ok_or_else(|| {
331                Error::Parse("Missing or invalid got in RedPacketBase".to_string())
332            })? as u32,
333            msg: data["msg"]
334                .as_str()
335                .ok_or_else(|| Error::Parse("Missing msg in RedPacketBase".to_string()))?
336                .to_string(),
337            userName: data["userName"]
338                .as_str()
339                .ok_or_else(|| Error::Parse("Missing userName in RedPacketBase".to_string()))?
340                .to_string(),
341            userAvatarURL: data["userAvatarURL"]
342                .as_str()
343                .ok_or_else(|| Error::Parse("Missing userAvatarURL in RedPacketBase".to_string()))?
344                .to_string(),
345        })
346    }
347}
348
349impl RedPacketInfo {
350    pub fn from_value(data: &Value) -> Result<Self, Error> {
351        let info_data = &data["info"];
352        let info = RedPacketBase::from_value(info_data)?;
353
354        let recivers = parse_string_list(data, "recivers", "receivers");
355        let who = parse_who_list(data)?;
356
357        Ok(RedPacketInfo {
358            info,
359            recivers,
360            who,
361        })
362    }
363}
364
365impl_str_enum!(GestureType {
366    Rock => "石头",
367    Scissors => "剪刀",
368    Paper => "布",
369});
370
371impl_str_enum!(RedPacketType {
372    Random => "random",
373    Average => "average",
374    Specify => "specify",
375    Heartbeat => "heartbeat",
376    RockPaperScissors => "rockPaperScissors",
377});