statistics_products.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. <?php
  2. /**
  3. * 产品统计分析模块
  4. *
  5. * 包含与产品相关的数据分析功能
  6. */
  7. require_once 'statistics_utils.php';
  8. /**
  9. * 获取热门产品数据
  10. *
  11. * @param mysqli $conn 数据库连接
  12. * @param string $start_date 开始日期
  13. * @param string $end_date 结束日期
  14. * @param int $limit 限制返回的产品数量
  15. * @return mysqli_result 热门产品数据结果集
  16. */
  17. function getTopProducts($conn, $start_date, $end_date, $limit = 5) {
  18. $sql = "SELECT
  19. p.ProductName,
  20. SUM(oi.quantity) as total_quantity,
  21. SUM(oi.total_price) as total_revenue
  22. FROM order_items oi
  23. JOIN products p ON oi.product_id = p.id
  24. JOIN orders o ON oi.order_id = o.id
  25. WHERE o.order_date BETWEEN ? AND ?
  26. GROUP BY oi.product_id
  27. ORDER BY total_revenue DESC
  28. LIMIT ?";
  29. $stmt = $conn->prepare($sql);
  30. $stmt->bind_param("ssi", $start_date, $end_date, $limit);
  31. $stmt->execute();
  32. return $stmt->get_result();
  33. }
  34. /**
  35. * 获取产品销售趋势
  36. *
  37. * @param mysqli $conn 数据库连接
  38. * @param string $start_date 开始日期
  39. * @param string $end_date 结束日期
  40. * @param int $product_id 产品ID,为0时获取所有产品的总体趋势
  41. * @param string $period 时间粒度 (day/week/month)
  42. * @return mysqli_result 产品销售趋势数据结果集
  43. */
  44. function getProductSalesTrend($conn, $start_date, $end_date, $product_id = 0, $period = 'month') {
  45. $groupFormat = '%Y-%m-%d';
  46. if ($period == 'week') {
  47. $groupFormat = '%x-W%v'; // ISO year and week number
  48. } else if ($period == 'month') {
  49. $groupFormat = '%Y-%m';
  50. }
  51. $sql = "SELECT
  52. DATE_FORMAT(o.order_date, '$groupFormat') as time_period,
  53. SUM(oi.quantity) as total_quantity,
  54. SUM(oi.total_price) as total_revenue,
  55. COUNT(DISTINCT o.id) as order_count
  56. FROM order_items oi
  57. JOIN orders o ON oi.order_id = o.id";
  58. if ($product_id > 0) {
  59. $sql .= " WHERE o.order_date BETWEEN ? AND ? AND oi.product_id = ?";
  60. } else {
  61. $sql .= " WHERE o.order_date BETWEEN ? AND ?";
  62. }
  63. $sql .= " GROUP BY time_period
  64. ORDER BY MIN(o.order_date)";
  65. $stmt = $conn->prepare($sql);
  66. if ($product_id > 0) {
  67. $stmt->bind_param("ssi", $start_date, $end_date, $product_id);
  68. } else {
  69. $stmt->bind_param("ss", $start_date, $end_date);
  70. }
  71. $stmt->execute();
  72. return $stmt->get_result();
  73. }
  74. /**
  75. * 获取产品类别销售分布
  76. *
  77. * @param mysqli $conn 数据库连接
  78. * @param string $start_date 开始日期
  79. * @param string $end_date 结束日期
  80. * @return mysqli_result 产品类别销售分布数据结果集
  81. */
  82. function getProductCategorySales($conn, $start_date, $end_date) {
  83. $sql = "SELECT
  84. pc.name as category_name,
  85. SUM(oi.quantity) as total_quantity,
  86. SUM(oi.total_price) as total_revenue,
  87. COUNT(DISTINCT o.id) as order_count
  88. FROM order_items oi
  89. JOIN products p ON oi.product_id = p.id
  90. JOIN product_categories pc ON p.category_id = pc.id
  91. JOIN orders o ON oi.order_id = o.id
  92. WHERE o.order_date BETWEEN ? AND ?
  93. GROUP BY p.category_id
  94. ORDER BY total_revenue DESC";
  95. $stmt = $conn->prepare($sql);
  96. $stmt->bind_param("ss", $start_date, $end_date);
  97. $stmt->execute();
  98. return $stmt->get_result();
  99. }
  100. /**
  101. * 获取产品与地区关联分析
  102. *
  103. * @param mysqli $conn 数据库连接
  104. * @param string $start_date 开始日期
  105. * @param string $end_date 结束日期
  106. * @param int $limit 限制返回的产品-地区组合数量
  107. * @return mysqli_result 产品与地区关联分析数据结果集
  108. */
  109. function getProductRegionAnalysis($conn, $start_date, $end_date, $limit = 10) {
  110. $sql = "SELECT
  111. p.ProductName,
  112. c.countryName,
  113. SUM(oi.quantity) as total_quantity,
  114. SUM(oi.total_price) as total_revenue
  115. FROM order_items oi
  116. JOIN products p ON oi.product_id = p.id
  117. JOIN orders o ON oi.order_id = o.id
  118. JOIN customer cu ON o.customer_id = cu.id
  119. JOIN country c ON cu.cs_country = c.id
  120. WHERE o.order_date BETWEEN ? AND ?
  121. GROUP BY oi.product_id, cu.cs_country
  122. ORDER BY total_revenue DESC
  123. LIMIT ?";
  124. $stmt = $conn->prepare($sql);
  125. $stmt->bind_param("ssi", $start_date, $end_date, $limit);
  126. $stmt->execute();
  127. return $stmt->get_result();
  128. }
  129. /**
  130. * 渲染热门产品表格
  131. *
  132. * @param mysqli_result $top_products 热门产品数据
  133. * @return void
  134. */
  135. function renderTopProductsTable($top_products) {
  136. ?>
  137. <div class="chart-container">
  138. <div class="chart-header">
  139. <h2 class="chart-title">热门产品</h2>
  140. </div>
  141. <table class="data-table">
  142. <thead>
  143. <tr>
  144. <th>产品名称</th>
  145. <th>销售数量</th>
  146. <th>销售收入</th>
  147. </tr>
  148. </thead>
  149. <tbody>
  150. <?php while ($row = $top_products->fetch_assoc()): ?>
  151. <tr>
  152. <td><?php echo htmlspecialchars($row['ProductName']); ?></td>
  153. <td><?php echo number_format($row['total_quantity']); ?></td>
  154. <td>¥<?php echo number_format($row['total_revenue'], 2); ?></td>
  155. </tr>
  156. <?php endwhile; ?>
  157. </tbody>
  158. </table>
  159. </div>
  160. <?php
  161. }
  162. /**
  163. * 渲染产品销售趋势图
  164. *
  165. * @param array $time_labels 时间标签
  166. * @param array $quantities 产品销售数量
  167. * @param array $revenues 产品销售收入
  168. * @return void
  169. */
  170. function renderProductSalesTrendChart($time_labels, $quantities, $revenues) {
  171. ?>
  172. <div class="chart-container">
  173. <div class="chart-header">
  174. <h2 class="chart-title">产品销售趋势</h2>
  175. </div>
  176. <canvas id="productSalesTrendChart"></canvas>
  177. </div>
  178. <script>
  179. // 产品销售趋势图
  180. var productSalesTrendCtx = document.getElementById('productSalesTrendChart').getContext('2d');
  181. var productSalesTrendChart = new Chart(productSalesTrendCtx, {
  182. type: 'line',
  183. data: {
  184. labels: <?php echo json_encode($time_labels); ?>,
  185. datasets: [
  186. {
  187. label: '销售数量',
  188. data: <?php echo json_encode($quantities); ?>,
  189. backgroundColor: 'rgba(54, 162, 235, 0.2)',
  190. borderColor: 'rgba(54, 162, 235, 1)',
  191. borderWidth: 2,
  192. yAxisID: 'y-quantity',
  193. tension: 0.1
  194. },
  195. {
  196. label: '销售收入',
  197. data: <?php echo json_encode($revenues); ?>,
  198. backgroundColor: 'rgba(255, 99, 132, 0.2)',
  199. borderColor: 'rgba(255, 99, 132, 1)',
  200. borderWidth: 2,
  201. yAxisID: 'y-revenue',
  202. tension: 0.1
  203. }
  204. ]
  205. },
  206. options: {
  207. responsive: true,
  208. scales: {
  209. 'y-quantity': {
  210. type: 'linear',
  211. position: 'left',
  212. title: {
  213. display: true,
  214. text: '销售数量'
  215. },
  216. beginAtZero: true
  217. },
  218. 'y-revenue': {
  219. type: 'linear',
  220. position: 'right',
  221. title: {
  222. display: true,
  223. text: '销售收入'
  224. },
  225. beginAtZero: true,
  226. grid: {
  227. drawOnChartArea: false
  228. }
  229. }
  230. }
  231. }
  232. });
  233. </script>
  234. <?php
  235. }
  236. /**
  237. * 渲染产品类别销售分布图
  238. *
  239. * @param array $categories 类别名称
  240. * @param array $quantities 类别销售数量
  241. * @param array $revenues 类别销售收入
  242. * @return void
  243. */
  244. function renderProductCategorySalesChart($categories, $quantities, $revenues) {
  245. ?>
  246. <div class="chart-container">
  247. <div class="chart-header">
  248. <h2 class="chart-title">产品类别销售分布</h2>
  249. </div>
  250. <style>
  251. .pie-charts-container {
  252. display: flex;
  253. flex-direction: row;
  254. justify-content: space-between;
  255. margin-bottom: 20px;
  256. }
  257. .pie-chart-wrapper {
  258. flex: 0 0 48%;
  259. max-width: 48%;
  260. }
  261. </style>
  262. <div class="pie-charts-container">
  263. <div class="pie-chart-wrapper">
  264. <h3 style="text-align: center; margin-bottom: 15px;">产品类别销售数量分布</h3>
  265. <canvas id="categoryQuantityChart"></canvas>
  266. </div>
  267. <div class="pie-chart-wrapper">
  268. <h3 style="text-align: center; margin-bottom: 15px;">产品类别销售收入分布</h3>
  269. <canvas id="categoryRevenueChart"></canvas>
  270. </div>
  271. </div>
  272. </div>
  273. <script>
  274. // 产品类别数量分布图
  275. var categoryQuantityCtx = document.getElementById('categoryQuantityChart').getContext('2d');
  276. var categoryQuantityChart = new Chart(categoryQuantityCtx, {
  277. type: 'pie',
  278. data: {
  279. labels: <?php echo json_encode($categories); ?>,
  280. datasets: [{
  281. data: <?php echo json_encode($quantities); ?>,
  282. backgroundColor: [
  283. 'rgba(255, 99, 132, 0.7)',
  284. 'rgba(54, 162, 235, 0.7)',
  285. 'rgba(255, 206, 86, 0.7)',
  286. 'rgba(75, 192, 192, 0.7)',
  287. 'rgba(153, 102, 255, 0.7)',
  288. 'rgba(255, 159, 64, 0.7)'
  289. ],
  290. borderWidth: 1
  291. }]
  292. },
  293. options: {
  294. responsive: true,
  295. maintainAspectRatio: true,
  296. plugins: {
  297. legend: {
  298. position: 'bottom',
  299. }
  300. }
  301. }
  302. });
  303. // 产品类别收入分布图
  304. var categoryRevenueCtx = document.getElementById('categoryRevenueChart').getContext('2d');
  305. var categoryRevenueChart = new Chart(categoryRevenueCtx, {
  306. type: 'pie',
  307. data: {
  308. labels: <?php echo json_encode($categories); ?>,
  309. datasets: [{
  310. data: <?php echo json_encode($revenues); ?>,
  311. backgroundColor: [
  312. 'rgba(255, 99, 132, 0.7)',
  313. 'rgba(54, 162, 235, 0.7)',
  314. 'rgba(255, 206, 86, 0.7)',
  315. 'rgba(75, 192, 192, 0.7)',
  316. 'rgba(153, 102, 255, 0.7)',
  317. 'rgba(255, 159, 64, 0.7)'
  318. ],
  319. borderWidth: 1
  320. }]
  321. },
  322. options: {
  323. responsive: true,
  324. maintainAspectRatio: true,
  325. plugins: {
  326. legend: {
  327. position: 'bottom',
  328. }
  329. }
  330. }
  331. });
  332. </script>
  333. <?php
  334. }
  335. /**
  336. * 渲染产品与地区关联分析表格
  337. *
  338. * @param mysqli_result $product_region_data 产品与地区关联数据
  339. * @return void
  340. */
  341. function renderProductRegionAnalysisTable($product_region_data) {
  342. ?>
  343. <div class="chart-container">
  344. <div class="chart-header">
  345. <h2 class="chart-title">产品地区关联分析</h2>
  346. </div>
  347. <table class="data-table">
  348. <thead>
  349. <tr>
  350. <th>产品名称</th>
  351. <th>国家/地区</th>
  352. <th>销售数量</th>
  353. <th>销售收入</th>
  354. </tr>
  355. </thead>
  356. <tbody>
  357. <?php while ($row = $product_region_data->fetch_assoc()): ?>
  358. <tr>
  359. <td><?php echo htmlspecialchars($row['ProductName']); ?></td>
  360. <td><?php echo htmlspecialchars($row['countryName']); ?></td>
  361. <td><?php echo number_format($row['total_quantity']); ?></td>
  362. <td>¥<?php echo number_format($row['total_revenue'], 2); ?></td>
  363. </tr>
  364. <?php endwhile; ?>
  365. </tbody>
  366. </table>
  367. </div>
  368. <?php
  369. }