DistributionRuleClass.php 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. <?php
  2. /**
  3. * 用途:分销规则业务(xhDistributionRule / Tier / Scope)
  4. * 谁用:hdApp 应用-门店设置-分销规则
  5. */
  6. namespace bizHd\distribution\classes;
  7. use bizHd\base\classes\BaseClass;
  8. use Yii;
  9. class DistributionRuleClass extends BaseClass
  10. {
  11. public static $baseFile = '\bizHd\distribution\models\DistributionRule';
  12. /**
  13. * 获取门店分销规则(无则返回默认结构)
  14. * @param int $shopId
  15. * @param int $mainId
  16. * @return array
  17. */
  18. public static function getRule($shopId, $mainId)
  19. {
  20. $rule = self::getByCondition(['shopId' => $shopId]);
  21. if (empty($rule)) {
  22. return self::defaultRule($shopId, $mainId);
  23. }
  24. $ruleId = (int)$rule['id'];
  25. $tiers = DistributionRuleTierClass::getAllByCondition(['ruleId' => $ruleId], 'sort ASC, id ASC');
  26. $scopes = DistributionRuleScopeClass::getAllByCondition(['ruleId' => $ruleId], 'id ASC');
  27. $goodsIds = [];
  28. $categoryIds = [];
  29. foreach ($scopes as $row) {
  30. if ((int)$row['scopeType'] === 1) {
  31. $goodsIds[] = (int)$row['targetId'];
  32. } elseif ((int)$row['scopeType'] === 2) {
  33. $categoryIds[] = (int)$row['targetId'];
  34. }
  35. }
  36. $rule['tiers'] = $tiers ?: self::defaultTiers(false);
  37. $rule['goodsIds'] = $goodsIds;
  38. $rule['categoryIds'] = $categoryIds;
  39. return $rule;
  40. }
  41. /**
  42. * 保存分销规则
  43. * @param int $shopId
  44. * @param int $mainId
  45. * @param array $data
  46. * @throws \Exception
  47. */
  48. public static function saveRule($shopId, $mainId, $data)
  49. {
  50. self::validateRulePayload($data);
  51. $existing = self::getByCondition(['shopId' => $shopId], true);
  52. $saveData = [
  53. 'shopId' => $shopId,
  54. 'mainId' => $mainId,
  55. 'status' => isset($data['status']) ? (int)$data['status'] : 0,
  56. 'settleDays' => isset($data['settleDays']) ? (int)$data['settleDays'] : 0,
  57. 'bonusType' => isset($data['bonusType']) ? (int)$data['bonusType'] : 1,
  58. 'bonusRate' => isset($data['bonusRate']) ? round((float)$data['bonusRate'], 2) : 0,
  59. 'durationType' => isset($data['durationType']) ? (int)$data['durationType'] : 1,
  60. 'durationDays' => isset($data['durationDays']) ? (int)$data['durationDays'] : 0,
  61. 'timeStartType' => isset($data['timeStartType']) ? (int)$data['timeStartType'] : 1,
  62. 'productScope' => isset($data['productScope']) ? (int)$data['productScope'] : 1,
  63. ];
  64. $transaction = Yii::$app->db->beginTransaction();
  65. try {
  66. if (empty($existing)) {
  67. $model = self::add($saveData, true);
  68. $ruleId = (int)$model->id;
  69. } else {
  70. self::updateByCondition(['id' => $existing->id], $saveData);
  71. $ruleId = (int)$existing->id;
  72. }
  73. DistributionRuleTierClass::deleteByCondition(['ruleId' => $ruleId]);
  74. if ((int)$saveData['bonusType'] === 2) {
  75. $tierRows = [];
  76. foreach ($data['tiers'] as $index => $tier) {
  77. $tierRows[] = [
  78. 'ruleId' => $ruleId,
  79. 'minDays' => (int)$tier['minDays'],
  80. 'maxDays' => (int)$tier['maxDays'],
  81. 'bonusRate' => round((float)$tier['bonusRate'], 2),
  82. 'sort' => isset($tier['sort']) ? (int)$tier['sort'] : $index,
  83. ];
  84. }
  85. if (!empty($tierRows)) {
  86. DistributionRuleTierClass::batchAdd($tierRows);
  87. }
  88. }
  89. DistributionRuleScopeClass::deleteByCondition(['ruleId' => $ruleId]);
  90. $scopeRows = [];
  91. $productScope = (int)$saveData['productScope'];
  92. if ($productScope === 2 && !empty($data['goodsIds']) && is_array($data['goodsIds'])) {
  93. foreach ($data['goodsIds'] as $gid) {
  94. $gid = (int)$gid;
  95. if ($gid > 0) {
  96. $scopeRows[] = ['ruleId' => $ruleId, 'scopeType' => 1, 'targetId' => $gid];
  97. }
  98. }
  99. }
  100. if ($productScope === 3 && !empty($data['categoryIds']) && is_array($data['categoryIds'])) {
  101. foreach ($data['categoryIds'] as $cid) {
  102. $cid = (int)$cid;
  103. if ($cid > 0) {
  104. $scopeRows[] = ['ruleId' => $ruleId, 'scopeType' => 2, 'targetId' => $cid];
  105. }
  106. }
  107. }
  108. if (!empty($scopeRows)) {
  109. DistributionRuleScopeClass::batchAdd($scopeRows);
  110. }
  111. $transaction->commit();
  112. } catch (\Exception $e) {
  113. $transaction->rollBack();
  114. throw $e;
  115. }
  116. }
  117. /**
  118. * 校验提交参数
  119. * @param array $data
  120. * @throws \Exception
  121. */
  122. protected static function validateRulePayload($data)
  123. {
  124. $settleDays = isset($data['settleDays']) ? (int)$data['settleDays'] : 0;
  125. if ($settleDays < 0) {
  126. throw new \Exception('佣金发放天数不能为负');
  127. }
  128. $bonusType = isset($data['bonusType']) ? (int)$data['bonusType'] : 1;
  129. if ($bonusType === 1) {
  130. $rate = isset($data['bonusRate']) ? (float)$data['bonusRate'] : 0;
  131. if ($rate <= 0 || $rate > 100) {
  132. throw new \Exception('请填写正确的固定佣金比例(0-100)');
  133. }
  134. $durationType = isset($data['durationType']) ? (int)$data['durationType'] : 1;
  135. if ($durationType === 2) {
  136. $durationDays = isset($data['durationDays']) ? (int)$data['durationDays'] : 0;
  137. if ($durationDays <= 0) {
  138. throw new \Exception('请填写分红限制天数');
  139. }
  140. }
  141. } else {
  142. $tiers = isset($data['tiers']) && is_array($data['tiers']) ? $data['tiers'] : [];
  143. if (empty($tiers)) {
  144. throw new \Exception('请至少添加一个时间阶梯');
  145. }
  146. self::validateTiers($tiers);
  147. }
  148. $productScope = isset($data['productScope']) ? (int)$data['productScope'] : 1;
  149. if ($productScope === 2) {
  150. $goodsIds = isset($data['goodsIds']) && is_array($data['goodsIds']) ? $data['goodsIds'] : [];
  151. if (empty($goodsIds)) {
  152. throw new \Exception('请选择参与分红的商品');
  153. }
  154. }
  155. if ($productScope === 3) {
  156. $categoryIds = isset($data['categoryIds']) && is_array($data['categoryIds']) ? $data['categoryIds'] : [];
  157. if (empty($categoryIds)) {
  158. throw new \Exception('请选择参与分红的分类');
  159. }
  160. }
  161. }
  162. /**
  163. * 校验时间阶梯:不可重叠、不可留空
  164. * @param array $tiers
  165. * @throws \Exception
  166. */
  167. protected static function validateTiers($tiers)
  168. {
  169. $normalized = [];
  170. foreach ($tiers as $tier) {
  171. $minDays = isset($tier['minDays']) ? (int)$tier['minDays'] : -1;
  172. $maxDays = isset($tier['maxDays']) ? (int)$tier['maxDays'] : -1;
  173. $rate = isset($tier['bonusRate']) ? (float)$tier['bonusRate'] : 0;
  174. if ($minDays < 0) {
  175. throw new \Exception('时间阶梯起始天数不能为空');
  176. }
  177. if ($maxDays > 0 && $maxDays < $minDays) {
  178. throw new \Exception('时间阶梯结束天数不能小于起始天数');
  179. }
  180. if ($rate <= 0 || $rate > 100) {
  181. throw new \Exception('请填写正确的阶梯佣金比例');
  182. }
  183. $normalized[] = ['min' => $minDays, 'max' => $maxDays, 'rate' => $rate];
  184. }
  185. usort($normalized, function ($a, $b) {
  186. return $a['min'] - $b['min'];
  187. });
  188. $unlimitedCount = 0;
  189. for ($i = 0; $i < count($normalized); $i++) {
  190. $cur = $normalized[$i];
  191. if ($cur['max'] === 0) {
  192. $unlimitedCount++;
  193. if ($i !== count($normalized) - 1) {
  194. throw new \Exception('「及以上」阶梯只能放在最后一行');
  195. }
  196. }
  197. if ($i > 0) {
  198. $prev = $normalized[$i - 1];
  199. if ($prev['max'] <= 0) {
  200. throw new \Exception('时间阶梯区间不可重叠');
  201. }
  202. if ($cur['min'] <= $prev['max']) {
  203. throw new \Exception('时间阶梯区间不可重叠');
  204. }
  205. }
  206. }
  207. }
  208. /**
  209. * 默认规则(未保存过)
  210. */
  211. protected static function defaultRule($shopId, $mainId)
  212. {
  213. return [
  214. 'id' => 0,
  215. 'shopId' => $shopId,
  216. 'mainId' => $mainId,
  217. 'status' => 0,
  218. 'settleDays' => 5,
  219. 'bonusType' => 1,
  220. 'bonusRate' => '0.00',
  221. 'durationType' => 1,
  222. 'durationDays' => 0,
  223. 'timeStartType' => 1,
  224. 'productScope' => 1,
  225. 'tiers' => self::defaultTiers(true),
  226. 'goodsIds' => [],
  227. 'categoryIds' => [],
  228. ];
  229. }
  230. /**
  231. * 默认递减阶梯示例
  232. */
  233. protected static function defaultTiers($asNew = true)
  234. {
  235. $rows = [
  236. ['minDays' => 0, 'maxDays' => 30, 'bonusRate' => '10.00', 'sort' => 0],
  237. ['minDays' => 31, 'maxDays' => 60, 'bonusRate' => '5.00', 'sort' => 1],
  238. ['minDays' => 61, 'maxDays' => 0, 'bonusRate' => '2.00', 'sort' => 2],
  239. ];
  240. if (!$asNew) {
  241. return $rows;
  242. }
  243. return array_map(function ($row) {
  244. return array_merge($row, ['id' => 0]);
  245. }, $rows);
  246. }
  247. }