MOrderStatistics.Class.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. <?php
  2. /**
  3. * 订单统计相关的
  4. * Created by PhpStorm.
  5. * User: phperstar
  6. * Date: 2021/04/19
  7. * Time: 11:40
  8. */
  9. namespace JinDouYun\Model\Order;
  10. use JinDouYun\Dao\Order\DOrder;
  11. use Mall\Framework\Core\StatusCode;
  12. use Mall\Framework\Core\ResultWrapper;
  13. use Mall\Framework\Core\ErrorCode;
  14. use Mall\Framework\Factory;
  15. use Jindouyun\Model\MBaseModel;
  16. use JinDouYun\Model\GoodsManage\MSku;
  17. use JinDouYun\Dao\Order\DOrderGoods;
  18. class MOrderStatistics extends MBaseModel
  19. {
  20. /**
  21. * @var int 单表数据量
  22. */
  23. private $cutTable = 200000;
  24. private $userCenterId;
  25. private $enterPriseId;
  26. private $objDOrderGoods;
  27. private $objDOrder;
  28. private $orderStatisticsTableName = 'order_statistics'; // 订单统计视图名字
  29. public function __construct($userCenterId, $enterPriseId)
  30. {
  31. parent::__construct($enterPriseId, $userCenterId);
  32. $this->userCenterId = $userCenterId;
  33. $this->enterPriseId = $enterPriseId;
  34. $this->objDOrder = new DOrder('default');
  35. $this->objDOrderGoods = new DOrderGoods('default');
  36. self::orderSubTable($enterPriseId, $userCenterId);
  37. }
  38. /**
  39. * 订单分表 分表规则:企业id_(客户/200000)
  40. * @param $enterpriseId (企业id)
  41. * @param $userId (用户id)
  42. * @throws \Exception
  43. */
  44. public function orderSubTable($enterpriseId, $userId)
  45. {
  46. $tableName = $this->objDOrder->getTableName('qianniao_order_' . $enterpriseId, $userId, $this->cutTable);
  47. self::existsTable($tableName);
  48. $this->objDOrder->setTable($tableName);
  49. }
  50. /**
  51. * 判断表是否存在
  52. * @param $tableName
  53. */
  54. public function existsTable($tableName)
  55. {
  56. $exists = $this->objDOrder->existsTable($tableName);
  57. if (!$exists) {
  58. //不存在删除视图
  59. self::dropView();
  60. }
  61. }
  62. /**
  63. * 删除订单视图
  64. */
  65. public function dropView()
  66. {
  67. $sql = 'DROP VIEW ' . $this->orderStatisticsTableName . '_' . $this->enterPriseId;
  68. $dbResult = $this->objDOrder->query($sql);
  69. if ($dbResult === false) {
  70. return ResultWrapper::fail($this->objDOrder->error(), ErrorCode::$dberror);
  71. }
  72. return ResultWrapper::success('删除成功');
  73. }
  74. /**
  75. * 订单汇总
  76. * @param $selectParams
  77. * @param $type
  78. * @return ResultWrapper
  79. * @throws \Exception
  80. */
  81. public function getAllOrderData($selectParams, $type, $export = 0)
  82. {
  83. $limit = $selectParams['limit'];
  84. unset($selectParams['limit']);
  85. $offset = $selectParams['offset'];
  86. unset($selectParams['offset']);
  87. if($export){
  88. $limit = 99999;
  89. $offset = 0;
  90. }
  91. $returnWhere = ' ';//查所有数据
  92. if ($selectParams['onReturn']) {//为真去除退货打开 查没退的数据
  93. $returnWhere = ' AND returnStatus != ' . StatusCode::$orderReturn['allReturn'] . ' AND detailsReturnStatus = ' . StatusCode::$orderReturn['notReturn'];//未退货
  94. }
  95. if ($type == 1) {//商品
  96. $groupBySql = ' GROUP BY goodsBasicId';
  97. } elseif ($type == 2) {//客户
  98. $groupBySql = ' GROUP BY customerId,goodsBasicId';
  99. } elseif ($type == 3) {//业务员
  100. $groupBySql = ' GROUP BY salesManId,goodsBasicId';
  101. } else {
  102. return ResultWrapper::fail('type参数错误', ErrorCode::$paramError);
  103. }
  104. $whereSql = '';
  105. $where = ' AND ';
  106. if (isset($selectParams['outStatus']) && !empty($selectParams['outStatus'])) {
  107. $whereSql .= $where . ' outStatus = ' . $selectParams['outStatus'];
  108. }
  109. if (isset($selectParams['goodsName']) && !empty($selectParams['goodsName'])) {
  110. $whereSql .= $where . ' goodsName LIKE "%' . $selectParams['goodsName'] . '%" ';
  111. }
  112. if (isset($selectParams['categoryId']) && !empty($selectParams['categoryId'])) {
  113. $whereSql .= $where . ' FIND_IN_SET(' . $selectParams['categoryId'] . ',categoryPath)';
  114. }
  115. if (isset($selectParams['customerId']) && !empty($selectParams['customerId'])) {
  116. $whereSql .= $where . ' customerId = ' . $selectParams['customerId'];
  117. }
  118. if (isset($selectParams['shopId']) && !empty($selectParams['shopId'])) {
  119. $whereSql .= $where . ' shopId = ' . $selectParams['shopId'];
  120. }
  121. if (isset($selectParams['start']) && !empty($selectParams['start']) && isset($selectParams['end']) && !empty($selectParams['end'])) {
  122. $whereSql .= $where . ' createTime BETWEEN ' . $selectParams['start'] . ' AND ' . $selectParams['end'];
  123. }
  124. //判断订单商品表是否存在
  125. $dbResult = $this->objDOrderGoods->existsTable('qianniao_order_goods_' . $this->enterPriseId . '_1');
  126. if (!$dbResult) {
  127. $return = [
  128. 'data' => [],
  129. 'total' => 0,
  130. ];
  131. return ResultWrapper::success($return);
  132. }
  133. $sql = "SELECT outStatus,customerId,customerName,salesManId,salesManName,goodsBasicId,goodsId,goodsCode,goodsName,skuId,unitName,shopId,shopName,SUM(detailsBuyNum) AS num,SUM(detailsTotalMoney) AS totalMoney,originPrice,SUM(outCostPrice * detailsBuyNum) AS totalCost, SUM(outCostPrice * detailsBuyNum) / SUM(detailsBuyNum) AS costPrice,SUM(detailsTotalMoney - (outCostPrice * detailsBuyNum)) AS totalProfit, SUM(detailsTotalMoney - (outCostPrice * detailsBuyNum)) / SUM(detailsBuyNum) AS profit, SUM(otherNum) AS otherNum "
  134. . "FROM " . $this->orderStatisticsTableName . '_' . $this->enterPriseId . ' '
  135. . 'WHERE deleteStatus = ' . StatusCode::$standard . ' AND detailsDeleteStatus = ' . StatusCode::$standard . ' AND outStatus = ' . StatusCode::$standard
  136. . $whereSql . ' '
  137. . $returnWhere
  138. . $groupBySql . ' '
  139. . 'ORDER BY detailsCreateTime DESC '
  140. . 'LIMIT ' . $offset . ',' . $limit;
  141. //判断视图是否存在
  142. $databaseName = Factory::config()->get('db')['default']['dbname'];
  143. $existsSql = "select table_name from information_schema.views where TABLE_SCHEMA = '" . $databaseName . "' and table_name = '" . $this->orderStatisticsTableName . "_" . $this->enterPriseId . "'";
  144. $dbResult = $this->objDOrderGoods->query($existsSql);
  145. if($dbResult === false) {
  146. return ResultWrapper::fail($this->objDOrderGoods->error(), ErrorCode::$dberror);
  147. }
  148. $existsView = array_shift($dbResult);
  149. unset($dbResult);
  150. if (empty($existsView['TABLE_NAME'])) {
  151. $modelResult = self::setOrderView();
  152. if (!$modelResult->isSuccess()) {
  153. return ResultWrapper::fail($modelResult->getData(), $modelResult->getErrorCode());
  154. }
  155. }
  156. if($export){
  157. $dbResult = $this->objDOrderGoods->exportQuery($sql);
  158. if ($dbResult === false) {
  159. return ResultWrapper::fail($this->objDOrderGoods->error(), ErrorCode::$dberror);
  160. }
  161. $modelResult = self::formatOrderStatistics($dbResult);
  162. if(!$modelResult->isSuccess()){
  163. return ResultWrapper::fail($modelResult->getData(), $modelResult->getErrorCode());
  164. }
  165. $orderData = $modelResult->getData();
  166. unset($modelResult);
  167. self::exportOrderStatistics($orderData);
  168. exit;
  169. }
  170. $dbResult = $this->objDOrderGoods->query($sql);
  171. if ($dbResult === false) {
  172. return ResultWrapper::fail($this->objDOrderGoods->error(), ErrorCode::$dberror);
  173. }
  174. $modelResult = self::formatOrderStatistics($dbResult);
  175. if(!$modelResult->isSuccess()){
  176. return ResultWrapper::fail($modelResult->getData(), $modelResult->getErrorCode());
  177. }
  178. $orderData = $modelResult->getData();
  179. unset($modelResult);
  180. $countSql = 'SELECT COUNT(*) AS `count` FROM (SELECT orderId FROM ' . $this->orderStatisticsTableName . '_' . $this->enterPriseId . ' WHERE deleteStatus = ' . StatusCode::$standard . ' AND detailsDeleteStatus = ' . StatusCode::$standard . ' AND outStatus = ' . StatusCode::$standard . $whereSql . ' ' . $returnWhere . $groupBySql . ') AS selectData';
  181. $dbResult = $this->objDOrderGoods->query($countSql);
  182. if ($dbResult === false) {
  183. return ResultWrapper::fail($this->objDOrderGoods->error(), ErrorCode::$dberror);
  184. }
  185. $totalData = array_shift($dbResult);
  186. unset($dbResult);
  187. $total = $totalData['count'];
  188. //查询结果需要格式化分类名称
  189. $return = [
  190. 'data' => $orderData,
  191. 'total' => ($total) ? intval($total) : 0,
  192. ];
  193. return ResultWrapper::success($return);
  194. }
  195. /**
  196. * 把所有的订单分包合并起来创建一个所有数据的视图
  197. * @throws \Exception
  198. */
  199. public function setOrderView()
  200. {
  201. $unionSql = '';
  202. $i = 1;
  203. while (true) {
  204. $orderTableName = 'qianniao_order_' . $this->enterPriseId . '_' . $i;
  205. $orderGoodsTableName = 'qianniao_order_goods_' . $this->enterPriseId . '_' . $i;
  206. $existsTable = $this->objDOrderGoods->existsTable($orderTableName);
  207. if (!$existsTable) {
  208. break;
  209. }
  210. //拼接sql
  211. $orderSql = "SELECT o.id AS orderId,o.no AS orderNo,o.shopId,o.shopName,o.outerTradeNo,o.totalMoney,o.payAmount,o.buyTotal,o.customerName,o.customerId,o.payStatus ,o.orderStatus,o.payType,o.deliveryType,o.source,o.remark,o.deleteStatus,o.auditStatus,o.returnStatus,o.outStatus,o.payTime,o.createTime,o.updateTime,o.preferentialActivityId AS orderPreferentialActivityId,o.salesManId,o.salesManName,o.customerType,o.userCenterId,g.id AS detailsId,g.goodsCode,g.goodsId,g.goodsName,g.originPrice,g.BuyNum AS detailsBuyNum,g.preferential,g.deliverNum,g.deleteStatus AS detailsDeleteStatus,g.returnStatus AS detailsReturnStatus,g.createTime AS detailsCreateTime,g.updateTime AS detailsUpdateTime,g.preferentialActivityId AS detailsPreferentialActivityId,g.totalMoney AS detailsTotalMoney,g.goodsBasicId,g.skuId,g.unitName,g.conversion,g.outCostPrice,g.categoryPath , JSON_EXTRACT(g.extends,'$.u_1_buy') AS otherNum FROM " . $orderTableName . ' o LEFT JOIN ' . $orderGoodsTableName . ' g ON o.id = g.orderId ';
  212. if (empty($unionSql)) {
  213. $unionSql = 'CREATE VIEW ' . $this->orderStatisticsTableName . '_' . $this->enterPriseId . ' AS ';
  214. } else {
  215. $unionSql .= ' UNION ALL ';
  216. }
  217. $unionSql .= $orderSql;
  218. $i++;
  219. }
  220. $dbResult = $this->objDOrderGoods->query($unionSql);
  221. if ($dbResult === false) {
  222. return ResultWrapper::fail($this->objDOrderGoods->error(), ErrorCode::$dberror);
  223. }
  224. return ResultWrapper::success('创建视图成功');
  225. }
  226. /**
  227. * 格式化销售报表
  228. * @param $params
  229. * @return ResultWrapper
  230. * @throws \Exception
  231. */
  232. public function formatOrderStatistics($params)
  233. {
  234. $skuIds = array_column($params, 'skuId');
  235. $skuData = [];
  236. if (!empty($skuIds)) {
  237. $objMSku = new MSku($this->userCenterId, $this->enterPriseId);
  238. $modelResult = $objMSku->getConversion($skuIds);
  239. if (!$modelResult->isSuccess()) {
  240. return ResultWrapper::fail($modelResult->getData(), $modelResult->getErrorCode());
  241. }
  242. $skuData = $modelResult->getData();
  243. unset($modelResult);
  244. }
  245. foreach ($params as &$value) {
  246. unset($value['unitId']);
  247. unset($value['unitName']);
  248. unset($value['skuName']);
  249. unset($value['skuBarCode']);
  250. $value['skuId'] = isset($skuData[$value['skuId']]['masterSkuId']) ? $skuData[$value['skuId']]['masterSkuId'] : $value['skuId'];
  251. }
  252. $return = parent::formatOrderMan($this->enterPriseId, $params);
  253. return ResultWrapper::success($return);
  254. }
  255. /**
  256. * 订单汇总导出方法
  257. * @param $result
  258. */
  259. public function exportOrderStatistics($result)
  260. {
  261. //导出到本地
  262. header("Content-type:application/vnd.ms-excel");
  263. header("Content-Disposition:filename=销售报表.csv");
  264. header('Cache-Control: max-age=0');
  265. $fp = fopen('php://output', 'a');
  266. $head = ['客户', '商品名称','商品规格', '店铺', '数量','其他单位', '销售收入', '成本', '总成本', '毛利', '总毛利']; //定义标题
  267. foreach ($head as $i => $v) {
  268. $head[$i] = mb_convert_encoding($v, 'GBK', 'utf-8'); //将中文标题转换编码,否则乱码
  269. }
  270. fputcsv($fp, $head);
  271. $limit = 10000;
  272. $num = 0;//计数器
  273. foreach ($result as $value) {//循环数据
  274. $num++;
  275. if ($num == $limit) {
  276. ob_flush();//释放内存
  277. flush();
  278. }
  279. $rows['customerName'] = isset($value['customerName']) ? $value['customerName'] : null;
  280. $rows['goodsName'] = isset($value['goodsName']) ? $value['goodsName'] : null;
  281. $rows['unitName'] = isset($value['unitName']) ? $value['skuName'].(!empty($value['skuName']) ? '_' : '').$value['unitName'] : null;
  282. $rows['shopName'] = isset($value['shopName']) ? $value['shopName'] : null;
  283. $rows['num'] = isset($value['num']) ? $value['num'] : null;
  284. $rows['otherNum'] = isset($value['otherNum']) ? $value['otherNum'] : null;
  285. $rows['totalMoney'] = isset($value['totalMoney']) ? $value['totalMoney'] : null;
  286. $rows['costPrice'] = isset($value['costPrice']) ? $value['costPrice'] : null;
  287. $rows['totalCost'] = isset($value['totalCost']) ? $value['totalCost'] : null;
  288. $rows['profit'] = isset($value['profit']) ? $value['profit'] : null;
  289. $rows['totalProfit'] = isset($value['totalProfit']) ? $value['totalProfit'] : null;
  290. foreach ($rows as $kk => $vv) {
  291. $rs[$kk] = mb_convert_encoding($vv, 'GBK', 'utf-8'); //转译编码
  292. }
  293. fputcsv($fp, $rs);
  294. $rows = [];
  295. }
  296. }
  297. }