1use crate::db::DbConn;
6use crate::entity::reward::{
7 EventBalanceDto, RewardRecordResultDto, RewardTicketResultDto, RewardTicketTransferLog,
8 RewardTicketsDetailDto, RewardWinnerDto, Rewardtb,
9};
10use crate::sea_entity::{
11 membertb, rewardrecordtb, rewardtb, rewardticketnumbertb, rewardtickettb,
12 rewardtickettransferlog,
13};
14use chrono::{DateTime, Utc};
15use sea_orm::{
16 sea_query::Expr, ActiveModelTrait, ColumnTrait, ConnectionTrait, DatabaseConnection,
17 EntityTrait, PaginatorTrait, QueryFilter, QueryOrder, Set,
18};
19use sqlx::postgres::PgPool;
20use std::collections::HashMap;
21use uuid::Uuid;
22
23#[derive(Debug, Clone)]
25pub struct RewardRow {
26 pub reward_id: i32,
28 pub name: Option<String>,
30 pub price: Option<i32>,
32 pub reward_category: Option<i16>,
34 pub physical_category: Option<i16>,
36 pub stock: Option<i32>,
38 pub dead_line: Option<DateTime<Utc>>,
40 pub from_event_id: Option<i32>,
42}
43
44#[derive(Debug, Clone)]
46pub struct TicketNumberRow {
47 pub member_id: i32,
49 pub exchange_balance: i32,
51 pub lucky_balance: i32,
53 pub total: i32,
55 pub event_id: Option<i32>,
57}
58
59pub struct RewardRepository {
61 db: DbConn,
63}
64
65impl RewardRepository {
66 pub fn new(pool: PgPool) -> Self {
68 Self {
69 db: crate::db::from_sqlx_pool(pool),
70 }
71 }
72
73 pub fn from_db(db: DatabaseConnection) -> Self {
75 Self { db }
76 }
77
78 #[tracing::instrument(skip_all, level = "debug")]
80 pub async fn list_rewards(&self, event_id: i32) -> crate::error::ApiResult<Vec<Rewardtb>> {
81 let rows = rewardtb::Entity::find()
82 .filter(rewardtb::Column::FromEventId.eq(event_id))
83 .order_by_asc(rewardtb::Column::RewardId)
84 .all(&self.db)
85 .await?;
86 Ok(rows.into_iter().map(model_to_rewardtb).collect())
87 }
88
89 #[tracing::instrument(skip_all, level = "debug")]
91 pub async fn get_reward_by_id(
92 &self,
93 reward_id: i32,
94 ) -> crate::error::ApiResult<Option<RewardRow>> {
95 let row = rewardtb::Entity::find_by_id(reward_id)
96 .one(&self.db)
97 .await?;
98 Ok(row.map(model_to_reward_row))
99 }
100
101 #[tracing::instrument(skip_all, level = "debug")]
103 pub async fn deduct_exchange_balance(
104 &self,
105 member_id: i32,
106 amount: i32,
107 ) -> crate::error::ApiResult<u64> {
108 let r = rewardticketnumbertb::Entity::update_many()
109 .col_expr(
110 rewardticketnumbertb::Column::ExchangeBalance,
111 Expr::col(rewardticketnumbertb::Column::ExchangeBalance).sub(Expr::value(amount)),
112 )
113 .filter(rewardticketnumbertb::Column::MemberId.eq(member_id))
114 .filter(rewardticketnumbertb::Column::ExchangeBalance.gte(amount))
115 .exec(&self.db)
116 .await?;
117 Ok(r.rows_affected)
118 }
119
120 #[tracing::instrument(skip_all, level = "debug")]
122 pub async fn deduct_lucky_balance(
123 &self,
124 member_id: i32,
125 amount: i32,
126 ) -> crate::error::ApiResult<u64> {
127 let r = rewardticketnumbertb::Entity::update_many()
128 .col_expr(
129 rewardticketnumbertb::Column::LuckyBalance,
130 Expr::col(rewardticketnumbertb::Column::LuckyBalance).sub(Expr::value(amount)),
131 )
132 .filter(rewardticketnumbertb::Column::MemberId.eq(member_id))
133 .filter(rewardticketnumbertb::Column::LuckyBalance.gte(amount))
134 .exec(&self.db)
135 .await?;
136 Ok(r.rows_affected)
137 }
138
139 #[tracing::instrument(skip_all, level = "debug")]
141 pub async fn get_ticket_number(
142 &self,
143 member_id: i32,
144 ) -> crate::error::ApiResult<Option<TicketNumberRow>> {
145 let row = rewardticketnumbertb::Entity::find()
146 .filter(rewardticketnumbertb::Column::MemberId.eq(member_id))
147 .one(&self.db)
148 .await?;
149 Ok(row.map(model_to_ticket_number))
150 }
151
152 #[tracing::instrument(skip_all, level = "debug")]
154 pub async fn insert_reward_record(
155 &self,
156 member_id: i32,
157 reward_id: i32,
158 exchange_number: i32,
159 ) -> crate::error::ApiResult<i64> {
160 Self::insert_reward_record_with(&self.db, member_id, reward_id, exchange_number).await
161 }
162
163 #[tracing::instrument(skip_all, level = "debug")]
165 pub async fn insert_lucky_tickets(
166 &self,
167 member_id: i32,
168 reward_id: i32,
169 event_id: i32,
170 physical_category: Option<i16>,
171 count: i32,
172 ) -> crate::error::ApiResult<()> {
173 let pc = physical_category.unwrap_or(0) as i32;
174 for _ in 0..count {
175 rewardtickettb::ActiveModel {
176 reward_ticket_id: Set(Uuid::new_v4().to_string()),
177 member_id: Set(member_id),
178 reward_id: Set(reward_id),
179 from_event_id: Set(event_id),
180 status: Set(1),
181 physical_category: Set(pc),
182 ..Default::default()
183 }
184 .insert(&self.db)
185 .await?;
186 }
187 Ok(())
188 }
189
190 #[tracing::instrument(skip_all, level = "debug")]
192 pub async fn select_reward_list(
193 &self,
194 member_id: i32,
195 ) -> crate::error::ApiResult<Vec<RewardRecordResultDto>> {
196 let records = rewardrecordtb::Entity::find()
197 .filter(rewardrecordtb::Column::MemberId.eq(member_id))
198 .order_by_desc(rewardrecordtb::Column::ExchangeTime)
199 .all(&self.db)
200 .await?;
201 if records.is_empty() {
202 return Ok(vec![]);
203 }
204 let reward_ids: Vec<i32> = records.iter().map(|r| r.reward_id).collect();
205 let rewards: HashMap<i32, rewardtb::Model> = rewardtb::Entity::find()
206 .filter(rewardtb::Column::RewardId.is_in(reward_ids))
207 .all(&self.db)
208 .await?
209 .into_iter()
210 .map(|r| (r.reward_id, r))
211 .collect();
212 Ok(records
213 .iter()
214 .map(|rec| {
215 let reward = rewards.get(&rec.reward_id);
216 RewardRecordResultDto {
217 reward_record_id: Some(rec.reward_record_id),
218 reward_id: Some(rec.reward_id),
219 exchange_time: rec
220 .exchange_time
221 .map(crate::utils::legacy_time::naive_db_to_api_utc),
222 exchange_number: Some(rec.exchange_number),
223 tracking_number: rec.tracking_number.clone(),
224 reward_name: reward.map(|r| r.name.clone()),
225 reward_image: reward.and_then(|r| r.image.clone()),
226 get_method: reward.map(|r| r.reward_category),
227 }
228 })
229 .collect())
230 }
231
232 #[tracing::instrument(skip_all, level = "debug")]
234 pub async fn get_reward_tickets(
235 &self,
236 member_id: i32,
237 ) -> crate::error::ApiResult<Vec<RewardTicketResultDto>> {
238 let tickets = rewardtickettb::Entity::find()
239 .filter(rewardtickettb::Column::MemberId.eq(member_id))
240 .all(&self.db)
241 .await?;
242 if tickets.is_empty() {
243 return Ok(vec![]);
244 }
245 let reward_ids: Vec<i32> = tickets.iter().map(|t| t.reward_id).collect();
246 let rewards: HashMap<i32, rewardtb::Model> = rewardtb::Entity::find()
247 .filter(rewardtb::Column::RewardId.is_in(reward_ids))
248 .all(&self.db)
249 .await?
250 .into_iter()
251 .map(|r| (r.reward_id, r))
252 .collect();
253
254 let mut groups: HashMap<i32, Vec<&rewardtickettb::Model>> = HashMap::new();
255 for t in &tickets {
256 groups.entry(t.reward_id).or_default().push(t);
257 }
258 let mut out: Vec<RewardTicketResultDto> = groups
259 .into_iter()
260 .map(|(reward_id, group)| {
261 let reward = rewards.get(&reward_id);
262 let status = group.iter().map(|t| t.status).max();
263 let gacha_time = group
264 .iter()
265 .filter_map(|t| t.gacha_time)
266 .max()
267 .map(crate::utils::legacy_time::naive_db_to_api_utc);
268 RewardTicketResultDto {
269 ticket_number: Some(group.len() as i32),
270 member_id: Some(member_id),
271 reward_id: Some(reward_id),
272 status,
273 reward_name: reward.map(|r| r.name.clone()),
274 reward_image: reward.and_then(|r| r.image.clone()),
275 gacha_time,
276 }
277 })
278 .collect();
279 out.sort_by(|a, b| b.gacha_time.cmp(&a.gacha_time));
280 Ok(out)
281 }
282
283 #[tracing::instrument(skip_all, level = "debug")]
285 pub async fn select_winner(
286 &self,
287 reward_id: i32,
288 event_id: i32,
289 ) -> crate::error::ApiResult<Option<RewardWinnerDto>> {
290 Self::select_random_winner_with(&self.db, reward_id, event_id, 1).await
291 }
292
293 #[tracing::instrument(skip_all, level = "debug")]
295 pub async fn block_winner(
296 &self,
297 event_id: i32,
298 status: i16,
299 winner_id: i32,
300 physical_category: Option<i16>,
301 ) -> crate::error::ApiResult<()> {
302 Self::block_winner_with(&self.db, event_id, status, winner_id, physical_category).await
303 }
304
305 #[tracing::instrument(skip_all, level = "debug")]
307 pub async fn update_ticket_status(
308 &self,
309 ticket_id: &str,
310 status: i16,
311 record_id: i64,
312 ) -> crate::error::ApiResult<()> {
313 Self::update_ticket_status_with(&self.db, ticket_id, status, record_id).await
314 }
315
316 #[tracing::instrument(skip_all, level = "debug")]
318 pub async fn decrement_stock(
319 &self,
320 reward_id: i32,
321 event_id: i32,
322 count: i32,
323 ) -> crate::error::ApiResult<u64> {
324 Self::decrement_stock_with(&self.db, reward_id, event_id, count).await
325 }
326
327 #[tracing::instrument(skip_all, level = "debug")]
329 pub async fn mark_non_win_tickets(&self, reward_id: i32) -> crate::error::ApiResult<()> {
330 Self::mark_non_win_tickets_with(&self.db, reward_id).await
331 }
332
333 pub(crate) async fn get_reward_draw_info_with<C: ConnectionTrait>(
335 conn: &C,
336 reward_id: i32,
337 ) -> crate::error::ApiResult<Option<(Option<i16>, i32)>> {
338 let row = rewardtb::Entity::find_by_id(reward_id).one(conn).await?;
339 Ok(row.map(|r| (Some(r.physical_category as i16), r.stock)))
340 }
341
342 pub(crate) async fn select_random_winner_with<C: ConnectionTrait>(
344 conn: &C,
345 reward_id: i32,
346 event_id: i32,
347 available_status: i16,
348 ) -> crate::error::ApiResult<Option<RewardWinnerDto>> {
349 let mut tickets = rewardtickettb::Entity::find()
350 .filter(rewardtickettb::Column::RewardId.eq(reward_id))
351 .filter(rewardtickettb::Column::FromEventId.eq(event_id))
352 .filter(rewardtickettb::Column::Status.eq(available_status as i32))
353 .all(conn)
354 .await?;
355 if tickets.is_empty() {
356 return Ok(None);
357 }
358 tickets.sort_by(|a, b| a.reward_ticket_id.cmp(&b.reward_ticket_id));
359 let idx = random_ticket_index(tickets.len());
360 let ticket = &tickets[idx];
361 let member = membertb::Entity::find_by_id(ticket.member_id)
362 .one(conn)
363 .await?;
364 Ok(Some(RewardWinnerDto {
365 member_id: ticket.member_id,
366 member_name: member.map(|m| m.username),
367 ticket_id: Some(ticket.reward_ticket_id.clone()),
368 }))
369 }
370
371 pub(crate) async fn block_winner_with<C: ConnectionTrait>(
373 conn: &C,
374 event_id: i32,
375 status: i16,
376 winner_id: i32,
377 physical_category: Option<i16>,
378 ) -> crate::error::ApiResult<()> {
379 let pc = physical_category.unwrap_or(0) as i32;
380 rewardtickettb::Entity::update_many()
381 .col_expr(rewardtickettb::Column::Status, Expr::value(status as i32))
382 .filter(rewardtickettb::Column::MemberId.eq(winner_id))
383 .filter(rewardtickettb::Column::FromEventId.eq(event_id))
384 .filter(rewardtickettb::Column::PhysicalCategory.eq(pc))
385 .exec(conn)
386 .await?;
387 Ok(())
388 }
389
390 pub(crate) async fn insert_reward_record_with<C: ConnectionTrait>(
392 conn: &C,
393 member_id: i32,
394 reward_id: i32,
395 exchange_number: i32,
396 ) -> crate::error::ApiResult<i64> {
397 let now = chrono::Utc::now().naive_utc();
398 let inserted = rewardrecordtb::ActiveModel {
399 member_id: Set(member_id),
400 reward_id: Set(reward_id),
401 exchange_number: Set(exchange_number),
402 tracking_number: Set(Some("想起来了的话会填的喵~".to_string())),
403 exchange_time: Set(Some(now)),
404 ..Default::default()
405 }
406 .insert(conn)
407 .await?;
408 Ok(inserted.reward_record_id as i64)
409 }
410
411 pub(crate) async fn update_ticket_status_with<C: ConnectionTrait>(
413 conn: &C,
414 ticket_id: &str,
415 status: i16,
416 record_id: i64,
417 ) -> crate::error::ApiResult<()> {
418 rewardtickettb::Entity::update_many()
419 .col_expr(rewardtickettb::Column::Status, Expr::value(status as i32))
420 .col_expr(
421 rewardtickettb::Column::RewardRecordId,
422 Expr::value(record_id as i32),
423 )
424 .filter(rewardtickettb::Column::RewardTicketId.eq(ticket_id))
425 .exec(conn)
426 .await?;
427 Ok(())
428 }
429
430 pub(crate) async fn decrement_stock_with<C: ConnectionTrait>(
432 conn: &C,
433 reward_id: i32,
434 event_id: i32,
435 count: i32,
436 ) -> crate::error::ApiResult<u64> {
437 let r = rewardtb::Entity::update_many()
438 .col_expr(
439 rewardtb::Column::Stock,
440 Expr::col(rewardtb::Column::Stock).sub(Expr::value(count)),
441 )
442 .filter(rewardtb::Column::RewardId.eq(reward_id))
443 .filter(rewardtb::Column::FromEventId.eq(event_id))
444 .filter(rewardtb::Column::Stock.gte(count))
445 .exec(conn)
446 .await?;
447 Ok(r.rows_affected)
448 }
449
450 pub(crate) async fn mark_non_win_tickets_with<C: ConnectionTrait>(
452 conn: &C,
453 reward_id: i32,
454 ) -> crate::error::ApiResult<()> {
455 rewardtickettb::Entity::update_many()
456 .col_expr(rewardtickettb::Column::Status, Expr::value(2))
457 .filter(rewardtickettb::Column::RewardId.eq(reward_id))
458 .filter(rewardtickettb::Column::Status.eq(1))
459 .exec(conn)
460 .await?;
461 Ok(())
462 }
463
464 #[tracing::instrument(skip_all, level = "debug")]
466 pub async fn has_opened_rewards(&self, event_id: i32) -> crate::error::ApiResult<bool> {
467 let row = rewardtb::Entity::find()
468 .filter(rewardtb::Column::FromEventId.eq(event_id))
469 .filter(Expr::col(rewardtb::Column::Stock).lt(Expr::col(rewardtb::Column::TotalNumber)))
470 .one(&self.db)
471 .await?;
472 Ok(row.is_some())
473 }
474
475 #[tracing::instrument(skip_all, level = "debug")]
477 pub async fn update_event_deadline(
478 &self,
479 event_id: i32,
480 end_time: DateTime<Utc>,
481 ) -> crate::error::ApiResult<()> {
482 let naive = crate::utils::legacy_time::api_to_db_naive(end_time);
483 rewardtb::Entity::update_many()
484 .col_expr(rewardtb::Column::DeadLine, Expr::value(naive))
485 .filter(rewardtb::Column::FromEventId.eq(event_id))
486 .exec(&self.db)
487 .await?;
488 Ok(())
489 }
490
491 #[tracing::instrument(skip_all, level = "debug")]
493 pub async fn get_reward_balance(
494 &self,
495 member_id: i32,
496 event_id: i32,
497 ) -> crate::error::ApiResult<EventBalanceDto> {
498 let row = rewardticketnumbertb::Entity::find()
499 .filter(rewardticketnumbertb::Column::MemberId.eq(member_id))
500 .filter(rewardticketnumbertb::Column::EventId.eq(event_id))
501 .one(&self.db)
502 .await?;
503 Ok(match row {
504 Some(r) => EventBalanceDto {
505 user_id: member_id,
506 event_id,
507 exchange_balance: Some(r.exchange_balance),
508 lucky_balance: Some(r.lucky_balance),
509 total_tickets: Some(r.total),
510 },
511 None => EventBalanceDto {
512 user_id: member_id,
513 event_id,
514 exchange_balance: Some(0),
515 lucky_balance: Some(0),
516 total_tickets: Some(0),
517 },
518 })
519 }
520
521 #[tracing::instrument(skip_all, level = "debug")]
523 pub async fn get_ticket_details(
524 &self,
525 member_id: i32,
526 ) -> crate::error::ApiResult<Vec<RewardTicketsDetailDto>> {
527 let rows = rewardtickettb::Entity::find()
528 .filter(rewardtickettb::Column::MemberId.eq(member_id))
529 .all(&self.db)
530 .await?;
531 Ok(rows
532 .into_iter()
533 .map(|r| RewardTicketsDetailDto {
534 reward_ticket_id: Some(r.reward_ticket_id),
535 from_event_id: Some(r.from_event_id),
536 reward_id: Some(r.reward_id),
537 status: Some(r.status),
538 })
539 .collect())
540 }
541
542 #[tracing::instrument(skip_all, level = "debug")]
544 pub async fn check_account_exists(&self, member_id: i32) -> crate::error::ApiResult<bool> {
545 let n = membertb::Entity::find_by_id(member_id)
546 .count(&self.db)
547 .await?;
548 Ok(n > 0)
549 }
550
551 #[tracing::instrument(skip_all, level = "debug")]
553 pub async fn check_balance_account_exists(
554 &self,
555 member_id: i32,
556 event_id: i32,
557 ) -> crate::error::ApiResult<bool> {
558 let n = rewardticketnumbertb::Entity::find()
559 .filter(rewardticketnumbertb::Column::MemberId.eq(member_id))
560 .filter(rewardticketnumbertb::Column::EventId.eq(event_id))
561 .count(&self.db)
562 .await?;
563 Ok(n > 0)
564 }
565
566 #[tracing::instrument(skip_all, level = "debug")]
568 pub async fn create_balance_account(
569 &self,
570 member_id: i32,
571 event_id: i32,
572 ) -> crate::error::ApiResult<()> {
573 rewardticketnumbertb::ActiveModel {
574 member_id: Set(member_id),
575 event_id: Set(event_id),
576 exchange_balance: Set(0),
577 lucky_balance: Set(0),
578 total: Set(0),
579 ..Default::default()
580 }
581 .insert(&self.db)
582 .await?;
583 Ok(())
584 }
585
586 #[tracing::instrument(skip_all, level = "debug")]
588 pub async fn transfer_decr(
589 &self,
590 from_id: i32,
591 event_id: i32,
592 ticket_type: i32,
593 amount: i32,
594 ) -> crate::error::ApiResult<u64> {
595 let r = match ticket_type {
596 2 => {
597 rewardticketnumbertb::Entity::update_many()
598 .col_expr(
599 rewardticketnumbertb::Column::ExchangeBalance,
600 Expr::col(rewardticketnumbertb::Column::ExchangeBalance)
601 .sub(Expr::value(amount)),
602 )
603 .filter(rewardticketnumbertb::Column::MemberId.eq(from_id))
604 .filter(rewardticketnumbertb::Column::EventId.eq(event_id))
605 .filter(rewardticketnumbertb::Column::ExchangeBalance.gte(amount))
606 .exec(&self.db)
607 .await?
608 }
609 3 => {
610 rewardticketnumbertb::Entity::update_many()
611 .col_expr(
612 rewardticketnumbertb::Column::LuckyBalance,
613 Expr::col(rewardticketnumbertb::Column::LuckyBalance)
614 .sub(Expr::value(amount)),
615 )
616 .filter(rewardticketnumbertb::Column::MemberId.eq(from_id))
617 .filter(rewardticketnumbertb::Column::EventId.eq(event_id))
618 .filter(rewardticketnumbertb::Column::LuckyBalance.gte(amount))
619 .exec(&self.db)
620 .await?
621 }
622 _ => return Err(crate::error::AppError::business("无效的券类型")),
623 };
624 Ok(r.rows_affected)
625 }
626
627 #[tracing::instrument(skip_all, level = "debug")]
629 pub async fn transfer_incr(
630 &self,
631 to_id: i32,
632 event_id: i32,
633 ticket_type: i32,
634 amount: i32,
635 ) -> crate::error::ApiResult<()> {
636 match ticket_type {
637 2 => {
638 rewardticketnumbertb::Entity::update_many()
639 .col_expr(
640 rewardticketnumbertb::Column::ExchangeBalance,
641 Expr::col(rewardticketnumbertb::Column::ExchangeBalance)
642 .add(Expr::value(amount)),
643 )
644 .filter(rewardticketnumbertb::Column::MemberId.eq(to_id))
645 .filter(rewardticketnumbertb::Column::EventId.eq(event_id))
646 .exec(&self.db)
647 .await?;
648 }
649 3 => {
650 rewardticketnumbertb::Entity::update_many()
651 .col_expr(
652 rewardticketnumbertb::Column::LuckyBalance,
653 Expr::col(rewardticketnumbertb::Column::LuckyBalance)
654 .add(Expr::value(amount)),
655 )
656 .filter(rewardticketnumbertb::Column::MemberId.eq(to_id))
657 .filter(rewardticketnumbertb::Column::EventId.eq(event_id))
658 .exec(&self.db)
659 .await?;
660 }
661 _ => return Err(crate::error::AppError::business("无效的券类型")),
662 }
663 Ok(())
664 }
665
666 #[tracing::instrument(skip_all, level = "debug")]
668 pub async fn insert_transfer_log(
669 &self,
670 from_id: i32,
671 to_id: i32,
672 event_id: i32,
673 ticket_type: i32,
674 amount: i32,
675 ) -> crate::error::ApiResult<()> {
676 rewardtickettransferlog::ActiveModel {
677 from_member_id: Set(from_id),
678 to_member_id: Set(to_id),
679 event_id: Set(event_id),
680 ticket_type: Set(ticket_type),
681 amount: Set(amount),
682 transfer_time: Set(Some(chrono::Utc::now().naive_utc())),
683 ..Default::default()
684 }
685 .insert(&self.db)
686 .await?;
687 Ok(())
688 }
689
690 #[tracing::instrument(skip_all, level = "debug")]
692 pub async fn get_transfer_logs(
693 &self,
694 member_id: i32,
695 ) -> crate::error::ApiResult<Vec<RewardTicketTransferLog>> {
696 use sea_orm::Condition;
697 let rows = rewardtickettransferlog::Entity::find()
698 .filter(
699 Condition::any()
700 .add(rewardtickettransferlog::Column::FromMemberId.eq(member_id))
701 .add(rewardtickettransferlog::Column::ToMemberId.eq(member_id)),
702 )
703 .order_by_desc(rewardtickettransferlog::Column::TransferTime)
704 .all(&self.db)
705 .await?;
706 Ok(rows
707 .into_iter()
708 .map(|r| RewardTicketTransferLog {
709 log_id: Some(r.log_id),
710 from_member_id: r.from_member_id,
711 to_member_id: r.to_member_id,
712 event_id: r.event_id,
713 ticket_type: r.ticket_type,
714 amount: r.amount,
715 transfer_time: r
716 .transfer_time
717 .map(crate::utils::legacy_time::naive_db_to_api_utc),
718 })
719 .collect())
720 }
721}
722
723fn model_to_rewardtb(m: rewardtb::Model) -> Rewardtb {
725 Rewardtb {
726 reward_id: Some(m.reward_id),
727 name: Some(m.name),
728 image: m.image,
729 from_event: m.from_event,
730 from_event_id: Some(m.from_event_id),
731 reward_category: Some(m.reward_category as i16),
732 physical_category: Some(m.physical_category as i16),
733 price: Some(m.price),
734 total_number: Some(m.total_number),
735 stock: Some(m.stock),
736 dead_line: m
737 .dead_line
738 .map(crate::utils::legacy_time::naive_db_to_api_utc),
739 sponsor: m.sponsor,
740 sponsor_id: m.sponsor_id,
741 }
742}
743
744fn model_to_reward_row(m: rewardtb::Model) -> RewardRow {
746 RewardRow {
747 reward_id: m.reward_id,
748 name: Some(m.name),
749 price: Some(m.price),
750 reward_category: Some(m.reward_category as i16),
751 physical_category: Some(m.physical_category as i16),
752 stock: Some(m.stock),
753 dead_line: m
754 .dead_line
755 .map(crate::utils::legacy_time::naive_db_to_api_utc),
756 from_event_id: Some(m.from_event_id),
757 }
758}
759
760fn model_to_ticket_number(m: rewardticketnumbertb::Model) -> TicketNumberRow {
762 TicketNumberRow {
763 member_id: m.member_id,
764 exchange_balance: m.exchange_balance,
765 lucky_balance: m.lucky_balance,
766 total: m.total,
767 event_id: Some(m.event_id),
768 }
769}
770
771fn random_ticket_index(len: usize) -> usize {
773 if len == 0 {
774 return 0;
775 }
776 (Uuid::new_v4().as_u128() as usize) % len
777}