District.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. <?php
  2. /**
  3. * 厦门云联储网络科技有限公司
  4. * https://www.baokuaiyun.com
  5. * Copyright (c) 2023 爆块云 All rights reserved.
  6. */
  7. namespace app\models;
  8. use Yii;
  9. use yii\helpers\Json;
  10. use yii\db\ActiveRecord;
  11. use yii\behaviors\TimestampBehavior;
  12. use yii\caching\DbDependency;
  13. /**
  14. * This is the model class for table "{{%district}}".
  15. *
  16. * @property integer $id
  17. * @property integer $parent_id
  18. * @property string $citycode
  19. * @property string $adcode
  20. * @property string $name
  21. * @property string $lng
  22. * @property string $lat
  23. * @property string $level
  24. */
  25. class District extends \yii\db\ActiveRecord
  26. {
  27. const IS_DELETE_YES = 1;//已删除
  28. const IS_DELETE_NO = 0;//未删除
  29. /**
  30. * @inheritdoc
  31. */
  32. public static function tableName()
  33. {
  34. return '{{%district}}';
  35. }
  36. public function behaviors()
  37. {
  38. return [
  39. [
  40. 'class' => TimestampBehavior::class,
  41. 'attributes' => [
  42. ActiveRecord::EVENT_BEFORE_INSERT => ['updated_at', 'created_at'],
  43. ActiveRecord::EVENT_BEFORE_UPDATE => 'updated_at'
  44. ]
  45. ]
  46. ];
  47. }
  48. /**
  49. * @inheritdoc
  50. */
  51. public function rules()
  52. {
  53. return [
  54. [['parent_id', 'is_delete'], 'integer'],
  55. [['citycode', 'adcode', 'name', 'lng', 'lat', 'level'], 'required'],
  56. [['citycode', 'adcode', 'name', 'lng', 'lat', 'level'], 'string', 'max' => 255],
  57. ];
  58. }
  59. /**
  60. * @inheritdoc
  61. */
  62. public function attributeLabels()
  63. {
  64. return [
  65. 'id' => 'ID',
  66. 'parent_id' => 'Parent ID',
  67. 'citycode' => 'Citycode',
  68. 'adcode' => 'Adcode',
  69. 'name' => 'Name',
  70. 'lng' => '经度',
  71. 'lat' => '纬度',
  72. 'level' => 'Level',
  73. 'is_delete' => 'IS DELETE',
  74. 'created_at' => 'created_at',
  75. 'updated_at' => 'updated_at',
  76. ];
  77. }
  78. public function getCity()
  79. {
  80. return $this->hasMany(District::className(), ['parent_id'=>'id'])->where(['level'=>'city']);
  81. }
  82. /**
  83. * 获取所有的省市区
  84. * @return District[]
  85. */
  86. public static function getAll()
  87. {
  88. return self::getDistrictInfoAllField();
  89. if (cache()->get('district_info'))
  90. return cache()->get('district_info');
  91. $province = self::find()->where(['level' => 'province'])->select(['id' ,'name', 'level'])->asArray()->all();
  92. foreach ($province as &$v1) {
  93. $v1['cityAll'] = self::find()->where(['level' => 'city', 'parent_id' => $v1['id']])
  94. ->select(['id','name', 'level'])->asArray()->all();
  95. if (is_array($v1['cityAll'])) {
  96. foreach ($v1['cityAll'] as &$v2) {
  97. $v2['districtAll'] = self::find()->where(['level' => 'district', 'parent_id' => $v2['id']])
  98. ->select(['id', 'name', 'level'])->asArray()->all();
  99. if (is_array($v2['districtAll'])) {
  100. foreach ($v2['districtAll'] as &$v3) {
  101. $v3['townAll'] = self::find()
  102. ->where(['level' => 'town', 'parent_id' => $v3['id']])
  103. ->select(['id', 'name', 'level'])
  104. ->asArray()
  105. ->all();
  106. if (is_array($v3['townAll'])) {
  107. foreach ($v3['townAll'] as &$v4) {
  108. $v4['villageAll'] = self::find()
  109. ->where(['level' => 'village', 'parent_id' => $v4['id']])
  110. ->select(['id', 'name', 'level'])
  111. ->asArray()
  112. ->all();
  113. }
  114. }
  115. }
  116. }
  117. }
  118. }
  119. }
  120. cache()->set('district_info', $province);
  121. return $province;
  122. }
  123. public static function getDistrictInfoAllField(...$args)
  124. {
  125. $childKey = 0;
  126. $cacheKey = array_merge(['district_info_all_field'], $args);
  127. if ($cacheV = cache()->get($cacheKey)) {
  128. return $cacheV;
  129. }
  130. $province = [];
  131. $city = [];
  132. $district = [];
  133. $town = [];
  134. $village = [];
  135. // 分批查询
  136. $query = self::find()
  137. ->where(['is_delete' => 0])
  138. ->select(['id', 'parent_id', 'name', 'level', 'lng', 'lat', 'adcode'])
  139. ->asArray();
  140. foreach ($query->batch(1000) as $rows) {
  141. foreach ($rows as $item) {
  142. switch ($item['level']) {
  143. case 'province':
  144. $province[] = $item;
  145. break;
  146. case 'city':
  147. $city[] = $item;
  148. break;
  149. case 'district':
  150. $district[] = $item;
  151. break;
  152. case 'town':
  153. $town[] = $item;
  154. break;
  155. case 'village':
  156. $village[] = $item;
  157. break;
  158. }
  159. }
  160. unset($rows);
  161. }
  162. // 按 parent_id 索引,优化循环
  163. $cityByParent = [];
  164. $districtByParent = [];
  165. $townByParent = [];
  166. $villageByParent = [];
  167. foreach ($city as $item) {
  168. $cityByParent[$item['parent_id']][] = $item;
  169. }
  170. foreach ($district as $item) {
  171. $districtByParent[$item['parent_id']][] = $item;
  172. }
  173. foreach ($town as $item) {
  174. $townByParent[$item['parent_id']][] = $item;
  175. }
  176. foreach ($village as $item) {
  177. $villageByParent[$item['parent_id']][] = $item;
  178. }
  179. // 构建层级结构
  180. foreach ($province as &$v) {
  181. $v['cityAll'] = $cityByParent[$v['id']] ?? [];
  182. foreach ($v['cityAll'] as &$i_city) {
  183. $i_city['districtAll'] = $districtByParent[$i_city['id']] ?? [];
  184. foreach ($i_city['districtAll'] as &$i_district) {
  185. $i_district['townAll'] = $townByParent[$i_district['id']] ?? [];
  186. foreach ($i_district['townAll'] as &$i_town) {
  187. $i_town['villageAll'] = $villageByParent[$i_town['id']] ?? [];
  188. }
  189. }
  190. }
  191. }
  192. // 设置缓存
  193. $sqlDependency = new \yii\caching\DbDependency(['sql' => self::find()->select('MAX(updated_at)')->createCommand()->getRawSql()]);
  194. cache()->set($cacheKey, $province, 86400 * 100, $sqlDependency);
  195. return $province;
  196. }
  197. public static function getAll2($childKey = 0)
  198. {
  199. ini_set('memory_limit', '1000M');
  200. set_time_limit(0);
  201. $cacheKey = array_merge(['district_info_all_field'], func_get_args());
  202. if ($cacheV = cache()->get($cacheKey))
  203. return $cacheV;
  204. $province = [];
  205. $city = [];
  206. $district = [];
  207. $town = [];
  208. $village = [];
  209. $list = self::find()->where(['is_delete' => 0])->select(['id' ,'parent_id' ,'name', 'level', 'lng', 'lat','adcode'])->asArray()->all();
  210. foreach($list as $item){
  211. if($item['level'] == 'province'){
  212. $province[] = $item;
  213. }
  214. if($item['level'] == 'city'){
  215. $city[] = $item;
  216. }
  217. if($item['level'] == 'district'){
  218. $district[] = $item;
  219. }
  220. if($item['level'] == 'town'){
  221. $town[] = $item;
  222. }
  223. if($item['level'] == 'village'){
  224. $village[] = $item;
  225. }
  226. }
  227. foreach ($province as &$v) {
  228. foreach($city as $d => $i_city){
  229. if($i_city['parent_id'] == $v['id']){
  230. foreach($district as $c => $i_district){
  231. $childKey == 0 && $i_district['townAll'] = [];
  232. $childKey == 1 && $i_district['children'] = [];
  233. if($i_district['parent_id'] == $i_city['id']){
  234. foreach($town as $b => $i_town){
  235. $childKey == 0 && $i_town['villageAll'] = [];
  236. $childKey == 1 && $i_town['children'] = [];
  237. if($i_town['parent_id'] == $i_district['id']){
  238. foreach($village as $a => $i_village){
  239. if($i_village['parent_id'] == $i_town['id']){
  240. $childKey == 0 && $i_town['villageAll'][] = $i_village;
  241. $childKey == 1 && $i_town['children'][] = $i_village;
  242. unset($village[$a]);
  243. }
  244. }
  245. $childKey == 0 && $i_district['townAll'][] = $i_town;
  246. $childKey == 1 && $i_district['children'][] = $i_town;
  247. unset($town[$b]);
  248. }
  249. }
  250. $childKey == 0 && $i_city['districtAll'][] = $i_district;
  251. $childKey == 1 && $i_city['children'][] = $i_district;
  252. unset($district[$c]);
  253. }
  254. }
  255. $childKey == 0 && $v['cityAll'][] = $i_city;
  256. $childKey == 1 && $v['children'][] = $i_city;
  257. unset($city[$d]);
  258. }
  259. }
  260. }
  261. $sqlDependency = new \yii\caching\DbDependency(['sql'=>Self::find()->select("max(updated_at)")->createCommand()->getRawSql()]);
  262. cache()->set($cacheKey, $province, 86400 * 100, $sqlDependency);
  263. return $province;
  264. }
  265. /**
  266. * 验证起送规则
  267. */
  268. public static function verificationOffer ($address, $price)
  269. {
  270. $offerInfo = OfferPrice::findOne(['store_id' => get_store_id(), 'is_delete' => 0]);
  271. if (empty($offerInfo)) {
  272. return true;
  273. }
  274. $detail = Json::decode($offerInfo->detail)?: [];
  275. foreach ($detail as $val) {
  276. $offerPrice = $val['offer_price'];
  277. foreach ($val['province_list'] as $v) {
  278. if ($v['id'] == $address['province_id']
  279. || $v['id'] == $address['city_id']
  280. || $v['id'] == $address['district_id']) {
  281. if ($price >= $offerPrice) {
  282. return true;
  283. } else {
  284. return [
  285. 'msg' => '差'.($offerPrice - $price).'元',
  286. 'price' => $offerPrice - $price
  287. ];
  288. }
  289. }
  290. }
  291. }
  292. if ($price >= $offerInfo->price) {
  293. return true;
  294. } else {
  295. return [
  296. 'msg' => '差'.($offerInfo->price - $price).'元',
  297. 'price' => $offerInfo->price - $price
  298. ];
  299. }
  300. }
  301. /**
  302. * 获取格式化省市数据
  303. * @param string $type
  304. * @param int $value_type
  305. * @return District[]
  306. */
  307. public function getFormatCity($type, $value_type)
  308. {
  309. $cache_key = 'format_dis_data_'.$type.'_'.$value_type;
  310. if (cache()->get($cache_key))
  311. return [
  312. 'code' => 0,
  313. 'msg' => 'success',
  314. 'data' => cache()->get($cache_key)
  315. ];
  316. $province = self::find()
  317. ->where(['is_delete' => '0','level' => 'province'])
  318. ->select(['id' ,'name'])
  319. ->asArray()->all();
  320. //新增统一查询快速分组
  321. $areaAll = self::find()->where(['is_delete' => '0'])->andWhere(['!=', 'level', 'province' ])
  322. ->select(['parent_id','id','name'])
  323. ->asArray()->all();
  324. $childrenAll = [];
  325. foreach ($areaAll as $v){
  326. if ($value_type == 1) {
  327. $v['value'] = $v['id'];
  328. } else {
  329. $v['value'] = $v['name'];
  330. }
  331. $v['label'] = $v['name'];
  332. $childrenAll[$v['parent_id']][] = $v;
  333. }
  334. $data = [];
  335. foreach ($province as $v1) {
  336. $tmp = [];
  337. if ($value_type == 1) {
  338. $tmp['value'] = $v1['id'];
  339. } else {
  340. $tmp['value'] = $v1['name'];
  341. }
  342. $tmp['label'] = $v1['name'];
  343. $tmp['children'] = [];
  344. $v1['cityAll'] = $childrenAll[$v1['id']] ?? [];
  345. foreach ($v1['cityAll'] as $v2) {
  346. $v2['districtAll'] = $childrenAll[$v2['id']] ?? [];
  347. $tmp_area = [];
  348. if ($type == 'area') {
  349. foreach ($v2['districtAll'] as $v3) {
  350. $tmp_1 = $v3;
  351. $tmp_1['children'] = $childrenAll[$v3['id']] ?? [];
  352. $tmp_area[] = $tmp_1;
  353. }
  354. }
  355. $tmp['children'][] = [
  356. 'value' => $value_type == 1 ? $v2['id'] : $v2['name'],
  357. 'label' => $v2['name'],
  358. 'children' => $tmp_area
  359. ];
  360. }
  361. $data[] = $tmp;
  362. }
  363. cache()->set($cache_key, $data);
  364. return [
  365. 'code' => 0,
  366. 'msg' => 'success',
  367. 'data' => $data
  368. ];
  369. }
  370. /**
  371. * User: chiyanying
  372. * Date: 2020/9/26
  373. * Time: 11:07
  374. * @param $city_name
  375. * @return mixed
  376. * Notes:根据城市名称获取城市id
  377. */
  378. public static function getCityId($city_name)
  379. {
  380. $city = District::find()->where(['level'=>'city'])
  381. ->andWhere(['like','name',$city_name])->one();
  382. return $city->id;
  383. }
  384. /**
  385. * User: chiyanying
  386. * Date: 2020/9/28
  387. * Time: 11:16
  388. * @param $long
  389. * @param $lat
  390. * @return mixed
  391. * Notes:根据经纬获取详细地址
  392. * 例如
  393. {"status":"1","info":"OK","infocode":"10000","regeocode":{"formatted_address":"\u6cb3\u5357\u7701\u6d1b\u9633\u5e02\u6d1b\u9f99\u533a\u5173\u6797\u8857\u9053\u5e7f\u5229\u8857\u5b9d\u9f99\u5e7f\u573a","addressComponent":{"country":"\u4e2d\u56fd","province":"\u6cb3\u5357\u7701","city":"\u6d1b\u9633\u5e02","citycode":"0379","district":"\u6d1b\u9f99\u533a","adcode":"410311","township":"\u5173\u6797\u8857\u9053","towncode":"410311004000","neighborhood":{"name":[],"type":[]},"building":{"name":[],"type":[]},"streetNumber":{"street":"\u5c55\u89c8\u8def","number":"27\u53f7","location":"112.465602,34.614936","direction":"\u4e1c\u5357","distance":"52.5022"},"businessAreas":{"@attributes":{"type":"list"},"businessArea":[]}}}}
  394. */
  395. public static function getAddressByLocation($long, $lat)
  396. {
  397. $url = "https://restapi.amap.com/v3/geocode/regeo?output=xml&key=48d5eef8af838b0bee145cf2eaed599d&location={$long},{$lat}&radius=1000&extensions=base&batch=false&roadlevel=0";
  398. $header = array(
  399. 'Accept: application/json',
  400. );
  401. $curl = curl_init();
  402. //设置抓取的url
  403. curl_setopt($curl, CURLOPT_URL, $url);
  404. //设置头文件的信息作为数据流输出
  405. curl_setopt($curl, CURLOPT_HEADER, 0);
  406. // 超时设置,以秒为单位
  407. curl_setopt($curl, CURLOPT_TIMEOUT, 1);
  408. // 超时设置,以毫秒为单位
  409. // curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
  410. // 设置请求头
  411. curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
  412. //设置获取的信息以文件流的形式返回,而不是直接输出。
  413. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  414. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
  415. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
  416. //执行命令
  417. $data = curl_exec($curl);
  418. // 显示错误信息
  419. if (curl_error($curl)) {
  420. \Yii::warning('获取城市信息失败');
  421. } else {
  422. // 打印返回的内容
  423. curl_close($curl);
  424. //禁止引用外部xml实体
  425. libxml_disable_entity_loader(true);
  426. $values = json_decode(json_encode(simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
  427. return $values['regeocode'];
  428. }
  429. }
  430. /**
  431. * User: chiyanying
  432. * Date: 2020/9/28
  433. * Time: 11:24
  434. * @param string $province_name
  435. * @param string $city_name
  436. * @param string $district_name
  437. * Notes:根据省市区相关
  438. */
  439. public static function getDistrictByAddress($province_name='',$city_name='',$district_name=''){
  440. $province = District::find()->where(['level'=>'province'])
  441. ->andWhere(['like','name',$province_name])->asArray()->one();
  442. $city = District::find()->where(['level'=>'city','parent_id'=>$province['id']])
  443. ->andWhere(['like','name',$city_name])->asArray()->one();
  444. $district = District::find()->where(['level'=>'district','parent_id'=>$city['id']])
  445. ->andWhere(['like','name',$district_name])->asArray()->one();
  446. return [
  447. 'province'=>$province,
  448. 'city'=>$city,
  449. 'district'=>$district,
  450. ];
  451. }
  452. /**
  453. * Undocumented function
  454. *
  455. * @Author LGL 24963@qq.com
  456. * @DateTime 2021-03-19
  457. * @desc:
  458. * @param [type] $param
  459. * @return object
  460. */
  461. public static function getDistrict($param)
  462. {
  463. if (is_array($param)) {
  464. $id = $param['id'];
  465. } else {
  466. $id = $param;
  467. }
  468. $str = District::find()->where(['id' => $id])->select('id, name, parent_id, level')->one();
  469. return $str;
  470. }
  471. public static function getAdcodeById($id) {
  472. $model = self::findOne($id);
  473. if(!$model){
  474. return '';
  475. }
  476. return $model->adcode;
  477. }
  478. }