Commit 970e524088ff7aafdcac22cd725f2dc69750c023

Authored by YunaiV
1 parent 1ac2f2b5

code review:交易统计

Showing 15 changed files with 60 additions and 34 deletions
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/trade/TradeStatisticsController.java
@@ -36,9 +36,11 @@ public class TradeStatisticsController { @@ -36,9 +36,11 @@ public class TradeStatisticsController {
36 @Operation(summary = "获得交易统计") 36 @Operation(summary = "获得交易统计")
37 @PreAuthorize("@ss.hasPermission('statistics:trade:query')") 37 @PreAuthorize("@ss.hasPermission('statistics:trade:query')")
38 public CommonResult<TradeStatisticsComparisonRespVO<TradeSummaryRespVO>> getTradeSummaryComparison() { 38 public CommonResult<TradeStatisticsComparisonRespVO<TradeSummaryRespVO>> getTradeSummaryComparison() {
  39 + // TODO @疯狂:这个要不要 tradeStatisticsService 调用里面的多个方法,组合出最终的 TradeSummaryRespVO;
39 return success(tradeStatisticsService.getTradeSummaryComparison()); 40 return success(tradeStatisticsService.getTradeSummaryComparison());
40 } 41 }
41 42
  43 + // TODO @疯狂:【晚点再改和讨论;等首页的接口出来】这个要不还是叫 analyse,对比选中的时间段,和上一个时间段;类似 MemberStatisticsController 的 getMemberAnalyse
42 @GetMapping("/trend/summary") 44 @GetMapping("/trend/summary")
43 @Operation(summary = "获得交易状况统计") 45 @Operation(summary = "获得交易状况统计")
44 @PreAuthorize("@ss.hasPermission('statistics:trade:query')") 46 @PreAuthorize("@ss.hasPermission('statistics:trade:query')")
@@ -48,6 +50,8 @@ public class TradeStatisticsController { @@ -48,6 +50,8 @@ public class TradeStatisticsController {
48 ArrayUtil.get(reqVO.getTimes(), 1))); 50 ArrayUtil.get(reqVO.getTimes(), 1)));
49 } 51 }
50 52
  53 + // TODO 芋艿:已经 review
  54 + // TODO @疯狂:这个要不直接叫 list;它即使就是把每条统计拿出来
51 @GetMapping("/trend/list") 55 @GetMapping("/trend/list")
52 @Operation(summary = "获得交易状况明细") 56 @Operation(summary = "获得交易状况明细")
53 @PreAuthorize("@ss.hasPermission('statistics:trade:query')") 57 @PreAuthorize("@ss.hasPermission('statistics:trade:query')")
@@ -57,6 +61,7 @@ public class TradeStatisticsController { @@ -57,6 +61,7 @@ public class TradeStatisticsController {
57 ArrayUtil.get(reqVO.getTimes(), 1))); 61 ArrayUtil.get(reqVO.getTimes(), 1)));
58 } 62 }
59 63
  64 + // TODO @疯狂:这个要不直接叫 export;它即使就是把每条统计导出
60 @GetMapping("/trend/export-excel") 65 @GetMapping("/trend/export-excel")
61 @Operation(summary = "导出获得交易状况明细 Excel") 66 @Operation(summary = "导出获得交易状况明细 Excel")
62 @PreAuthorize("@ss.hasPermission('statistics:trade:export')") 67 @PreAuthorize("@ss.hasPermission('statistics:trade:export')")
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/trade/vo/TradeTrendSummaryRespVO.java
@@ -16,25 +16,26 @@ public class TradeTrendSummaryRespVO { @@ -16,25 +16,26 @@ public class TradeTrendSummaryRespVO {
16 @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY) 16 @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY)
17 private LocalDate date; 17 private LocalDate date;
18 18
  19 + // TODO @疯狂,要不加个 turnoverPrice?
19 @Schema(description = "营业额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 20 @Schema(description = "营业额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
20 - private Integer turnover; 21 + private Integer turnover; // 营业额 = 商品支付金额 + 充值金额
21 22
22 - @Schema(description = "商品支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 23 + @Schema(description = "订单支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
23 private Integer orderPayPrice; 24 private Integer orderPayPrice;
24 25
  26 + @Schema(description = "订单退款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  27 + private Integer orderRefundPrice;
  28 +
  29 + @Schema(description = "支付佣金金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  30 + private Integer brokerageSettlementPrice;
  31 +
25 @Schema(description = "充值金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 32 @Schema(description = "充值金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
26 private Integer rechargePrice; 33 private Integer rechargePrice;
27 34
28 @Schema(description = "支出金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 35 @Schema(description = "支出金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
29 - private Integer expensePrice; 36 + private Integer expensePrice; // 余额支付金额 + 支付佣金金额 + 商品退款金额
30 37
31 @Schema(description = "余额支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 38 @Schema(description = "余额支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
32 - private Integer balancePrice;  
33 -  
34 - @Schema(description = "支付佣金金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")  
35 - private Integer brokerageSettlementPrice;  
36 -  
37 - @Schema(description = "商品退款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")  
38 - private Integer orderRefundPrice; 39 + private Integer balancePrice; // TODO @疯狂:这个字段要不改成:walletPayPrice
39 40
40 } 41 }
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/dataobject/trade/TradeStatisticsDO.java
@@ -48,6 +48,7 @@ public class TradeStatisticsDO extends TenantBaseDO { @@ -48,6 +48,7 @@ public class TradeStatisticsDO extends TenantBaseDO {
48 * 总支付金额,单位:分 48 * 总支付金额,单位:分
49 */ 49 */
50 private Integer orderPayPrice; 50 private Integer orderPayPrice;
  51 + // TODO @疯狂:这个字段改成 walletPayPrice,然后挪到 rechargePayCount 前面;
51 /** 52 /**
52 * 总支付金额(余额),单位:分 53 * 总支付金额(余额),单位:分
53 */ 54 */
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/infra/ApiAccessLogStatisticsMapper.java
@@ -6,6 +6,7 @@ import org.apache.ibatis.annotations.Param; @@ -6,6 +6,7 @@ import org.apache.ibatis.annotations.Param;
6 6
7 import java.time.LocalDateTime; 7 import java.time.LocalDateTime;
8 8
  9 +// TODO @芋艿:api 访问日志,现在会清理,可能要单独有个偏业务的访问表;
9 /** 10 /**
10 * API 访问日志的统计 Mapper 11 * API 访问日志的统计 Mapper
11 * 12 *
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/pay/PayWalletStatisticsMapper.java
@@ -17,14 +17,17 @@ import java.time.LocalDateTime; @@ -17,14 +17,17 @@ import java.time.LocalDateTime;
17 @Mapper 17 @Mapper
18 public interface PayWalletStatisticsMapper extends BaseMapperX<TradeStatisticsDO> { 18 public interface PayWalletStatisticsMapper extends BaseMapperX<TradeStatisticsDO> {
19 19
  20 + // TODO 芋艿:已经 review;
20 WalletSummaryRespBO selectRechargeSummaryByPayTimeBetween(@Param("beginTime") LocalDateTime beginTime, 21 WalletSummaryRespBO selectRechargeSummaryByPayTimeBetween(@Param("beginTime") LocalDateTime beginTime,
21 @Param("endTime") LocalDateTime endTime, 22 @Param("endTime") LocalDateTime endTime,
22 @Param("payStatus") Boolean payStatus); 23 @Param("payStatus") Boolean payStatus);
23 24
  25 + // TODO 芋艿:已经 review;
24 WalletSummaryRespBO selectRechargeSummaryByRefundTimeBetween(@Param("beginTime") LocalDateTime beginTime, 26 WalletSummaryRespBO selectRechargeSummaryByRefundTimeBetween(@Param("beginTime") LocalDateTime beginTime,
25 @Param("endTime") LocalDateTime endTime, 27 @Param("endTime") LocalDateTime endTime,
26 @Param("refundStatus") Integer refundStatus); 28 @Param("refundStatus") Integer refundStatus);
27 29
  30 + // TODO 芋艿:已经 review;
28 Integer selectPriceSummaryByBizTypeAndCreateTimeBetween(@Param("beginTime") LocalDateTime beginTime, 31 Integer selectPriceSummaryByBizTypeAndCreateTimeBetween(@Param("beginTime") LocalDateTime beginTime,
29 @Param("endTime") LocalDateTime endTime, 32 @Param("endTime") LocalDateTime endTime,
30 @Param("bizType") Integer bizType); 33 @Param("bizType") Integer bizType);
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/trade/AfterSaleStatisticsMapper.java
@@ -16,6 +16,7 @@ import java.time.LocalDateTime; @@ -16,6 +16,7 @@ import java.time.LocalDateTime;
16 @Mapper 16 @Mapper
17 public interface AfterSaleStatisticsMapper extends BaseMapperX<TradeStatisticsDO> { 17 public interface AfterSaleStatisticsMapper extends BaseMapperX<TradeStatisticsDO> {
18 18
  19 + // TODO 芋艿:已 review
19 AfterSaleSummaryRespBO selectSummaryByRefundTimeBetween(@Param("beginTime") LocalDateTime beginTime, 20 AfterSaleSummaryRespBO selectSummaryByRefundTimeBetween(@Param("beginTime") LocalDateTime beginTime,
20 @Param("endTime") LocalDateTime endTime); 21 @Param("endTime") LocalDateTime endTime);
21 22
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/trade/TradeOrderStatisticsMapper.java
@@ -20,9 +20,11 @@ public interface TradeOrderStatisticsMapper extends BaseMapperX&lt;TradeStatisticsD @@ -20,9 +20,11 @@ public interface TradeOrderStatisticsMapper extends BaseMapperX&lt;TradeStatisticsD
20 // TODO 芋艿:已经 review 20 // TODO 芋艿:已经 review
21 List<MemberAreaStatisticsRespVO> selectSummaryListByAreaId(); 21 List<MemberAreaStatisticsRespVO> selectSummaryListByAreaId();
22 22
  23 + // TODO 芋艿:已经 review
23 Integer selectCountByCreateTimeBetween(@Param("beginTime") LocalDateTime beginTime, 24 Integer selectCountByCreateTimeBetween(@Param("beginTime") LocalDateTime beginTime,
24 @Param("endTime") LocalDateTime endTime); 25 @Param("endTime") LocalDateTime endTime);
25 26
  27 + // TODO 芋艿:已经 review
26 Integer selectCountByPayTimeBetween(@Param("beginTime") LocalDateTime beginTime, 28 Integer selectCountByPayTimeBetween(@Param("beginTime") LocalDateTime beginTime,
27 @Param("endTime") LocalDateTime endTime); 29 @Param("endTime") LocalDateTime endTime);
28 30
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/trade/TradeStatisticsMapper.java
@@ -24,6 +24,7 @@ public interface TradeStatisticsMapper extends BaseMapperX&lt;TradeStatisticsDO&gt; { @@ -24,6 +24,7 @@ public interface TradeStatisticsMapper extends BaseMapperX&lt;TradeStatisticsDO&gt; {
24 TradeTrendSummaryRespVO selectByTimeBetween(@Param("beginTime") LocalDateTime beginTime, 24 TradeTrendSummaryRespVO selectByTimeBetween(@Param("beginTime") LocalDateTime beginTime,
25 @Param("endTime") LocalDateTime endTime); 25 @Param("endTime") LocalDateTime endTime);
26 26
  27 + // TODO @芋艿:已经 review
27 List<TradeTrendSummaryRespVO> selectListByTimeBetween(@Param("beginTime") LocalDateTime beginTime, 28 List<TradeTrendSummaryRespVO> selectListByTimeBetween(@Param("beginTime") LocalDateTime beginTime,
28 @Param("endTime") LocalDateTime endTime); 29 @Param("endTime") LocalDateTime endTime);
29 30
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/pay/PayWalletStatisticsService.java
@@ -12,6 +12,7 @@ import java.time.LocalDateTime; @@ -12,6 +12,7 @@ import java.time.LocalDateTime;
12 */ 12 */
13 public interface PayWalletStatisticsService { 13 public interface PayWalletStatisticsService {
14 14
  15 + // TODO 芋艿:已经 review
15 /** 16 /**
16 * 获取钱包统计 17 * 获取钱包统计
17 * 18 *
@@ -21,6 +22,7 @@ public interface PayWalletStatisticsService { @@ -21,6 +22,7 @@ public interface PayWalletStatisticsService {
21 */ 22 */
22 WalletSummaryRespBO getWalletSummary(LocalDateTime beginTime, LocalDateTime endTime); 23 WalletSummaryRespBO getWalletSummary(LocalDateTime beginTime, LocalDateTime endTime);
23 24
  25 + // TODO 芋艿:已经 review
24 /** 26 /**
25 * 获取钱包充值统计 27 * 获取钱包充值统计
26 * 28 *
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/AfterSaleStatisticsService.java
@@ -11,6 +11,7 @@ import java.time.LocalDateTime; @@ -11,6 +11,7 @@ import java.time.LocalDateTime;
11 */ 11 */
12 public interface AfterSaleStatisticsService { 12 public interface AfterSaleStatisticsService {
13 13
  14 + // TODO 芋艿:已经 review
14 /** 15 /**
15 * 获取售后单统计 16 * 获取售后单统计
16 * 17 *
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/BrokerageStatisticsService.java
@@ -9,6 +9,7 @@ import java.time.LocalDateTime; @@ -9,6 +9,7 @@ import java.time.LocalDateTime;
9 */ 9 */
10 public interface BrokerageStatisticsService { 10 public interface BrokerageStatisticsService {
11 11
  12 + // TODO 芋艿:已经 review
12 /** 13 /**
13 * 获取已结算的佣金金额 14 * 获取已结算的佣金金额
14 * 15 *
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/TradeStatisticsService.java
@@ -45,6 +45,7 @@ public interface TradeStatisticsService { @@ -45,6 +45,7 @@ public interface TradeStatisticsService {
45 */ 45 */
46 List<TradeTrendSummaryRespVO> getTradeStatisticsList(LocalDateTime beginTime, LocalDateTime endTime); 46 List<TradeTrendSummaryRespVO> getTradeStatisticsList(LocalDateTime beginTime, LocalDateTime endTime);
47 47
  48 + // TODO 芋艿:已经 review;
48 /** 49 /**
49 * 统计昨日交易 50 * 统计昨日交易
50 * 51 *
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/TradeStatisticsServiceImpl.java
@@ -57,6 +57,30 @@ public class TradeStatisticsServiceImpl implements TradeStatisticsService { @@ -57,6 +57,30 @@ public class TradeStatisticsServiceImpl implements TradeStatisticsService {
57 return TradeStatisticsConvert.INSTANCE.convert(yesterdayData, beforeYesterdayData, monthData, lastMonthData); 57 return TradeStatisticsConvert.INSTANCE.convert(yesterdayData, beforeYesterdayData, monthData, lastMonthData);
58 } 58 }
59 59
  60 + /**
  61 + * 统计指定日期的交易数据
  62 + *
  63 + * @param days 增加的天数
  64 + * @return 交易数据
  65 + */
  66 + private TradeSummaryRespBO getTradeSummaryByDays(int days) {
  67 + LocalDateTime date = LocalDateTime.now().plusDays(days);
  68 + return tradeStatisticsMapper.selectOrderCreateCountSumAndOrderPayPriceSumByTimeBetween(
  69 + LocalDateTimeUtil.beginOfDay(date), LocalDateTimeUtil.endOfDay(date));
  70 + }
  71 +
  72 + /**
  73 + * 统计指定月份的交易数据
  74 + *
  75 + * @param months 增加的月数
  76 + * @return 交易数据
  77 + */
  78 + private TradeSummaryRespBO getTradeSummaryByMonths(int months) {
  79 + LocalDateTime monthDate = LocalDateTime.now().plusMonths(months);
  80 + return tradeStatisticsMapper.selectOrderCreateCountSumAndOrderPayPriceSumByTimeBetween(
  81 + LocalDateTimeUtils.beginOfMonth(monthDate), LocalDateTimeUtils.endOfMonth(monthDate));
  82 + }
  83 +
60 @Override 84 @Override
61 public TradeStatisticsComparisonRespVO<TradeTrendSummaryRespVO> getTradeTrendSummaryComparison(LocalDateTime beginTime, 85 public TradeStatisticsComparisonRespVO<TradeTrendSummaryRespVO> getTradeTrendSummaryComparison(LocalDateTime beginTime,
62 LocalDateTime endTime) { 86 LocalDateTime endTime) {
@@ -73,6 +97,7 @@ public class TradeStatisticsServiceImpl implements TradeStatisticsService { @@ -73,6 +97,7 @@ public class TradeStatisticsServiceImpl implements TradeStatisticsService {
73 return tradeStatisticsMapper.selectExpensePriceByTimeBetween(beginTime, endTime); 97 return tradeStatisticsMapper.selectExpensePriceByTimeBetween(beginTime, endTime);
74 } 98 }
75 99
  100 + // TODO @疯狂:是不是直接返回 TradeStatisticsDO;上层在去聚合?
76 @Override 101 @Override
77 public List<TradeTrendSummaryRespVO> getTradeStatisticsList(LocalDateTime beginTime, LocalDateTime endTime) { 102 public List<TradeTrendSummaryRespVO> getTradeStatisticsList(LocalDateTime beginTime, LocalDateTime endTime) {
78 return tradeStatisticsMapper.selectListByTimeBetween(beginTime, endTime); 103 return tradeStatisticsMapper.selectListByTimeBetween(beginTime, endTime);
@@ -80,6 +105,9 @@ public class TradeStatisticsServiceImpl implements TradeStatisticsService { @@ -80,6 +105,9 @@ public class TradeStatisticsServiceImpl implements TradeStatisticsService {
80 105
81 @Override 106 @Override
82 public String statisticsYesterdayTrade() { 107 public String statisticsYesterdayTrade() {
  108 + // TODO @疯狂:如果已经统计,则跳过;
  109 + // TODO @疯狂:改成 statisticsTrade,然后传入 days,统计多少天;days 通过 job 传参;方便把历史给统计出来;或者大家有的时候要修复数据(会 fix 业务数据,然后清理统计表),重新统计的时候;
  110 + // 1. 从各个数据表,统计对应数据
83 LocalDateTime yesterday = LocalDateTime.now().minusDays(1); 111 LocalDateTime yesterday = LocalDateTime.now().minusDays(1);
84 LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(yesterday); 112 LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(yesterday);
85 LocalDateTime endTime = LocalDateTimeUtil.endOfDay(yesterday); 113 LocalDateTime endTime = LocalDateTimeUtil.endOfDay(yesterday);
@@ -108,28 +136,4 @@ public class TradeStatisticsServiceImpl implements TradeStatisticsService { @@ -108,28 +136,4 @@ public class TradeStatisticsServiceImpl implements TradeStatisticsService {
108 return stopWatch.prettyPrint(); 136 return stopWatch.prettyPrint();
109 } 137 }
110 138
111 - /**  
112 - * 统计指定日期的交易数据  
113 - *  
114 - * @param days 增加的天数  
115 - * @return 交易数据  
116 - */  
117 - private TradeSummaryRespBO getTradeSummaryByDays(int days) {  
118 - LocalDateTime date = LocalDateTime.now().plusDays(days);  
119 - return tradeStatisticsMapper.selectOrderCreateCountSumAndOrderPayPriceSumByTimeBetween(  
120 - LocalDateTimeUtil.beginOfDay(date), LocalDateTimeUtil.endOfDay(date));  
121 - }  
122 -  
123 - /**  
124 - * 统计指定月份的交易数据  
125 - *  
126 - * @param months 增加的月数  
127 - * @return 交易数据  
128 - */  
129 - private TradeSummaryRespBO getTradeSummaryByMonths(int months) {  
130 - LocalDateTime monthDate = LocalDateTime.now().plusMonths(months);  
131 - return tradeStatisticsMapper.selectOrderCreateCountSumAndOrderPayPriceSumByTimeBetween(  
132 - LocalDateTimeUtils.beginOfMonth(monthDate), LocalDateTimeUtils.endOfMonth(monthDate));  
133 - }  
134 -  
135 } 139 }
yudao-module-mall/yudao-module-statistics-biz/src/main/resources/mapper/pay/PayWalletStatisticsMapper.xml
1 <?xml version="1.0" encoding="UTF-8"?> 1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 <mapper namespace="cn.iocoder.yudao.module.statistics.dal.mysql.pay.PayWalletStatisticsMapper"> 3 <mapper namespace="cn.iocoder.yudao.module.statistics.dal.mysql.pay.PayWalletStatisticsMapper">
  4 +
4 <select id="selectRechargeSummaryByPayTimeBetween" 5 <select id="selectRechargeSummaryByPayTimeBetween"
5 resultType="cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO"> 6 resultType="cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO">
6 SELECT COUNT(1) AS rechargePayCount, 7 SELECT COUNT(1) AS rechargePayCount,
yudao-module-mall/yudao-module-statistics-biz/src/main/resources/mapper/trade/TradeStatisticsMapper.xml
@@ -43,6 +43,7 @@ @@ -43,6 +43,7 @@
43 FROM trade_statistics 43 FROM trade_statistics
44 WHERE time BETWEEN #{beginTime} AND #{endTime} 44 WHERE time BETWEEN #{beginTime} AND #{endTime}
45 AND deleted = FALSE 45 AND deleted = FALSE
  46 + <!-- TODO @疯狂:已经不用 group by,它就是每天统计一条; -->
46 GROUP BY date 47 GROUP BY date
47 </select> 48 </select>
48 49