StoreListForm.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. <?php
  2. /**
  3. * 重庆赤晓店信息科技有限公司
  4. * https://www.chixiaodian.com
  5. * Copyright (c) 2023 赤店商城 All rights reserved.
  6. */
  7. namespace app\modules\alliance\models;
  8. use app\constants\OptionSetting;
  9. use app\models\Cat;
  10. use app\models\Option;
  11. use app\models\WechatConfig;
  12. use app\models\FoodCat;
  13. use app\models\Banner;
  14. use app\models\District;
  15. use app\models\Goods;
  16. use app\models\Order;
  17. use app\models\SaasCategory;
  18. use app\models\Store;
  19. use app\plugins\food\models\FoodGoods;
  20. use app\plugins\food\models\FoodOrder;
  21. use yii\base\Model;
  22. use yii\helpers\Json;
  23. class StoreListForm extends Model
  24. {
  25. public $page = 1;
  26. public $limit = 12;
  27. // 经度
  28. public $longitude;
  29. // 纬度
  30. public $latitude;
  31. // 距离
  32. public $distance;
  33. // 0:默认排序 1:距离优先 2:好评优先 3:销量优先 4:低价优先 5:高价优先
  34. public $sort_type;
  35. // 搜索关键词
  36. public $keyword;
  37. // 分类id
  38. public $common_cat_id;
  39. // 城市id
  40. public $city_id;
  41. // 区县id
  42. public $district_id;
  43. // 筛选option信息
  44. public $option;
  45. //搜索商品数量
  46. public $store_goods_limit=6;
  47. public function rules()
  48. {
  49. return [
  50. [['longitude', 'latitude', 'keyword', 'option'], 'string'],
  51. [['longitude', 'latitude', 'keyword'], 'trim'],
  52. [['page', 'limit', 'common_cat_id', 'distance', 'sort_type', 'city_id', 'district_id'], 'integer'],
  53. [['page'], 'default', 'value' => 1],
  54. [['limit'], 'default', 'value' => 12],
  55. ['option', function ($attr, $params) {
  56. $data = Json::decode($this->option);
  57. if (!$data) {
  58. $this->addError($attr, "{$attr}数据格式错误。");
  59. }
  60. $this->option = $data;
  61. }],
  62. ['option', function ($attr, $params) {
  63. if (!is_array($this->option)) {
  64. $this->addError($attr, "{$attr}必须是一个数组。");
  65. return;
  66. }
  67. foreach ($this->option as $key => $opt) {
  68. if (!isset($opt['name']) || !isset($opt['value'])) {
  69. $this->addError($attr, "{$attr}[{$key}]['name']或{$attr}[{$key}]['value']不存在。");
  70. return;
  71. }
  72. }
  73. }],
  74. ];
  75. }
  76. public function list() {
  77. if (!$this->validate()) {
  78. return [
  79. 'code' => 1,
  80. 'msg' => $this->getErrorSummary(false)[0]
  81. ];
  82. }
  83. $query = Store::find()->where(['is_delete' => 0]);
  84. if ($this->keyword) {
  85. $query->andWhere(['like', 'name', $this->keyword]);
  86. }
  87. if ($this->common_cat_id > 0) {
  88. $query->andWhere(['category_id'=>$this->common_cat_id]);
  89. }
  90. if ($this->city_id) {
  91. $query->andWhere(['city_id' => $this->city_id]);
  92. }
  93. if ($this->district_id) {
  94. $query->andWhere(['district_id' => $this->district_id]);
  95. }
  96. if (is_array($this->option)) {
  97. foreach ($this->option as $choose) {
  98. $query->andWhere("JSON_CONTAINS(info,JSON_OBJECT('name',\"{$choose['name']}\"))")
  99. ->andWhere("JSON_CONTAINS(info, JSON_OBJECT('list',JSON_ARRAY(\"{$choose['value']}\")))");
  100. }
  101. }
  102. $query->select(['id', 'name', 'logo', 'coordinate', 'created_at', 'category_id', 'tags', 'sales', 'rank', 'per_spend','business_model','address']);
  103. // 0:默认排序 1:距离优先 2:好评优先 3:销量优先 4:低价优先 5:高价优先
  104. if (empty($this->sort_type)) {
  105. $list = $query->orderBy('created_at desc')->asArray()->all();
  106. }
  107. if ($this->sort_type == 2) {
  108. $list = $query->orderBy('rank desc')->asArray()->all();
  109. }
  110. if ($this->sort_type == 3) {
  111. $list = $query->orderBy('sales desc')->asArray()->all();
  112. }
  113. if ($this->sort_type == 4) {
  114. $list = $query->orderBy('per_spend asc')->asArray()->all();
  115. }
  116. if ($this->sort_type == 5) {
  117. $list = $query->orderBy('per_spend desc')->asArray()->all();
  118. }
  119. if (empty($list)) {
  120. $list = $query->asArray()->all();
  121. }
  122. $distance = array();
  123. foreach ($list as $index => $item) {
  124. $list[$index]['distance'] = -1;
  125. if (!empty($item['coordinate'])) {
  126. list($lati, $long) = explode(',', $item['coordinate']);
  127. if ($long && $this->longitude) {
  128. $from = [$this->longitude, $this->latitude];
  129. $to = [$long, $lati];
  130. $list[$index]['distance'] = $this->get_distance($from, $to, false, 2);
  131. if ((($list[$index]['distance'] > $this->distance) && $this->distance > 0) || $list[$index]['distance'] == -1){
  132. unset($list[$index]);
  133. continue;
  134. }
  135. }
  136. }
  137. $distance[] = $list[$index]['distance'];
  138. // if ($list[$index]['distance'] == -1){
  139. // unset($list[$index]);
  140. // } else {
  141. // $distance[] = $list[$index]['distance'];
  142. // }
  143. }
  144. if ($this->sort_type == 1) {
  145. $list = array_values($list);
  146. array_multisort($distance, SORT_ASC, $list);
  147. }
  148. $data = array_slice($list, ($this->page - 1) * $this->limit, $this->limit);
  149. foreach ($data as $index => $item) {
  150. $data[$index]['goods_list'] = $this->goods_list($item);
  151. $data[$index]['distance'] = $this->distance($item['distance']);
  152. $data[$index]['tags'] = !empty($item['tags']) ? Json::decode($item['tags']) : [];
  153. if($item['category_id']){
  154. $categoryInfo = SaasCategory::find()->where(['id' => $item['category_id'],'is_delete'=>0])->asArray()->one();
  155. //$categoryInfo = Cat::find()->where(['id'=>$item['category_id'],'is_delete'=>0])->asArray()->one();
  156. if($categoryInfo){
  157. $data[$index]['category_name'] = $categoryInfo['name'];
  158. }else{
  159. $data[$index]['category_name'] = '';
  160. }
  161. }else{
  162. $data[$index]['category_name'] = '';
  163. }
  164. if($item['business_model']==4){
  165. $goodsInfo = Goods::find()->alias('g')
  166. ->leftJoin(['fc' => FoodCat::tableName()], 'fc.id=g.cat_id')
  167. ->where(['g.store_id' => $item['id'], 'g.is_delete' => 0, 'g.status' => 1])->andWhere(['not like', 'g.name', '当面付'])
  168. ->andWhere(['!=', 'g.md_food_id', 0])->select("g.id,g.name,g.price,g.original_price,g.cover_pic")->limit($this->store_goods_limit)->asArray()->orderBy('g.sort')->all();
  169. }else{
  170. //获取6条商品记录
  171. $goodsInfo = Goods::find()->where(['store_id'=>$item['id'],'is_delete'=>0,'status'=>1])->select("id,name,price,original_price,cover_pic")->limit($this->store_goods_limit)->asArray()->orderBy('sort')->all();
  172. }
  173. if(count($goodsInfo) > 0){
  174. $data[$index]['goodsInfo'] = $goodsInfo;
  175. }else{
  176. $data[$index]['goodsInfo'] = [];
  177. }
  178. $wechatInfo = WechatConfig::find()->where(['store_id'=>$item['id'],'is_delete'=>0,'type'=>1])->asArray()->one();
  179. if($wechatInfo){
  180. $data[$index]['wechat_app_id'] = $wechatInfo['app_id'];
  181. }
  182. $alipayInfo = Option::get('alipay_config',$item['id'],'alipay');
  183. if($alipayInfo && $alipayInfo['value']){
  184. $alipayInfo = json_decode($alipayInfo['value'],true);
  185. $data[$index]['alipay_app_id'] = $alipayInfo['app_id'];
  186. }
  187. }
  188. $res = [
  189. 'list' => $data,
  190. 'page_count' => ceil(count($list) / $this->limit),
  191. 'row_count' => count($list)
  192. ];
  193. return [
  194. 'code' => 0,
  195. 'msg' => 'success',
  196. 'data' => $res
  197. ];
  198. }
  199. /**
  200. * 获取当前定位信息
  201. */
  202. private function locationInfo() {
  203. $tencent_map_key = Option::get('tencent_map_key', 0, 'saas', '')['value'];
  204. if (get_store_id() > 0) {
  205. $tencent_map_key = Option::get(OptionSetting::TENCENT_MAP_KEY, get_store_id(), 'pay', Option::get(OptionSetting::TENCENT_MAP_KEY, get_store_id(), 'store', '')['value'] ?: $tencent_map_key)['value'];
  206. }
  207. $place_url = 'https://apis.map.qq.com/ws/geocoder/v1/?location=' . $this->latitude . ',' . $this->longitude . '&key=' . $tencent_map_key;
  208. $json_place = file_get_contents($place_url);
  209. $place_arr = Json::decode($json_place);
  210. if ($place_arr && isset($place_arr['result'])) {
  211. $address = $place_arr['result']['ad_info'];
  212. $city = District::find()->select('id, name')->where(['name' => $address['city']])->one();
  213. $district_list = District::find()->select('id, name')->where(['parent_id' => $city['id']])->asArray()->all();
  214. $data['city'] = $city;
  215. $data['district_list'] = $district_list;
  216. $data['address'] = $address;
  217. $data['detail'] = $place_arr['result']['formatted_addresses']['recommend'];
  218. return [
  219. 'code' => 0,
  220. 'msg' => '定位成功',
  221. 'data' => $data
  222. ];
  223. } else {
  224. return [
  225. 'code' => 1,
  226. 'msg' => $place_arr['message'] ?: '定位失败'
  227. ];
  228. }
  229. }
  230. public function config() {
  231. if (!$this->validate()) {
  232. return [
  233. 'code' => 1,
  234. 'msg' => $this->getErrorSummary(false)[0]
  235. ];
  236. }
  237. $location = $this->locationInfo();
  238. if ($location['code'] > 0) {
  239. return $location;
  240. }
  241. $data = [
  242. 'code' => 0,
  243. 'msg' => 'success',
  244. 'data' => [
  245. 'location' => $location['data']
  246. ]
  247. ];
  248. $saas_category = SaasCategory::find()->where(['is_delete' => 0])->orderBy('sort DESC')->asArray()->all();
  249. $category_data = [];
  250. foreach ($saas_category as $category) {
  251. $category_data[] = [
  252. 'cat_num' => Store::find()->where(['is_delete' => 0, 'category_id' => $category['id']])->count(),
  253. 'common_cat_id' => $category['id'],
  254. 'cat_name' => $category['name'],
  255. 'pic' => $category['icon'],
  256. 'config' => Json::decode($category['option'])
  257. ];
  258. }
  259. $data['data']['search'] = $category_data;
  260. $data['data']['banner'] = Banner::find()->andWhere(['is_delete' => 0, 'type' => Banner::TYPE_SAAS])->select('pic_url, title, page_url, open_type')->orderBy('sort ASC')->asArray()->all();
  261. return $data;
  262. }
  263. /**
  264. * 根据起点坐标和终点坐标测距离
  265. * @param [array] $from [起点坐标(经纬度),例如:array(118.012951,36.810024)]
  266. * @param [array] $to [终点坐标(经纬度)]
  267. * @param [bool] $km 是否以公里为单位 false:米 true:公里(千米)
  268. * @param [int] $decimal 精度 保留小数位数
  269. * @return [string] 距离数值
  270. */
  271. public function get_distance($from, $to, $km = true, $decimal = 2)
  272. {
  273. sort($from);
  274. sort($to);
  275. $EARTH_RADIUS = 6370.996; // 地球半径系数
  276. $distance = $EARTH_RADIUS * 2 * asin(sqrt(pow(sin(($from[0] * pi() / 180 - $to[0] * pi() / 180) / 2), 2) + cos($from[0] * pi() / 180) * cos($to[0] * pi() / 180) * pow(sin(($from[1] * pi() / 180 - $to[1] * pi() / 180) / 2), 2))) * 1000;
  277. if ($km) {
  278. $distance = $distance / 1000;
  279. }
  280. return round($distance, $decimal);
  281. }
  282. private static function distance($distance)
  283. {
  284. if ($distance == -1) {
  285. return -1;
  286. }
  287. if ($distance > 1000) {
  288. $distance = round($distance / 1000, 2) . 'km';
  289. } else {
  290. $distance .= 'm';
  291. }
  292. return $distance;
  293. }
  294. private function goods_list($params){
  295. if ($params['category_id'] == 1) {
  296. $goods_list = FoodGoods::find()->where(['store_id' => $params['id'], 'is_delete' => 0])->select(['name', 'price', 'original_price', 'cover_pic'])->orderBy('virtual_sales desc, sort desc')->limit(6)->asArray()->all();
  297. foreach ($goods_list as &$goods_item) {
  298. $cover_pic = json_decode($goods_item['cover_pic'], true);
  299. $goods_item['pic'] = $cover_pic[0];
  300. }
  301. } else {
  302. $goods_list = Goods::find()->where(['store_id' => $params['id'], 'is_delete' => 0, 'status' => 1])->select(['name', 'price', 'original_price', 'cover_pic'])->orderBy('virtual_sales desc, sort desc')->limit(6)->asArray()->all();
  303. }
  304. return $goods_list;
  305. }
  306. public function sales() {
  307. // 上月第一天
  308. // $last_month_first_day = strtotime(date('Y-m-01') . ' -1 month');
  309. // 上月最后一天
  310. // $last_month_last_day = strtotime(date('Y-m-01') . ' -1 day');
  311. // 本月第一天
  312. $start_time = strtotime(date("Y-m-01"));
  313. // 当前时间
  314. $end_time = time();
  315. $stores = Store::findAll(['is_delete' => 0]);
  316. if ($stores) {
  317. foreach ($stores as $store) {
  318. $cache_key = 'cache_store_sales_num_' . $store->id;
  319. // 点餐
  320. if ($store->category_id == 1) {
  321. $count = FoodOrder::find()->where(['is_pay' => 1, 'store_id' => $store->id])->andWhere(['>=', 'created_at', $start_time])->andWhere(['<=', 'created_at', $end_time])->count();
  322. cache()->set($cache_key, $count);
  323. } else {
  324. $normal_count = Order::find()->where(['is_pay' => 1, 'store_id' => $store->id])->andWhere(['>=', 'created_at', $start_time])->andWhere(['<=', 'created_at', $end_time])->count();
  325. $scan_count = \app\plugins\scanCodePay\models\Order::find()->where(['is_pay' => 1, 'store_id' => $store->id])->andWhere(['>=', 'created_at', $start_time])->andWhere(['<=', 'created_at', $end_time])->count();
  326. cache()->set($cache_key, $normal_count + $scan_count);
  327. }
  328. // var_dump(cache()->get($cache_key));
  329. }
  330. }
  331. exit();
  332. }
  333. }