AddonsCoupon.php 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343
  1. <?php
  2. namespace addons\Coupon\common\models;
  3. use addons\Agent\AddonConfig;
  4. use addons\Coupon\common\enums\AddonsCouponEnum;
  5. use addons\Store\common\models\Store;
  6. use addons\Store\common\models\StoreCouponShareLog;
  7. use addons\Store\common\models\StoreCouponUseLog;
  8. use addons\Store\common\models\StoreCouponUserRelate;
  9. use addons\Store\common\models\StoreGoods;
  10. use common\enums\AddonsEnum;
  11. use common\enums\RabbitMqEnum;
  12. use common\enums\StatusEnum;
  13. use common\helpers\DateHelper;
  14. use common\helpers\TaskHelper;
  15. use common\models\goods\Goods;
  16. use common\models\goods\GoodsCate;
  17. use common\models\goods\GoodsCateRelate;
  18. use common\models\mall\MallAddons;
  19. use common\models\user\User;
  20. use Exception;
  21. use Yii;
  22. /**
  23. * This is the model class for table "{{%addons_coupon}}".
  24. *
  25. * @property int $id
  26. * @property int $mall_id
  27. * @property int $store_id 门店id
  28. * @property string $name 优惠券名称
  29. * @property int $type 优惠券类型,1:折扣,2:满减
  30. * @property float $discount 折扣率
  31. * @property float|null $discount_limit 折扣优惠金额上限
  32. * @property string $pic_url 优惠券图片(未使用)
  33. * @property string $desc 优惠券简述(未使用)
  34. * @property float $min_price 最低消费金额
  35. * @property float $sub_price 优惠金额
  36. * @property int $total_count 优惠券总数量
  37. * @property int $sort 排序按升序排列
  38. * @property int $expire_type 到期类型:1=领取后n天过期,2=指定有效期,3=领取后n天生效,生效后m天过期
  39. * @property int $expire_day 有效天数,expire_type=1和3时
  40. * @property int $effect_days 领取到生效之间间隔的天数(领取后n天生效)
  41. * @property int $begin_at 有效期开始时间
  42. * @property int $end_at 有效期结束时间
  43. * @property int $appoint_type 1 指定分类 2 指定商品 3全部 4指定门店(020门店)
  44. * @property string|null $rule 使用说明
  45. * @property int $is_remind 是否开启提醒
  46. * @property int $remind_days 提前提醒天数(到期前多少天提醒),is_remind=1 时有效
  47. * @property int $is_receive_limit 是否限制领取
  48. * @property int $receive_count 每人可以领取的数量,is_receive_limit=1时有意义
  49. * @property int $is_member 是否指定会员等级
  50. * @property int $is_original_use 是否原价购买使用(如果是,则只有原价购买商品时可以使用该优惠券)
  51. * @property int $is_public_receive 是否公开领取,公开领取的优惠券会展示在商品详情页
  52. * @property int $is_giving 启用优惠券转送功能:0-否;1-是;
  53. * @property int $giving_expire 赠送有效期,单位天,0不限制
  54. * @property int $status 状态[-1:删除;0:禁用;1启用]
  55. * @property int $coupon_status 优惠券状态,1:未开始,2:领取中,3:已过期,4:已失效(后台操作失效)
  56. * @property int $is_giving_reward 是否开启转赠奖励,开启后转赠优惠券自己可以获得额外奖励;is_giving=1 时有效
  57. * @property int $is_consume_reward 是否开启使用奖励,开启后使用该优惠券可以获得额外奖励;is_giving=1 时有效
  58. * @property string $qrcode_url 优惠券分享二维码保存路径,二维码包含内容:页面路径+优惠券id
  59. * @property string $poster_url 优惠券分享海报图片保存路径
  60. * @property int $give_time 可转赠次数
  61. * @property int $created_at 创建时间
  62. * @property int $updated_at
  63. * @property int $deleted_at
  64. * @property int $is_superposition_use 优惠券是否可以叠加使用
  65. * @property int $applet_code 小程序码连接
  66. */
  67. class AddonsCoupon extends \common\models\base\BaseActiveRecord
  68. {
  69. /**
  70. * {@inheritdoc}
  71. */
  72. public static function tableName()
  73. {
  74. return '{{%addons_coupon}}';
  75. }
  76. /**
  77. * {@inheritdoc}
  78. */
  79. public function rules()
  80. {
  81. return [
  82. [['mall_id', 'min_price', 'type', 'total_count', 'sort', 'expire_type', 'appoint_type',], 'required'],
  83. [['mall_id', 'store_id', 'mch_id', 'type', 'total_count', 'sort', 'expire_type', 'expire_day', 'begin_at', 'end_at',
  84. 'appoint_type', 'is_remind', 'remind_days', 'effect_days', 'is_receive_limit', 'receive_count', 'is_member',
  85. 'is_original_use', 'is_public_receive', 'is_giving', 'giving_expire','status', 'coupon_status', 'is_giving_reward','is_open_screen',
  86. 'is_consume_reward', 'created_at', 'updated_at', 'deleted_at', 'is_superposition_use', 'is_allow_balance_pay', 'is_giving_use', 'day_limit'], 'integer'],
  87. [['discount', 'discount_limit', 'min_price', 'sub_price','give_time'], 'number'],
  88. [['rule', 'applet_code'], 'string'],
  89. [['name'], 'string', 'max' => 150],
  90. ['give_time', 'default', 'value' => '0'],
  91. [['pic_url', 'desc', 'qrcode_url'], 'string', 'max' => 255],
  92. ['discount', 'default', 'value' => '0'],
  93. ['discount', 'check_discount', 'when' => function ($model) {
  94. return $model->type == 1;
  95. }],
  96. ['sub_price', 'compare', 'compareAttribute' => 'min_price', 'operator' => '<=', 'when' => function ($model) {
  97. return $model->type == 2;
  98. }, 'message' => '优惠金额不能大于满减金额'],
  99. ['expire_day', 'required', 'when' => function ($model) {
  100. return $model->type == 1 || $model->type == 3;
  101. }],
  102. ['effect_days', 'required', 'when' => function ($model) {
  103. return $model->type == 3;
  104. }],
  105. ['begin_at', 'required', 'when' => function ($model) {
  106. return $model->type == 2;
  107. }],
  108. ['end_at', 'required', 'when' => function ($model) {
  109. return $model->type == 2;
  110. }],
  111. ['remind_days', 'required', 'when' => function ($model) {
  112. return $model->is_remind == 1;
  113. }],
  114. ['receive_count', 'required', 'when' => function ($model) {
  115. return $model->is_receive_limit == 1;
  116. }],
  117. ['begin_at', 'compare', 'compareAttribute' => 'end_at', 'operator' => '<', 'when' => function ($model) {
  118. return $model->expire_type == 2;
  119. }, 'message' => '优惠券开始时间必须小于结束时间'],
  120. ['begin_at', function ($attribute, $params) {
  121. if ($this->$attribute <= time() && $this->expire_type == 2) {
  122. $this->addError($attribute, '优惠券开始时间不能小于现在时间');
  123. }
  124. }],
  125. ];
  126. }
  127. /**
  128. * {@inheritdoc}
  129. */
  130. public function attributeLabels()
  131. {
  132. return [
  133. 'id' => 'ID',
  134. 'mall_id' => '商城ID',
  135. 'store_id' => '门店ID',
  136. 'name' => '优惠券名称',
  137. 'type' => '优惠券类型',
  138. 'discount' => '优惠折扣',
  139. 'discount_limit' => '折扣金额上限',
  140. 'pic_url' => 'Pic Url',
  141. 'desc' => 'Desc',
  142. 'min_price' => '满减券最低消费金额',
  143. 'sub_price' => '优惠金额',
  144. 'total_count' => '优惠券可发放数量',
  145. 'sort' => '优惠券排序',
  146. 'expire_type' => '优惠券过期类型',
  147. 'expire_day' => '优惠券有效天数',
  148. 'effect_days' => '优惠券领取到生效之间间隔的天数',
  149. 'begin_at' => '优惠券有效期开始时间',
  150. 'end_at' => '优惠券有效期结束时间',
  151. 'appoint_type' => '优惠券可用场景',
  152. 'rule' => '使用规则(说明)',
  153. 'is_remind' => '是否发送过期提醒',
  154. 'remind_days' => '提前提醒天数',
  155. 'is_receive_limit' => '是否限制领取数量',
  156. 'receive_count' => '每个会员可以领取的数量',
  157. 'is_member' => '是否限制会员等级',
  158. 'is_original_use' => '是否必须原价购买可用',
  159. 'is_public_receive' => '是否公开领取',
  160. 'is_giving' => '是否允许转赠',
  161. 'giving_expire' => '赠送有效期,单位天,0不限制',
  162. 'status' => 'Status',
  163. 'coupon_status' => '优惠券状态',
  164. 'give_time'=>'可转赠次数',
  165. 'is_giving_reward' => '是否开启转赠奖励',
  166. 'is_consume_reward' => '是否开启使用奖励',
  167. 'qrcode_url' => '优惠券二维码',
  168. 'poster_url' => '优惠券海报',
  169. 'created_at' => 'Created At',
  170. 'updated_at' => 'Updated At',
  171. 'deleted_at' => 'Deleted At',
  172. ];
  173. }
  174. public function scenarios()
  175. {
  176. return [
  177. 'create' => ['name', 'type', 'discount', 'discount_limit', 'min_price', 'sub_price', 'total_count', 'sort', 'expire_type', 'expire_day', 'begin_at', 'end_at', 'appoint_type', 'rule', 'is_remind', 'remind_days', 'effect_days', 'is_receive_limit', 'receive_count', 'is_member', 'is_original_use', 'is_public_receive', 'is_giving', 'status', 'is_giving_reward', 'is_consume_reward', 'qrcode_url'],
  178. 'update' => ['name', 'type', 'discount', 'discount_limit', 'min_price', 'sub_price', 'total_count', 'sort', 'expire_type', 'expire_day', 'appoint_type', 'rule', 'is_remind', 'remind_days', 'effect_days', 'is_receive_limit', 'receive_count', 'is_member', 'is_original_use', 'is_public_receive', 'is_giving', 'status', 'is_giving_reward', 'is_consume_reward', 'qrcode_url'],
  179. 'default' => [],
  180. ];
  181. }
  182. public function check_discount($attribute, $params)
  183. {
  184. if ($this->$attribute < 0 || $this->$attribute > 9.9) {
  185. $this->addError($attribute, '折扣率必须在 1~9.9 之间');
  186. }
  187. }
  188. /**
  189. * 商品关系
  190. * @return \yii\db\ActiveQuery
  191. */
  192. public function getGoodsRelate()
  193. {
  194. return $this->hasMany(AddonsCouponGoodsRelate::class, ['coupon_id' => 'id']);
  195. }
  196. /**
  197. * 商品关系
  198. * @return \yii\db\ActiveQuery
  199. */
  200. public function getStoreGoodsRelate()
  201. {
  202. return $this
  203. ->hasMany(CouponStoreGoodsRelate::class, ['coupon_id' => 'id'])
  204. ->where(['status' => StatusEnum::ENABLED]);
  205. }
  206. /**
  207. * 分类关系
  208. * @return \yii\db\ActiveQuery
  209. */
  210. public function getCateRelate()
  211. {
  212. return $this->hasMany(AddonsCouponCatRelate::class, ['coupon_id' => 'id']);
  213. }
  214. /**
  215. * 等级关系
  216. * @return \yii\db\ActiveQuery
  217. */
  218. public function getMemberRelate()
  219. {
  220. return $this->hasMany(AddonsCouponMemberRelate::class, ['coupon_id' => 'id']);
  221. }
  222. /**
  223. * 会员关系
  224. * @return \yii\db\ActiveQuery
  225. */
  226. public function getUserRelate()
  227. {
  228. return $this->hasMany(AddonsCouponUserRelate::class, ['coupon_id' => 'id']);
  229. }
  230. /**
  231. * 会员关系
  232. * @return \yii\db\ActiveQuery
  233. */
  234. public function getUserRelateOne()
  235. {
  236. return $this->hasOne(AddonsCouponUserRelate::class, ['coupon_id' => 'id']);
  237. }
  238. /**
  239. * 商品
  240. * @return \yii\db\ActiveQuery
  241. */
  242. public function getGoods()
  243. {
  244. return $this->hasMany(Goods::class, ['id' => 'goods_id'])
  245. ->where(['mch_id' => 0])
  246. ->via('goodsRelate');
  247. }
  248. /**
  249. * 商品
  250. * @return \yii\db\ActiveQuery
  251. */
  252. public function getStoreGoods()
  253. {
  254. return $this->hasMany(StoreGoods::class, ['id' => 'goods_id'])
  255. ->where(['status' => StatusEnum::ENABLED, 'is_on_sale' => StatusEnum::ENABLED])
  256. ->via('storeGoodsRelate');
  257. }
  258. /**
  259. * 分类
  260. * @return \yii\db\ActiveQuery
  261. */
  262. public function getCat()
  263. {
  264. return $this->hasMany(GoodsCate::class, ['id' => 'cate_id'])
  265. ->where(['mch_id' => 0])
  266. ->via('cateRelate');
  267. }
  268. public function getStore()
  269. {
  270. $is_install = MallAddons::isInstall($this->mall_id, AddonsEnum::ADDONS_NAME_STORE);
  271. if (empty($is_install)) {
  272. return [];
  273. }
  274. return $this->hasMany(Store::class, ['id' => 'store_id'])
  275. ->select('id,store_name,logo_img,shop_owner,shop_mobile')
  276. ->where(['status' => StatusEnum::ENABLED, 'check_status' => 1]);
  277. }
  278. public static function deleteCoupon(int $coupon_id, int $mall_id)
  279. {
  280. $transaction = \Yii::$app->db->beginTransaction();
  281. try {
  282. $model = AddonsCoupon::findOne(['mall_id' => $mall_id, 'id' => $coupon_id, 'status' => StatusEnum::ENABLED]);
  283. if (empty($model)) {
  284. throw new \Exception('优惠券不存在或已删除');
  285. }
  286. $model->status = StatusEnum::DELETE;
  287. $model->coupon_status = AddonsCouponEnum::STATUS_FAILURE;
  288. $model->updated_at = time();
  289. $res_coupon = $model->save();
  290. $res_user_coupon = AddonsCouponUserRelate::updateAll(['status' => StatusEnum::DISABLED, 'coupon_status' => AddonsCouponEnum::STATUS_FAILURE], ['mall_id' => $mall_id, 'id' => $coupon_id, 'status' => StatusEnum::ENABLED]);
  291. if ($res_coupon === false || $res_user_coupon === false) {
  292. throw new \Exception('删除失败: res_coupon => ' . $model->getErrorMessage() . ', res_user_coupon => ' . AddonsCouponUserRelate::getStaticError());
  293. }
  294. $transaction->commit();
  295. return true;
  296. } catch (\Exception $e) {
  297. $transaction->rollBack();
  298. self::$static_error = $e->getMessage();
  299. return false;
  300. }
  301. }
  302. /**
  303. * 保存数据
  304. * @param array $params
  305. * @return AddonsCoupon|bool
  306. * @throws \yii\db\Exception
  307. */
  308. public static function setData(array $params, string $scenarios = 'create')
  309. {
  310. $transaction = \Yii::$app->db->beginTransaction();
  311. try {
  312. $coupon_info = $params['coupon_info'] ?? [];
  313. unset($coupon_info['effect_expire_days']);
  314. $member_level_limits = $params['member_level_limits'] ?? [];
  315. $goods_id_list = $params['goods_id_list'] ?? [];
  316. $store_goods_id_list = $params['store_goods_id_list'] ?? [];
  317. $giving_rewards = $params['giving_reward'] ?? [];
  318. $consume_rewards = $params['consume_reward'] ?? [];
  319. $category_ids = $params['category_ids'] ?? [];
  320. $time = time();
  321. if (empty($coupon_info)) throw new Exception('优惠券数据为空');
  322. $model = new self(['scenario' => $scenarios]);
  323. if (!$model->load($coupon_info, '') || false === $model->validate()) {
  324. $errors = $model->errors;
  325. $error_str = '';
  326. foreach ($errors as $error) {
  327. $error_str .= implode(',', $error) . ',';
  328. }
  329. $error_str = trim($error_str, ',');
  330. throw new \Exception($error_str);
  331. }
  332. // 检查优惠券使用场景
  333. self::checkUseScene($params);
  334. if (isset($coupon_info['type']) && $coupon_info['type'] == AddonsCouponEnum::EXCHANGE) {
  335. $coupon_info['min_price'] = $coupon_info['sub_price'] = 0;
  336. }
  337. unset($model);
  338. if (!empty($coupon_info['id'])) {
  339. // 更新
  340. $model = self::findOne(['id' => $coupon_info['id'], 'status' => [StatusEnum::ENABLED, StatusEnum::DISABLED], 'mall_id' => $coupon_info['mall_id']]);
  341. if (empty($model)) throw new Exception('优惠券不存在或已删除');
  342. if ($coupon_info['total_count'] < 1) {
  343. //禅道优惠1458 调整优惠券编辑发放数量
  344. throw new \Exception('优惠券发放数量不能小于1');
  345. }
  346. } else {
  347. // 添加
  348. $model = new self();
  349. $model->loadDefaultValues();
  350. // 计算优惠券 有效期起止时间
  351. if (AddonsCouponEnum::EXPIRE_TYPE1 == $coupon_info['expire_type'] || AddonsCouponEnum::EXPIRE_TYPE3 == $coupon_info['expire_type']) {
  352. // 领取后立即生效,n天过期
  353. // 领取后n天生效,m天过期
  354. $begin_at = $time;
  355. $end_at = 0;
  356. $coupon_status = 2;
  357. } else {
  358. $begin_at = $coupon_info['begin_at'];
  359. $end_at = $coupon_info['end_at'];
  360. if ($begin_at <= $time) {
  361. $coupon_status = 2;
  362. } else {
  363. $coupon_status = 1;
  364. }
  365. }
  366. $coupon_info['begin_at'] = $begin_at;
  367. $coupon_info['end_at'] = $end_at;
  368. $coupon_info['coupon_status'] = $coupon_status;
  369. }
  370. foreach ($coupon_info as $key => $val) {
  371. $model->$key = $val;
  372. }
  373. // 指定门店商品,查询出商品的门店
  374. if ($coupon_info['appoint_type'] == AddonsCouponEnum::APPOINT_TYPE6 && !empty($store_goods_id_list)) {
  375. // 查询门店
  376. $model->store_id = implode(',', array_unique(StoreGoods::find()
  377. ->select('store_id')
  378. ->where(['id' => $store_goods_id_list])
  379. ->column()));
  380. }
  381. // dd($model);
  382. if (!$model->save()) throw new Exception($model->getErrorMessage());
  383. if (!empty($category_ids)) {
  384. $res = AddonsCouponCatRelate::setData($model->id, $category_ids);
  385. if (!$res) throw new Exception(AddonsCouponCatRelate::getStaticError());
  386. }
  387. if (!empty($goods_id_list)) {
  388. $res = AddonsCouponGoodsRelate::setData($model->id, $goods_id_list);
  389. if (!$res) throw new Exception(AddonsCouponGoodsRelate::getStaticError());
  390. }
  391. if (!empty($store_goods_id_list)) {
  392. $res = CouponStoreGoodsRelate::setData($model->id, $coupon_info['mall_id'], $store_goods_id_list);
  393. if (!$res) throw new Exception(CouponStoreGoodsRelate::getStaticError());
  394. }
  395. if (1 == $coupon_info['is_member'] && !empty($member_level_limits)) {
  396. $res = AddonsCouponMemberRelate::setData($model->mall_id, $model->id, $member_level_limits);
  397. if (!$res) throw new Exception(AddonsCouponMemberRelate::getStaticError());
  398. }
  399. if (
  400. 1 == $coupon_info['is_giving'] &&
  401. 1 == $coupon_info['is_giving_reward'] &&
  402. !empty($giving_rewards)
  403. ) {
  404. $res = AddonsCouponReward::setData($model->mall_id, $model->id, AddonsCouponEnum::REWARD_TYPE_GIVING, $giving_rewards);
  405. if (!$res) throw new Exception(AddonsCouponReward::getStaticError());
  406. }
  407. if (1 == $coupon_info['is_consume_reward'] && !empty($consume_rewards)) {
  408. $res = AddonsCouponReward::setData($model->mall_id, $model->id, AddonsCouponEnum::REWARD_TYPE_CONSUME, $consume_rewards);
  409. if (!$res) throw new Exception(AddonsCouponReward::getStaticError());
  410. }
  411. $transaction->commit();
  412. return $model;
  413. } catch (Exception $e) {
  414. $transaction->rollBack();
  415. self::$static_error = $e->getMessage();
  416. return false;
  417. }
  418. }
  419. /**
  420. * 根据用户id查询能用的优惠券列表
  421. * 分多商户
  422. * @Author: hua
  423. * @DateTime: 2021/10/15 10:44
  424. * @Copyright: copyright (c) 2021 广东七件事集团
  425. * @param $form
  426. * @param $isNewRecord
  427. * @return array
  428. */
  429. public static function getCanUseCouponList($form, $isNewRecord)
  430. {
  431. if (!empty($isNewRecord)) return $form;
  432. // 下单包含主商城和多商户情况下 不返回优惠券
  433. if (count($form['group_order_goods']) > 1) return $form;
  434. $mall_id = $form['mall_id'];
  435. $is_install = MallAddons::isInstall($mall_id, AddonsEnum::ADDONS_NAME_COUPON);
  436. $goods_id_arr = $goods_arr = $mch_arr = [];
  437. if (!$is_install) return $form;
  438. $total_goods_price = 0;
  439. $total_coupon_discount_money = 0;
  440. $is_original_goods_price = 1;
  441. $data = is_array($form->data) ? $form->data : json_decode($form->data, true);
  442. $mch_id = 0;
  443. $mch_ids = [];
  444. // 优惠券优惠金额
  445. $discount_money = $form['marketing']['deduct']['coupon']['money'] ?? 0;
  446. foreach ($form['group_order_goods'] as $item) {
  447. if (empty($data['user_coupon_ids'])) {
  448. if ($item['mch_order_info']['goods_price'] != $item['mch_order_info']['original_goods_price']) {
  449. $is_original_goods_price = 0;
  450. }
  451. }
  452. $mch_id = $item['mch_order_info']['mch_id']; // 不同商户不能合单
  453. $mch_ids[] = $mch_id;
  454. $mch_arr[$item['mch_order_info']['mch_id']] = $item['mch_order_info']['goods_price'];
  455. if (!empty($item['goods'])) {
  456. foreach ($item['goods'] as $v) {
  457. $coupon_discount_money = 0;
  458. foreach ($v['goods_details'] as $val) {
  459. if(!empty($val['marketing'])){
  460. $coupon_discount_money += $val['marketing']['coupon_discount_money'] ?? 0;
  461. }
  462. }
  463. $total_coupon_discount_money += $coupon_discount_money;
  464. $total_goods_price += $v['goods_price'];
  465. $goods_id_arr[] = $v['goods_id'];
  466. $goods_arr[$v['goods_id']] = ['num' => $v['num'], 'goods_price' => $v['goods_price'], 'coupon_discount_money' => $coupon_discount_money, 'mch_id' => $v['goods_details'][0]['mch_id']];
  467. }
  468. }
  469. }
  470. $params = [];
  471. $time = time();
  472. $where[] = ['status' => StatusEnum::ENABLED];
  473. $where[] = ['coupon_status' => AddonsCouponEnum::STATUS_RECEIVING];
  474. $where[] = ['is_use' => AddonsCouponEnum::IS_USE0];
  475. $where[] = ['or', ['is_giving' => AddonsCouponEnum::GIVING_N], ['is_giving' => AddonsCouponEnum::GIVING, 'giving_status' => AddonsCouponEnum::GIVING_STATUS1]];
  476. $where[] = ['giving_status' => [AddonsCouponEnum::GIVING_STATUS0, AddonsCouponEnum::GIVING_STATUS1]];
  477. $where[] = ['user_id' => $form['user_id']];
  478. $params['where'] = $where;
  479. $params['with'] = ['coupon' => function ($query) use ($mall_id) {
  480. $query->where(['status' => [StatusEnum::ENABLED, StatusEnum::DISABLED], 'mall_id' => $mall_id])->with(['goodsRelate', 'cateRelate']);
  481. }];
  482. $list = AddonsCouponUserRelate::lists($params);
  483. $cate_id_arr = $cate_arr = [];
  484. $goods_cate_relate_list = self::getCateArr($goods_id_arr, $goods_arr, $cate_id_arr, $cate_arr);
  485. $can_use_list = $can_use_list1 = $can_use_list2 = $can_use_list3 = [];
  486. // 优惠券今日剩余可使用次数
  487. $exchangeTypeCouponMaxUseNum = [];
  488. foreach ($list as &$v) {
  489. if ($v['coupon']) {
  490. $coupon = $v['coupon'];
  491. // 是否原价购买使用(如果是,则只有原价购买商品时可以使用该优惠券)
  492. if(!empty($coupon['is_original_use'])) {
  493. if($is_original_goods_price == 0) {
  494. continue;
  495. }
  496. }
  497. // 验证这张券是否必须转赠后才能使用
  498. if (!self::checkCouponSourceIsFromSharing($v)) {
  499. continue;
  500. }
  501. $coupon_user_relate_arr[$coupon['id']] = $v['created_at'];
  502. if ($v['start_time'] > $time) continue;
  503. if ($v['end_time'] < $time) continue;
  504. // 1 指定分类 2 指定商品 3全部
  505. $coupon['user_coupon_id'] = $v['id'];
  506. $coupon['begin_at'] = $v['start_time'];
  507. $coupon['end_at'] = $v['end_time'];
  508. // 指定分类
  509. if ($coupon['appoint_type'] == AddonsCouponEnum::APPOINT_TYPE1) {
  510. $ct_id_arr = array_column($coupon['cateRelate'], 'cate_id');
  511. unset($coupon['cateRelate']);
  512. unset($coupon['goodsRelate']);
  513. self::packagingArr($coupon, $cate_id_arr, $cate_arr, $can_use_list1, $ct_id_arr, $coupon_user_relate_arr, $coupon['appoint_type'], 0, $total_coupon_discount_money, $goods_cate_relate_list);
  514. continue;
  515. }
  516. // 指定商品
  517. if ($coupon['appoint_type'] == AddonsCouponEnum::APPOINT_TYPE2) {
  518. $gs_id_arr = array_column($coupon['goodsRelate'], 'goods_id');
  519. unset($coupon['goodsRelate']);
  520. unset($coupon['cateRelate']);
  521. if($coupon['type'] == AddonsCouponEnum::EXCHANGE) {
  522. // 多件商品下单不能使用该券
  523. if (count($form['order_goods']) > 1) {
  524. continue;
  525. }
  526. $num = self::getTodayLimit($form['mall_id'], $form['user_id'], $coupon);
  527. // 验证该券每日使用次数
  528. if (!$num) {
  529. continue;
  530. }
  531. $exchangeTypeCouponMaxUseNum[$coupon['id']] = $num;
  532. }
  533. self::packagingArr($coupon, $goods_id_arr, $goods_arr, $can_use_list2, $gs_id_arr, $coupon_user_relate_arr, $coupon['appoint_type'], 0, $total_coupon_discount_money);
  534. continue;
  535. }
  536. // 全部
  537. if ($coupon['appoint_type'] == AddonsCouponEnum::APPOINT_TYPE3) {
  538. unset($coupon['goodsRelate']);
  539. unset($coupon['cateRelate']);
  540. // 使用优惠券前的金额
  541. $after_use_coupon_total_price = bcadd($total_goods_price, abs($discount_money), 2);
  542. self::packagingArr($coupon, [], [], $can_use_list3, [], $coupon_user_relate_arr, $coupon['appoint_type'], $after_use_coupon_total_price);
  543. continue;
  544. }
  545. // 指定商户
  546. if ($coupon['appoint_type'] == AddonsCouponEnum::APPOINT_TYPE5) {
  547. if (!empty($coupon['mch_id'])) {
  548. // 该券属于某个商户的全场通用券
  549. $mch_total_goods_price = $mch_arr[$mch_id];
  550. // 计算使用优惠券前的金额
  551. $mch_total_goods_price = bcadd($mch_total_goods_price, abs($discount_money), 2);
  552. self::packagingArr($coupon, [], [], $can_use_list4, [], $coupon_user_relate_arr, $coupon['appoint_type'], $mch_total_goods_price);
  553. // 剔除不是相关商户的券
  554. $can_use_list4 && $can_use_list4 = static::getMchUseCoupon($can_use_list4, $mch_ids);
  555. }
  556. continue;
  557. }
  558. }
  559. }
  560. if (!empty($can_use_list1)) self::formatArr($can_use_list1, $can_use_list, 2);
  561. if (!empty($can_use_list2)) self::formatArr($can_use_list2, $can_use_list, 2);
  562. if (!empty($can_use_list3)) self::formatArr($can_use_list3, $can_use_list, 2);
  563. if (!empty($can_use_list4)) self::formatArr($can_use_list4, $can_use_list, 2);
  564. if (!empty($exchangeTypeCouponMaxUseNum)) {
  565. // 兑换券只能下单一个商品,所以拿第一个即可
  566. array_walk($exchangeTypeCouponMaxUseNum, function (&$val) use ($form) {
  567. if (!empty($form->sku) && !empty($sku = reset($form->sku))) {
  568. $val = min($sku['stock'], $val);
  569. }
  570. });
  571. }
  572. $form->marketing['deduct']['coupon'] = [
  573. 'list' => $can_use_list,
  574. 'type_map' => AddonsCouponEnum::getTypeMap(),
  575. 'expire_type_map' => AddonsCouponEnum::getExpireTypeMap(),
  576. 'appoint_type_map' => AddonsCouponEnum::getAppointTypeMap(),
  577. 'discount_money' => $discount_money,
  578. 'exchange_type_coupon_max_use_num' => $exchangeTypeCouponMaxUseNum ?: null,
  579. 'goods_stock' => $form->sku ? array_column($form->sku, 'stock', 'id') : [],
  580. ];
  581. return $form;
  582. }
  583. /**
  584. * 验证该券每日使用次数
  585. *
  586. * @param int $mallId
  587. * @param int $userId
  588. * @param array $coupon
  589. * @param int $usedNum 已使用次数
  590. * @param int $fromType
  591. * @param bool $minimumZero 是否最小是0
  592. *
  593. * @return int
  594. */
  595. public static function getTodayLimit(int $mallId, int $userId, array $coupon, int $usedNum = 0, int $fromType = 1, bool $minimumZero = true): int
  596. {
  597. if ($coupon['day_limit'] <= 0) {
  598. return 0;
  599. }
  600. $res = $coupon['day_limit'] - self::getCouponTodayUseNum($mallId, $userId, $coupon['id'], $fromType) - $usedNum;
  601. if (!$minimumZero) {
  602. return $res;
  603. }
  604. // 验证今日使用这个张券的次数
  605. return max($res, 0);
  606. }
  607. /**
  608. * 返回指定优惠券今日使用次数
  609. *
  610. * @param int $mallId
  611. * @param int $userId
  612. * @param int $id
  613. * @param int $fromType
  614. *
  615. * @return int
  616. */
  617. public static function getCouponTodayUseNum(int $mallId, int $userId, int $id, int $fromType = 1): int
  618. {
  619. if ($fromType == 2) {
  620. $find = StoreCouponUseLog::find();
  621. } else {
  622. $find = AddonsCouponUseLog::find();
  623. }
  624. $today = DateHelper::today();
  625. return $find
  626. ->where([
  627. 'mall_id' => $mallId,
  628. 'user_id' => $userId,
  629. 'coupon_id' => $id,
  630. 'status' => StatusEnum::ENABLED,
  631. ])
  632. ->andWhere([
  633. 'between', 'created_at', $today['start'], $today['end']
  634. ])
  635. ->count();
  636. }
  637. /**
  638. * 验证这张券是否开启赠送之后才能使用
  639. *
  640. * @param $couponUserRelate
  641. * @param int $fromType
  642. *
  643. * @return bool
  644. */
  645. public static function checkCouponSourceIsFromSharing($couponUserRelate, int $fromType = 1): bool
  646. {
  647. if (empty($couponUserRelate['coupon']['is_giving'])) {
  648. return true;
  649. }
  650. if (empty($couponUserRelate['coupon']['is_giving_use'])) {
  651. return true;
  652. }
  653. if ($fromType == 2) {
  654. $find = StoreCouponShareLog::find();
  655. } else {
  656. $find = AddonsCouponShareLog::find();
  657. }
  658. return $find
  659. ->where([
  660. 'mall_id' => $couponUserRelate['mall_id'],
  661. 'coupon_id' => $couponUserRelate['coupon_id'],
  662. 'get_user_coupon_id' => $couponUserRelate['id'],
  663. 'giving_status' => AddonsCouponEnum::GIVING_STATUS2
  664. ])
  665. ->exists();
  666. }
  667. /**
  668. * 给定一个用户优惠券列表,找出开启了转赠后才能使用的优惠券,并查询这个优惠券是否是通过转赠领取的
  669. *
  670. * @param int $mallId
  671. * @param array $couponUserRelateList
  672. * @param int $fromType 1、3平台,2门店
  673. *
  674. * @return array
  675. */
  676. public static function getGiftedList(int $mallId, array $couponUserRelateList, int $fromType = 1): array
  677. {
  678. if (!$couponUserRelateList) {
  679. return [];
  680. }
  681. // 用户优惠券id
  682. $relateIds = [];
  683. foreach ($couponUserRelateList as $item) {
  684. if (
  685. empty($item['coupon']) ||
  686. empty($item['coupon']['is_giving']) ||
  687. empty($item['coupon']['is_giving_use'])
  688. ) {
  689. continue;
  690. }
  691. $relateIds[] = $item['id'];
  692. }
  693. if (!$relateIds) {
  694. return [];
  695. }
  696. if ($fromType == 2) {
  697. $find = StoreCouponShareLog::find();
  698. } else {
  699. $find = AddonsCouponShareLog::find();
  700. }
  701. return $find
  702. ->where([
  703. 'mall_id' => $mallId,
  704. 'get_user_coupon_id' => $relateIds,
  705. 'giving_status' => AddonsCouponEnum::GIVING_STATUS2
  706. ])
  707. ->select('get_user_coupon_id')
  708. ->column();
  709. }
  710. /**
  711. * @desc:
  712. * @Author: hua
  713. * @Date: 2021/10/18
  714. * @Time: 11:54
  715. * @Copyright: copyright (c) 2021 广东七件事集团
  716. * @param $coupon //优惠券
  717. * @param $id_arr//关联id数组
  718. * @param $arr
  719. * @param $can_use_list
  720. * @param $have_id_arr
  721. * @param $coupon_user_relate_arr //领取时间数组
  722. * @param int $appoint_type //使用场景类型
  723. * @param int $total_price //总优惠价格
  724. */
  725. public static function packagingArr($coupon, $id_arr, $arr, &$can_use_list, $have_id_arr, $coupon_user_relate_arr, $appoint_type = 1, $total_price = 0,$total_coupon_discount_money=0, $goods_cate_relate_list = [])
  726. {
  727. $coupon['coupon_discount_money'] = 0;
  728. // 指定商品分类可用
  729. if ($appoint_type == AddonsCouponEnum::APPOINT_TYPE1) {
  730. // 获取商品分类
  731. $goods_cids = [];
  732. foreach ($goods_cate_relate_list as $item) {
  733. $goods_cids[$item['goods_id']][] = $item['cate_id'];
  734. }
  735. // 获取每个商品是否符合优惠券
  736. $intersect = array_unique(array_map(function ($item) use ($have_id_arr) {
  737. return boolval(array_intersect($item, $have_id_arr));
  738. }, $goods_cids));
  739. // 不符合返回
  740. if (!(count($intersect) == 1 && reset($intersect))) return ;
  741. foreach ($id_arr as $id) {
  742. // count(array_diff($id_arr, $have_id_arr)) == 0) 购买商品的分类IDS和当前券的分类IDS相同
  743. if (in_array($id, $have_id_arr)) {
  744. if (!empty($coupon['mch_id'])) {
  745. //该券属于某个商户的指定商品
  746. if ($arr[$id]['mch_id'] != $coupon['mch_id']) continue;
  747. }
  748. // 单分类总价
  749. // $price = $arr[$id]['goods_price'] ?? 0;
  750. // 全部分类总价
  751. $price = array_sum(array_column($arr, 'goods_price'));
  752. if ($coupon['min_price'] && ($total_coupon_discount_money + $price) >= $coupon['min_price']) {
  753. if ($coupon['type'] == AddonsCouponEnum::DISCOUNT) round($coupon['sub_price'] = $coupon['discount'] / 10 * $price, 2);
  754. $can_use_list[$coupon['mch_id']][] = $coupon;
  755. break;
  756. }
  757. }
  758. }
  759. }
  760. // 指定商品
  761. if ($appoint_type == AddonsCouponEnum::APPOINT_TYPE2) {
  762. foreach ($id_arr as $id) {
  763. // count(array_diff($id_arr, $have_id_arr)) == 0) 购买商品的IDS和当前券的IDS相同
  764. if (in_array($id, $have_id_arr) && (count(array_diff($id_arr, $have_id_arr)) == 0)) {
  765. if (!empty($coupon['mch_id'])) {
  766. //该券属于某个商户的指定商品
  767. if ($arr[$id]['mch_id'] != $coupon['mch_id']) continue;
  768. }
  769. // 兑换券
  770. if ($coupon['type'] == AddonsCouponEnum::EXCHANGE) {
  771. $can_use_list[$coupon['mch_id']][] = $coupon;
  772. break;
  773. } else { // 其他类型的券
  774. $price = $arr[$id]['goods_price'];
  775. if ($coupon['min_price'] && ($arr[$id]['coupon_discount_money'] + $price) >= $coupon['min_price']) {
  776. if ($coupon['type'] == AddonsCouponEnum::DISCOUNT) round($coupon['sub_price'] = $coupon['discount'] / 10 * $price, 2);
  777. $can_use_list[$coupon['mch_id']][] = $coupon;
  778. break;
  779. } else if (count(array_diff($id_arr, $have_id_arr)) == 0 && bcadd(array_sum(array_column($arr, 'goods_price')), $total_coupon_discount_money, 2) >= $coupon['min_price']) { // 组合使用
  780. $can_use_list[$coupon['mch_id']][] = $coupon;
  781. break;
  782. }
  783. }
  784. }
  785. }
  786. }
  787. // 全场可用 || 指定商户
  788. if ($appoint_type == AddonsCouponEnum::APPOINT_TYPE3 || $appoint_type == AddonsCouponEnum::APPOINT_TYPE5) {
  789. if ($coupon['min_price'] && $total_price >= $coupon['min_price']) {
  790. if ($coupon['type'] == AddonsCouponEnum::DISCOUNT) $coupon['sub_price'] = round($coupon['discount'] / 10 * $total_price, 2);
  791. $can_use_list[$coupon['mch_id']][] = $coupon;
  792. }
  793. }
  794. }
  795. /**
  796. * 获取分类数据
  797. * @Author: hua
  798. * @DateTime: 2021/10/15 10:56
  799. * @Copyright: copyright (c) 2021 广东七件事集团
  800. * @param $goods_id_arr
  801. * @param $goods_arr
  802. * @param $cate_id_arr
  803. * @param $cate_arr
  804. * @return string
  805. */
  806. public static function getCateArr($goods_id_arr, $goods_arr, &$cate_id_arr, &$cate_arr)
  807. {
  808. // 查询缓存
  809. $goods_cate_relate_list = GoodsCateRelate::getListByGoodsIds($goods_id_arr, 'id, cate_id, goods_id', 5);
  810. foreach ($goods_cate_relate_list as $v) {
  811. $cate_id_arr[] = $v['cate_id'];
  812. $cate_arr[$v['cate_id']]['mch_id'] = $goods_arr[$v['goods_id']]['mch_id'] ?? 0;
  813. if (isset($cate_arr[$v['cate_id']]['goods_price'])) {
  814. $cate_arr[$v['cate_id']]['goods_price'] += $goods_arr[$v['goods_id']]['goods_price'] ?? 0;
  815. } else {
  816. $cate_arr[$v['cate_id']]['goods_price'] = $goods_arr[$v['goods_id']]['goods_price'] ?? 0;
  817. }
  818. }
  819. return $goods_cate_relate_list;
  820. }
  821. /**
  822. * 格式化
  823. * @Author: hua
  824. * @DateTime: 2021/10/15 10:04
  825. * @Copyright: copyright (c) 2021 广东七件事集团
  826. * @param $input_arr
  827. * @param $output_arr
  828. * @param int $deep
  829. */
  830. public static function formatArr($input_arr, &$output_arr, $deep = 1)
  831. {
  832. if ($deep == 1) {
  833. array_map(function ($arg) use (&$output_arr) {
  834. $output_arr[] = $arg;
  835. }, $input_arr);
  836. }
  837. if ($deep == 2) {
  838. array_map(function ($args) use (&$output_arr) {
  839. array_map(function ($arg) use (&$output_arr) {
  840. $output_arr[] = $arg;
  841. }, $args);
  842. }, $input_arr);
  843. }
  844. }
  845. /**
  846. * 获取商城优惠券
  847. * @author hpei
  848. * @param int mall_id
  849. * @return array [coupon_id=>coupon_name]
  850. */
  851. public static function getMallCoupon($mall_id)
  852. {
  853. return self::find()
  854. ->where(['mall_id' => $mall_id])
  855. ->andWhere(['=', 'status', StatusEnum::ENABLED])
  856. ->andWhere(['=', 'coupon_status', AddonsCouponEnum::STATUS_RECEIVING])
  857. ->andWhere(['or', ['=', 'total_count', '-1'], ['>', 'total_count', 0]])
  858. ->andFilterWhere(['or', ['and', ['>', 'end_at', time()], ['=', 'expire_type', 2]], ['=', 'expire_type', 1]])
  859. ->indexBy('id')
  860. ->orderBy('sort asc,id desc')
  861. ->asArray()
  862. ->all();
  863. }
  864. /**
  865. * @name:
  866. * @msg: 解析优惠券信息
  867. * @param {array} $coupon_data 优惠券原始数据
  868. * @param {bool} $is_user_coupon 是否会员已经领取的优惠券的数据,默认 false
  869. * @return array
  870. */
  871. public static function parseCouponInfo(array $coupon_data, bool $is_user_coupon = false): array
  872. {
  873. $coupon_data['min_price'] = floatval($coupon_data['min_price']);
  874. $coupon_data['sub_price'] = floatval($coupon_data['sub_price']);
  875. $coupon_data['discount'] = floatval($coupon_data['discount']);
  876. $coupon_data['discount_limit'] = floatval($coupon_data['discount_limit']);
  877. $sub_price = 0;
  878. if (1 == $coupon_data['type']) {
  879. $content = '消费满' . $coupon_data['min_price'] . '元打' . $coupon_data['discount'] . '折';
  880. if ($coupon_data['discount_limit'] > 0) {
  881. $content .= ',最高优惠' . $coupon_data['discount_limit'] . '元';
  882. $sub_price = $coupon_data['discount_limit'];
  883. }
  884. } elseif (2 == $coupon_data['type']) {
  885. $content = '消费满' . $coupon_data['min_price'] . '元减' . $coupon_data['sub_price'] . '元';
  886. $sub_price = $coupon_data['sub_price'];
  887. } else {
  888. $content = '';
  889. }
  890. $superposition = '';
  891. if (1 == $coupon_data['is_original_use']) $superposition = '仅原价购买时可用';
  892. if ($is_user_coupon) {
  893. $begin_at = date('Y-m-d H:i', $coupon_data['start_time']);
  894. $end_at = date('Y-m-d H:i', $coupon_data['end_time']);
  895. $use_time = $begin_at . ' 至 ' . $end_at;
  896. } else {
  897. switch ($coupon_data['expire_type']) {
  898. case AddonsCouponEnum::APPOINT_TYPE1:
  899. $use_time = str_replace('N', $coupon_data['expire_day'], AddonsCouponEnum::getExpireTypeMap()[AddonsCouponEnum::APPOINT_TYPE1]);
  900. break;
  901. case AddonsCouponEnum::APPOINT_TYPE2:
  902. $begin_at = date('Y-m-d H:i', $coupon_data['begin_at']);
  903. $end_at = date('Y-m-d H:i', $coupon_data['end_at']);
  904. $use_time = $begin_at . ' 至 ' . $end_at;
  905. break;
  906. case AddonsCouponEnum::APPOINT_TYPE3:
  907. $use_time = str_replace('N', $coupon_data['expire_day'], AddonsCouponEnum::getExpireTypeMap()[AddonsCouponEnum::APPOINT_TYPE3]);
  908. $use_time = str_replace('M', $coupon_data['effect_days'], AddonsCouponEnum::getExpireTypeMap()[AddonsCouponEnum::APPOINT_TYPE3]);
  909. break;
  910. default:
  911. $use_time = '';
  912. break;
  913. }
  914. $begin_at = date('Y-m-d H:i', $coupon_data['begin_at']);
  915. $end_at = $coupon_data['end_at'] ? date('Y-m-d H:i', $coupon_data['end_at']) : '';
  916. }
  917. if(isset($coupon_data['is_use']) && $coupon_data['is_use'] == 1){
  918. $status_text = '已使用';
  919. }elseif ($coupon_data['is_giving']==1&&$coupon_data['giving_status']==AddonsCouponEnum::GIVING_STATUS2){
  920. $status_text = '已转赠';
  921. }else{
  922. $status_text = AddonsCouponEnum::getCouponStatusMap($coupon_data['coupon_status'] ?? 0, false);
  923. }
  924. $coupon_info = [
  925. 'name' => $coupon_data['name'],
  926. 'appoint_type' => $coupon_data['appoint_type'],
  927. 'status_text' => $status_text,
  928. 'use_time' => $use_time,
  929. 'content' => $content,
  930. 'begin_at' => $begin_at,
  931. 'end_at' => $end_at,
  932. 'sub_price' => floatval($sub_price),
  933. 'min_price' => floatval($coupon_data['min_price']),
  934. 'rule' => $coupon_data['rule'],
  935. 'superposition_use' => $superposition,
  936. ];
  937. return $coupon_info;
  938. }
  939. /**
  940. * @name:
  941. * @msg: 在优惠券列表中,处理每一张优惠券对于当前会员是否可以领取
  942. * @param {*} $coupon_list 优惠券列表数据,必须包含 is_member、is_receive_limit、receive_count 字段,必须关联查询 jxmall_coupon_member_relation 表
  943. * @param {int} $user_id 会员id,值为 0 表示未登录
  944. * @return {*}
  945. */
  946. public static function userCanReceive(array &$coupon_list, int $user_id = 0)
  947. {
  948. // 当前会员已经领取的优惠券数据
  949. if (!empty($user_id)) {
  950. // 会员已经登录
  951. $user_coupon = AddonsCouponUserRelate::find()
  952. ->select('coupon_id,count(*) as count')
  953. ->where(['user_id' => $user_id, 'coupon_id' => array_column($coupon_list, 'id'), 'status' => 1])
  954. ->groupBy('coupon_id')
  955. ->asArray()
  956. ->indexBy('coupon_id')
  957. ->all();
  958. // dd($user_coupon);
  959. }
  960. $user = User::findOne($user_id);
  961. foreach ($coupon_list as &$lv) {
  962. $lv = array_merge($lv, self::parseCouponInfo($lv));
  963. // 默认不可领取当前优惠券
  964. $lv['is_receive'] = 0;
  965. // 当前优惠券限制的会员等级
  966. if (1 == $lv['is_member']) {
  967. if (!empty($lv['memberRelate'])) {
  968. $member_levels = array_column($lv['memberRelate'], 'member_level');
  969. // dd($member_levels);
  970. $user_level = 0;
  971. if (!empty($user)) $user_level = $user->level;
  972. if (in_array($user_level, $member_levels)) $lv['is_receive'] = 1;
  973. }
  974. } else {
  975. $lv['is_receive'] = 1;
  976. }
  977. if (empty($user_coupon)) {
  978. // 没有任何会员领取过优惠券,当前会员可以继续领取优惠券
  979. $lv['is_receive'] = 1;
  980. } else {
  981. if (!empty($user_coupon[$lv['id']])) {
  982. // 会员已经领取过
  983. if (1 == $lv['is_receive_limit']) {
  984. // 已领取数量小于限领数量,可以继续领取优惠券
  985. if ($user_coupon[$lv['id']]['count'] < $lv['receive_count']) {
  986. $lv['is_receive'] = 1;
  987. }
  988. } else {
  989. // 不限制数量,可以继续领取优惠券
  990. $lv['is_receive'] = 1;
  991. }
  992. }
  993. // 可以继续领取优惠券
  994. $lv['is_receive'] = 0;
  995. }
  996. }
  997. return true;
  998. }
  999. /**
  1000. * @name:
  1001. * @msg: 检查优惠券的使用场景(指定适用类型、是否可以转赠、转赠和使用奖励、适用会员)
  1002. * @param {array} $post
  1003. * @return {*}
  1004. */
  1005. private static function checkUseScene(array $coupon_data)
  1006. {
  1007. $coupon_info = $coupon_data['coupon_info'] ?? [];
  1008. $giving_rewards = $coupon_data['giving_reward'] ?? [];
  1009. $consume_rewards = $coupon_data['consume_reward'] ?? [];
  1010. $goods_id_list = $coupon_data['goods_id_list'] ?? [];
  1011. $store_goods_id_list = $coupon_data['store_goods_id_list'] ?? [];
  1012. $member_level_limits = $coupon_data['member_level_limits'] ?? [];
  1013. $category_ids = $coupon_data['category_ids'] ?? [];
  1014. if (1 == $coupon_info['appoint_type']) {
  1015. // 指定分类可用
  1016. if (empty($category_ids)) {
  1017. throw new \Exception('指定分类可用的优惠券,请指定可用分类');
  1018. }
  1019. } elseif (2 == $coupon_info['appoint_type']) {
  1020. // 指定商品可用
  1021. if (empty($goods_id_list)) {
  1022. throw new \Exception('指定商品可用的优惠券,请指定可用商品');
  1023. }
  1024. } elseif (AddonsCouponEnum::APPOINT_TYPE6 == $coupon_info['appoint_type']) {
  1025. // 指定商品可用
  1026. if (empty($store_goods_id_list)) {
  1027. throw new \Exception('请指定可用门店商品');
  1028. }
  1029. }
  1030. if ($coupon_info['type'] == AddonsCouponEnum::EXCHANGE && !in_array($coupon_info['appoint_type'], [AddonsCouponEnum::APPOINT_TYPE2, AddonsCouponEnum::APPOINT_TYPE6])) {
  1031. throw new \Exception('使用范围选择错误');
  1032. }
  1033. if (1 == $coupon_info['is_giving']) {
  1034. // 开启转赠
  1035. if (1 == $coupon_info['is_giving_reward'] && empty($giving_rewards)) {
  1036. throw new \Exception('请填写转赠奖励');
  1037. }
  1038. if (1 == $coupon_info['is_consume_reward'] && empty($consume_rewards)) {
  1039. throw new \Exception('请填写使用奖励');
  1040. }
  1041. }
  1042. if (1 == $coupon_info['is_member']) {
  1043. // 指定会员等级
  1044. if (empty($member_level_limits)) {
  1045. throw new \Exception('请指定可以领取优惠券的会员等级');
  1046. }
  1047. }
  1048. }
  1049. /**
  1050. * 获取商品的优惠券设置
  1051. * @Author: lun
  1052. * @DateTime: 2022/5/12 0012 14:49
  1053. * @Copyright: copyright (c) 2021 广东七件事集团
  1054. * @param $params
  1055. * @return bool|string[]
  1056. */
  1057. public static function getGoodCouponSetting($params)
  1058. {
  1059. // 后台页面组件展示
  1060. if ($params['type'] == 'show_component') {
  1061. return [
  1062. 'component' => 'com-coupon',
  1063. 'path' => Yii::$app->basePath . '/../addons/Coupon/backend/views/goods',
  1064. ];
  1065. } else {
  1066. try {
  1067. if (empty($params['setting'][AddonsEnum::ADDONS_NAME_COUPON])) return true;
  1068. $setting = $params['setting'][AddonsEnum::ADDONS_NAME_COUPON];
  1069. // 数据保存
  1070. $res = CouponGiveItemSetting::setGoodsSetting($params['mall_id'], $params['goods_id'], $setting);
  1071. if ($res === false) throw new Exception(CouponGiveItemSetting::getStaticError());
  1072. return true;
  1073. } catch (Exception $e) {
  1074. self::setStaticError($e->getMessage());
  1075. return false;
  1076. }
  1077. }
  1078. }
  1079. /**
  1080. * 优惠卷统计
  1081. * @return \yii\db\ActiveQuery
  1082. */
  1083. public function getStatistics()
  1084. {
  1085. return $this->hasOne(AddonsCouponStatistics::class, ['coupon_id' => 'id']);
  1086. }
  1087. /**
  1088. * 获取仅当前商户可使用的券
  1089. *
  1090. * @param array $coupon_list
  1091. * @param array $mch_ids 当前商户所有id
  1092. * @return array
  1093. */
  1094. public static function getMchUseCoupon($coupon_list, $mch_ids)
  1095. {
  1096. foreach ($coupon_list as $k => $mch_coupon_list) {
  1097. foreach ($mch_coupon_list as $key => $coupon) {
  1098. $mch_str = $coupon['mch_id'] ?? 0;
  1099. $coupon_mch_ids = explode(',', $mch_str);
  1100. // 判断当前券是否是可以在当前商户里面使用 不符合删除
  1101. if (count(array_diff($coupon_mch_ids, $mch_ids)) == count($coupon_mch_ids)) unset($coupon_list[$k][$key]);
  1102. }
  1103. }
  1104. return $coupon_list;
  1105. }
  1106. /**
  1107. * 点击使用优惠券前验证
  1108. *
  1109. * @param array $baseWhere
  1110. * @param int $mallId
  1111. * @param int $userId
  1112. * @param array $params
  1113. *
  1114. * @return bool
  1115. */
  1116. public static function jumpCheck(array $baseWhere, int $mallId, int $userId, array $params): bool
  1117. {
  1118. $fromType = $params['from_type'];
  1119. $userCouponId = $params['user_coupon_id'];
  1120. $goodsId = $params['goods_id'];
  1121. $select = 'id, coupon_id, mall_id';
  1122. if ($fromType == 2) {
  1123. if (empty(MallAddons::isInstall($mallId, AddonsEnum::ADDONS_NAME_STORE))) {
  1124. self::setStaticError('优惠券不存在');
  1125. return false;
  1126. }
  1127. $couponUserRelateModel = new StoreCouponUserRelate();
  1128. $goodsModel = new StoreGoods();
  1129. $couponUserRelateFind = $couponUserRelateModel::find();
  1130. $select .= ', from_type';
  1131. } else {
  1132. $couponUserRelateModel = new AddonsCouponUserRelate();
  1133. $goodsModel = new Goods();
  1134. $couponUserRelateFind = $couponUserRelateModel::find();
  1135. }
  1136. $couponUserRelateModel::paramPack([
  1137. 'where' => array_merge($baseWhere, [
  1138. ['id' => $userCouponId]
  1139. ]),
  1140. 'select' => $select
  1141. ], $couponUserRelateFind);
  1142. $couponUserRelate = $couponUserRelateFind->asArray()->one();
  1143. if (empty($couponUserRelate)) {
  1144. self::setStaticError('优惠券不存在');
  1145. return false;
  1146. }
  1147. // 门店创建的优惠券不校验
  1148. if (isset($couponUserRelate['from_type']) && $couponUserRelate['from_type'] != 1) {
  1149. return true;
  1150. }
  1151. $couponUserRelate['coupon'] = AddonsCoupon::find()
  1152. ->where([
  1153. // 'status' => StatusEnum::ENABLED,
  1154. 'id' => $couponUserRelate['coupon_id']
  1155. ])
  1156. ->select('id, is_giving, is_giving_use, is_not_allow_balance_pay, day_limit, type')
  1157. ->asArray()
  1158. ->one();
  1159. if (
  1160. !$goodsModel::find()
  1161. ->where([
  1162. 'mall_id' => $mallId,
  1163. 'id' => $goodsId,
  1164. 'status' => StatusEnum::ENABLED,
  1165. 'is_on_sale' => StatusEnum::ENABLED
  1166. ])
  1167. ->select('stock')
  1168. ->scalar()
  1169. ) {
  1170. self::setStaticError('该商品库存不足');
  1171. return false;
  1172. }
  1173. if (empty($couponUserRelate['coupon'])) {
  1174. self::setStaticError('优惠券不存在');
  1175. return false;
  1176. }
  1177. if ($couponUserRelate['coupon']['type'] == AddonsCouponEnum::EXCHANGE && !self::getTodayLimit($mallId, $userId, $couponUserRelate['coupon'], 0, $fromType)) {
  1178. self::setStaticError('今日使用已上限');
  1179. return false;
  1180. }
  1181. if (!self::checkCouponSourceIsFromSharing($couponUserRelate, $fromType)) {
  1182. self::setStaticError('当前优惠券只能转赠后使用');
  1183. return false;
  1184. }
  1185. return true;
  1186. }
  1187. /**
  1188. * 验证优惠券是否能使用
  1189. *
  1190. * @param array $user_relate_list
  1191. * @param $form
  1192. * @param int $fromType
  1193. *
  1194. * @return void
  1195. * @throws Exception
  1196. */
  1197. public static function checkIsAllowUse(array $user_relate_list, $form, int $fromType = 1)
  1198. {
  1199. if (empty($user_relate_list)) {
  1200. return;
  1201. }
  1202. // 判断是否有兑换券类型
  1203. $coupons = array_column($user_relate_list, 'coupon');
  1204. $types = array_column($coupons, 'type');
  1205. if (in_array(AddonsCouponEnum::EXCHANGE, $types)) {
  1206. // 多件商品下单的不能使用兑换券
  1207. if (count($form['order_goods']) > 1) {
  1208. throw new Exception('单件商品下单时才允许使用兑换券');
  1209. }
  1210. if (count(array_unique($types)) > 1) {
  1211. throw new Exception('不允许使用不同类型的优惠券');
  1212. }
  1213. // 判断兑换券使用数量
  1214. if (count($user_relate_list) > reset($form['order_goods'])['num']) {
  1215. throw new Exception('兑换券选择错误');
  1216. }
  1217. $allowAppointType = $fromType == 2 ? AddonsCouponEnum::APPOINT_TYPE6 : AddonsCouponEnum::APPOINT_TYPE2;
  1218. $appointTypes = array_column($coupons, 'appoint_type');
  1219. if (count(array_unique($appointTypes)) > 1 || !in_array($allowAppointType, $appointTypes)) {
  1220. throw new Exception('兑换券选择错误');
  1221. }
  1222. $coupon = reset($coupons);
  1223. if ($coupon['appoint_type'] != $allowAppointType) {
  1224. throw new Exception('兑换券选择错误');
  1225. }
  1226. // 按同个优惠券归类
  1227. $couponInfoById = [];
  1228. foreach ($coupons as $coupon) {
  1229. if (!isset($couponInfoById[$coupon['id']])) {
  1230. $couponInfoById[$coupon['id']] = [
  1231. 'coupon' => $coupon,
  1232. 'num' => 1
  1233. ];
  1234. } else {
  1235. $couponInfoById[$coupon['id']]['num']++;
  1236. }
  1237. }
  1238. // 校验每种优惠券每日可使用的数量
  1239. foreach ($couponInfoById as $couponInfo) {
  1240. if (self::getTodayLimit($form['mall_id'], $form['user_id'], $couponInfo['coupon'], $couponInfo['num'], $fromType, false) < 0) {
  1241. throw new Exception('当前兑换券今日可用数量超限');
  1242. }
  1243. }
  1244. }
  1245. }
  1246. }