BargainUserOrder.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. <?php
  2. namespace addons\Bargain\common\models;
  3. use addons\Bargain\common\enums\BargainEnum;
  4. use addons\Bargain\common\models\BargainOrder;
  5. use common\enums\RedisKeyEnum;
  6. use common\enums\StatusEnum;
  7. use common\helpers\LockHelper;
  8. use common\models\base\BaseActiveRecord;
  9. use common\models\goods\Goods;
  10. use common\models\user\User;
  11. use common\models\user\UserLevel;
  12. use Yii;
  13. /**
  14. * This is the model class for table "qimall_addons_bargain_user_order".
  15. *
  16. * @property int $id
  17. * @property int $mall_id
  18. * @property int $user_id
  19. * @property int $bargain_order_id 砍价订单ID
  20. * @property float $price 砍价的金额
  21. * @property string $token
  22. * @property int $status 状态[-1:删除;0:禁用;1启用]
  23. * @property int $created_at
  24. * @property int $updated_at
  25. */
  26. class BargainUserOrder extends BaseActiveRecord
  27. {
  28. /**
  29. * {@inheritdoc}
  30. */
  31. public static function tableName()
  32. {
  33. return '{{%addons_bargain_user_order}}';
  34. }
  35. /**
  36. * {@inheritdoc}
  37. */
  38. public function rules()
  39. {
  40. return [
  41. [['mall_id', 'user_id', 'bargain_order_id', 'status', 'created_at', 'updated_at'], 'integer'],
  42. [['price'], 'number'],
  43. [['token'], 'string', 'max' => 255],
  44. ];
  45. }
  46. /**
  47. * {@inheritdoc}
  48. */
  49. public function attributeLabels()
  50. {
  51. return [
  52. 'id' => 'ID',
  53. 'mall_id' => 'Mall ID',
  54. 'user_id' => 'User ID',
  55. 'bargain_order_id' => '砍价订单ID',
  56. 'price' => '砍价的金额',
  57. 'token' => 'Token',
  58. 'status' => '状态[-1:删除;0:禁用;1启用]',
  59. 'created_at' => 'Created At',
  60. 'updated_at' => 'Updated At',
  61. ];
  62. }
  63. // 关联模型
  64. public function getUser()
  65. {
  66. return $this->hasOne(User::class, ['id' => 'user_id']);
  67. }
  68. public function getOrder()
  69. {
  70. return $this->hasOne(BargainOrder::class, ['id' => 'bargain_order_id']);
  71. }
  72. public static function userJoinBargain($params)
  73. {
  74. $stock_locks = [];
  75. $t = Yii::$app->db->beginTransaction();
  76. try {
  77. $lock_key = RedisKeyEnum::LOCK_BARGAIN_ORDER . $params['bargain_order_id'];
  78. $identification = uniqid();
  79. if (!LockHelper::lock($lock_key, $identification, 30, 60)) throw new \Exception('当前访问人数过多');
  80. $stock_locks[$lock_key] = $identification;
  81. $user_join_order_list = self::findAll(['mall_id' => $params['mall_id'], 'bargain_order_id' => $params['bargain_order_id'], 'status' => StatusEnum::ENABLED]);
  82. foreach ($user_join_order_list as $val) {
  83. if ($val['user_id'] == $params['user_id']) throw new \Exception('您已参与过本次砍价');
  84. }
  85. $bargain_order = BargainOrder::findOne(['mall_id' => $params['mall_id'], 'id' => $params['bargain_order_id']]);
  86. if (empty($bargain_order)) throw new \Exception('砍价活动已关闭');
  87. if ($bargain_order->order_status == BargainEnum::ORDER_SUCCESS) throw new \Exception('砍价已完成');
  88. if ($bargain_order->order_status == BargainEnum::ORDER_FAILURE) throw new \Exception('砍价失败');
  89. $active = BargainActive::findOne(['mall_id' => $params['mall_id'], 'id' => $bargain_order->bargain_active_id]);
  90. if (($bargain_order->created_at + $active['time'] * 3600) <= time()) throw new \Exception('砍价已结束');
  91. $bargain_goods_data = json_decode($bargain_order->bargain_goods_data,true);
  92. if ($active->stock <= 0 && $bargain_goods_data['type'] == 1) throw new \Exception('商品已售罄,暂时无法砍价哦~');
  93. if ($bargain_order->user_id != $params['user_id'] ||
  94. ($bargain_order->user_id == $params['user_id'] && $active->is_creator_exempt == 0)) {
  95. //判断用户等级能否参与砍价
  96. self::checkUserBargain($params, $active);
  97. //判断用户能否参与砍价
  98. self::checkUserBargainTime($params, $active, $bargain_order);
  99. }
  100. // 已砍价金额
  101. $total_price = array_sum(array_column($user_join_order_list, 'price'));
  102. // 参与砍价人数
  103. $total_people = count($user_join_order_list);
  104. // 剩余可砍价金额
  105. $reset_price = floatval($bargain_order->price) - floatval($bargain_order->min_price) - $total_price;
  106. $reset_price = $reset_price <= 0 ? 0 : $reset_price;
  107. if ($reset_price <= 0){
  108. //todo
  109. //推送一条信息给发布砍价的用户
  110. throw new \Exception('已砍至最低价');
  111. }
  112. $price = self::intToFloat(self::getPrice($reset_price, $total_people, $bargain_order->bargain_goods_data));
  113. $model = new self();
  114. $model->user_id = $params['user_id'];
  115. $model->mall_id = $params['mall_id'];
  116. $model->price = $price;
  117. $model->token = \Yii::$app->security->generateRandomString();
  118. $model->bargain_order_id = $params['bargain_order_id'];
  119. $res = $model->save();
  120. if ($res == false) throw new \Exception(self::getStaticError());
  121. $active->participant += 1;
  122. if ($price == $reset_price) $active->min_price_goods += 1;
  123. $res = $active->save();
  124. if ($res == false) throw new \Exception(BargainActive::getStaticError());
  125. $bargain_order->reduce_price+=$price;
  126. if($bargain_order->reduce_price==$bargain_order->min_price) $bargain_order->order_status = BargainEnum::ORDER_SUCCESS;
  127. $res = $bargain_order->save();
  128. if ($res == false) throw new \Exception($bargain_order->getErrorMessage());
  129. $t->commit();
  130. LockHelper::batchUlock($stock_locks);
  131. $info = [
  132. 'nickname' => $model->user->nickname ?? '',
  133. 'price' => $model->price,
  134. 'user_id' => $model->user_id,
  135. ];
  136. $start = substr($bargain_order->user->mobile,0,3);
  137. $end = substr($bargain_order->user->mobile,-4,4);
  138. $mobile = $start.'****'.$end;
  139. $bargain = [
  140. 'nickname' => $bargain_order->user->nickname ?? '',
  141. 'mobile' => $mobile,
  142. 'avatar_url' => $bargain_order->user->avatar_url ?? '',
  143. 'user_id' => $bargain_order->user_id,
  144. ];
  145. return ['info' => $info, 'bargain' => $bargain];
  146. } catch (\Exception $e) {
  147. LockHelper::batchUlock($stock_locks);
  148. $t->rollback();
  149. self::$static_error = $e->getMessage();
  150. return false;
  151. }
  152. }
  153. /**
  154. * @desc:砍价算法
  155. * @Author: hua
  156. * @Date: 2022/1/3
  157. * @Time: 15:46
  158. * @Copyright: copyright (c) 2021 广东七件事集团
  159. * @param $reset_price
  160. * @param $people
  161. * @param $bargain_goods_data
  162. * @return float|int
  163. */
  164. private static function getPrice($reset_price, $people, $bargain_goods_data)
  165. {
  166. $data = json_decode($bargain_goods_data);
  167. if (isset($data->bargain_people)) {
  168. $dataPeople = intval($data->bargain_people);
  169. if ($dataPeople != 0) {
  170. if ($people == $dataPeople - 1) {
  171. return $reset_price * 100;
  172. }
  173. if ($people >= $dataPeople) return 0;
  174. }
  175. }
  176. if ($people < intval($data->bargain_human)) {
  177. $min = $data->bargain_first_min_price > $data->bargain_first_max_price ? $data->bargain_first_max_price : $data->bargain_first_min_price;
  178. $max = $data->bargain_first_min_price > $data->bargain_first_max_price ? $data->bargain_first_min_price : $data->bargain_first_max_price;
  179. } else {
  180. $min = min($data->bargain_second_min_price, $data->bargain_second_max_price);
  181. $max = max($data->bargain_second_min_price, $data->bargain_second_max_price);
  182. }
  183. $money = mt_rand($min * 100, $max * 100);
  184. if ($money > $reset_price * 100) $money = (round($reset_price, 2)) * 100;
  185. return intval($money);
  186. }
  187. // int转float
  188. private static function intToFloat($int)
  189. {
  190. // 去除原来计算保留两个小数点
  191. return bcdiv($int, 100, 2);
  192. }
  193. /**
  194. * 指定位置插入字符串
  195. * @param $str string 原字符串
  196. * @param $i integer 插入位置
  197. * @param $substr string 插入字符串
  198. * @return string 处理后的字符串
  199. */
  200. private static function insertToStr($str, $i, $substr)
  201. {
  202. //指定插入位置前的字符串
  203. $startStr = substr($str, 0, $i);
  204. //指定插入位置后的字符串
  205. $lastStr = substr($str, -2);
  206. //将插入位置前,要插入的,插入位置后三个字符串拼接起来
  207. $str = $startStr . $substr . $lastStr;
  208. //返回结果
  209. return $str;
  210. }
  211. public function sendMessage($mall_id,$open_id,$total_price,$min_price,$temp_id){
  212. $notice_wechat = \Yii::$app->services->setting->mall->get(5, 'notice_wechat');
  213. if(!empty($notice_wechat['is_miniapp_priority'])){
  214. $mini_program = \Yii::$app->services->setting->mall->get($mall_id, 'mini_program');
  215. if (!empty($mini_program)) {
  216. \Yii::$app->params['wechatMiniProgramConfig'] = [
  217. 'app_id' => $mini_program['app_id'],
  218. 'secret' => $mini_program['secret'],
  219. 'key' => $mini_program['pay_secret'] ?? '',
  220. 'cert_path' => $mini_program['cert_pem_path'] ?? '',
  221. 'key_path' => $mini_program['key_pem_path'] ?? '',
  222. 'notify_url' => ''//回调地址
  223. ];
  224. }
  225. $app = \Yii::$app->wechat->miniProgram;
  226. }else{
  227. $wechat = \Yii::$app->services->setting->mall->get($mall_id, 'wechat');
  228. if ($wechat) {
  229. \Yii::$app->params['wechatConfig'] = [
  230. 'app_id' => $wechat['app_id'] ?? '',
  231. 'secret' => $wechat['secret'] ?? '',
  232. 'token' => $wechat['token'] ?? '',
  233. 'aes_key' => $wechat['aes_key'] ?? '',
  234. ];
  235. }
  236. $app = \Yii::$app->wechat->app;
  237. }
  238. $send_data = [
  239. 'first' => '你发布的商品已经砍价成功',
  240. 'keyword1' => '砍掉'.$total_price."元",
  241. 'keyword2' => date("Y-m-d H:i:s"),
  242. 'keyword3' => $min_price,
  243. 'remark' => '已砍至最低价,快来买我吧!!'
  244. ];
  245. $pageUrl = '/plugins2/bargain/joinBargain?id=' . '1';
  246. $h5_url = 'https://h5.qijianshirjcs.com' . "/h5/#" . $pageUrl;
  247. $app->template_message->send([
  248. 'touser' => $open_id,
  249. 'template_id' => $temp_id,
  250. 'url' => $h5_url,
  251. 'data' => $send_data,
  252. ]);
  253. }
  254. public static function checkUserBargain($params, $active)
  255. {
  256. $bargain_limit_user_level = empty($active->bargain_limit_user_level) ? [] : $active->bargain_limit_user_level;
  257. if (empty($bargain_limit_user_level)) {
  258. return true;
  259. }
  260. $user = User::find()
  261. ->where(['id' => $params['user_id'], 'status' => StatusEnum::ENABLED])
  262. ->select('id,level')
  263. ->asArray()
  264. ->one();
  265. if (empty($user)) {
  266. throw new \Exception('用户信息不存在');
  267. }
  268. $mall_level_info = UserLevel::find()
  269. ->where([
  270. 'mall_id' => $params['mall_id'],
  271. 'status' => StatusEnum::ENABLED
  272. ])
  273. ->andWhere(['in', 'id', $bargain_limit_user_level])
  274. ->select('id,level')
  275. ->asArray()
  276. ->all();
  277. $join_level = $mall_level_info ? array_column($mall_level_info, 'level') : [];
  278. if (in_array(0, $bargain_limit_user_level)) {
  279. $join_level[] = 0;
  280. }
  281. if (!in_array($user['level'], $join_level)) {
  282. throw new \Exception('很抱歉!您暂时无法帮砍~');
  283. }
  284. }
  285. public static function checkUserBargainTime($params, $active, $bargain_order)
  286. {
  287. //统计用户参与该活动的次数
  288. $model = (new \yii\db\Query())
  289. ->from('qimall_addons_bargain_order o')
  290. ->innerJoin('qimall_addons_bargain_user_order u', 'o.id = u.bargain_order_id')
  291. ->where([
  292. 'o.bargain_active_id' => $active->id,
  293. 'u.user_id' => $params['user_id'],
  294. 'o.status' => StatusEnum::ENABLED,
  295. 'u.status' => StatusEnum::ENABLED
  296. ]);
  297. if ($active['is_creator_exempt']) {
  298. $model->andWhere(['<>', 'o.user_id', $params['user_id']]);
  299. }
  300. $records = $model->select('o.user_id,u.bargain_order_id,u.user_id as bargain_user_id')
  301. ->all();
  302. $count = count($records);
  303. if (!empty($active->bargain_time)) {
  304. if ($count >= $active->bargain_time) {
  305. throw new \Exception('您的帮砍次数不足,去看看其它活动吧~');
  306. }
  307. }
  308. if ($count > 0 && empty($active->is_repeat_bargain)) {
  309. //不允许帮同个用户但不同砍价订单重复帮砍
  310. foreach ($records as $record) {
  311. if ($record['user_id'] == $bargain_order->user->id) {
  312. throw new \Exception('您已经帮ta砍过一次啦,不可以重复帮砍哦~');
  313. }
  314. }
  315. }
  316. }
  317. }