UserAddressRepository.php 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. <?php
  2. /**
  3. * User: Qinii
  4. * Date: 2020-06-03
  5. * Time: 09:41
  6. */
  7. namespace app\common\repositories\user;
  8. use app\common\repositories\BaseRepository;
  9. use app\common\dao\user\UserAddressDao as dao;
  10. use app\common\repositories\store\shipping\CityRepository;
  11. use app\entity\data\user\UserAddressEntity;
  12. use crmeb\services\HttpService;
  13. use think\facade\Log;
  14. /**
  15. * Class UserAddressRepository
  16. * @package app\common\repositories\user
  17. * @day 2020/6/3
  18. * @mixin dao
  19. */
  20. class UserAddressRepository extends BaseRepository
  21. {
  22. /**
  23. * @var dao
  24. */
  25. protected $dao;
  26. /**
  27. * UserAddressRepository constructor.
  28. * @param dao $dao
  29. */
  30. public function __construct(dao $dao)
  31. {
  32. $this->dao = $dao;
  33. }
  34. /**
  35. * @param int $id
  36. * @param int $uid
  37. * @return bool
  38. * @author Qinii
  39. */
  40. public function fieldExists(int $id,int $uid)
  41. {
  42. return $this->dao->userFieldExists($this->dao->getPk(),$id,$uid);
  43. }
  44. /**
  45. * @param int $uid
  46. * @return bool
  47. * @author Qinii
  48. */
  49. public function defaultExists(int $uid)
  50. {
  51. return $this->dao->userFieldExists('is_default',1,$uid);
  52. }
  53. /**
  54. * @param int $id
  55. * @return mixed
  56. * @throws \think\db\exception\DataNotFoundException
  57. * @throws \think\db\exception\DbException
  58. * @throws \think\db\exception\ModelNotFoundException
  59. * @author Qinii
  60. */
  61. public function checkDefault(int $id)
  62. {
  63. $res = $this->dao->getWhere([$this->dao->getPk() => $id]);
  64. return $res['is_default'];
  65. }
  66. /**
  67. * @param $province
  68. * @param $city
  69. * @return mixed
  70. * @author Qinii
  71. */
  72. public function getCityId($province,$city)
  73. {
  74. $make = app()->make(CityRepository::class);
  75. $provinceData = $make->getWhere(['name' => $province]);
  76. $cityData = $make->getWhere(['name' => $city,'parent_id' => $provinceData['city_id']]);
  77. if(!$cityData)$cityData = $make->getWhere([['name','like','直辖'.'%'],['parent_id' ,'=', $provinceData['city_id']]]);
  78. return $cityData['city_id'];
  79. }
  80. /**
  81. * @param $uid
  82. * @param $page
  83. * @param $limit
  84. * @return array
  85. * @author Qinii
  86. */
  87. public function getList($uid)
  88. {
  89. $list = $this->dao->getAll($uid)->order('is_default desc')->select();
  90. foreach ($list as &$address){
  91. $code_list = explode(',',$address['code_list']);
  92. if (isset($code_list[3])) {
  93. $address['town'] = \think\facade\Db::name('regions')->where('id', $code_list[3])->value('name');
  94. }
  95. }
  96. return compact('list');
  97. }
  98. /**
  99. * @param $id
  100. * @param $uid
  101. * @return array|\think\Model|null
  102. * @throws \think\db\exception\DataNotFoundException
  103. * @throws \think\db\exception\DbException
  104. * @throws \think\db\exception\ModelNotFoundException
  105. * @author Qinii
  106. */
  107. public function get($id,$uid)
  108. {
  109. $address = $this->dao->getWhere(['address_id' => $id,'uid' => $uid]) ?? [] ;
  110. $code_list = explode(',',$address['code_list']);
  111. if (isset($code_list[3])) {
  112. $address['town'] = \think\facade\Db::name('regions')->where('id', $code_list[3])->value('name');
  113. }
  114. return $address;
  115. }
  116. /**
  117. * 根据 地址ID 获取地址实体
  118. *
  119. * @param $addressId
  120. * @return UserAddressEntity
  121. */
  122. public function getAddressEntityByAddressId($addressId): UserAddressEntity
  123. {
  124. /** @var UserAddressEntity $addressEntity */
  125. $addressEntity = $this->dao->getAddressEntityByAddressId($addressId);
  126. return $addressEntity;
  127. }
  128. /**
  129. * 阿里云市场 - 地址解析
  130. *
  131. * 调用阿里云市场地址解析接口,将完整的地址文本解析为省/市/区/街道等结构化信息,
  132. * 并从 rrx_regions 表查询省/市/区/街道的 id 追加到返回数据中
  133. *
  134. * 接口文档: https://market.aliyun.com/detail/cmapi00067026
  135. * 请求方式: POST
  136. * 认证方式: Authorization:APPCODE
  137. *
  138. * @param string $address 待解析的地址文本,如 "北京市朝阳区建国路88号SOHO现代城A座1508室"
  139. * @return array 统一返回格式:
  140. * 成功 {"code":200,"msg":"成功","data":{...}}
  141. * 失败 {"code":-1,"msg":"错误信息","data":[]}
  142. */
  143. public function parse($address)
  144. {
  145. $config = config('third_party.aliyun_address_parse');
  146. $appCode = $config['appCode'] ?? '';
  147. if (empty($appCode)) {
  148. $msg = '地址解析失败:未配置阿里云市场 AppCode';
  149. Log::error($msg);
  150. return ['code' => -1, 'msg' => $msg, 'data' => []];
  151. }
  152. if (empty($address)) {
  153. $msg = '地址解析失败:地址文本为空';
  154. Log::error($msg);
  155. return ['code' => -2, 'msg' => $msg, 'data' => []];
  156. }
  157. $url = rtrim($config['gatewayUrl'] ?? 'https://slyparcel.market.alicloudapi.com', '/')
  158. . ($config['apiPath'] ?? '/v2/express_address/query');
  159. $header = ['Authorization:APPCODE ' . $appCode];
  160. try {
  161. $res = HttpService::postRequest($url, ['rawAddress' => $address], $header);
  162. if ($res === false) {
  163. $msg = '地址解析请求失败';
  164. Log::error($msg . ':' . $url . ',地址:' . $address);
  165. return ['code' => -3, 'msg' => $msg, 'data' => []];
  166. }
  167. $result = json_decode($res, true);
  168. if (json_last_error() !== JSON_ERROR_NONE) {
  169. $msg = '地址解析返回数据解析失败:' . json_last_error_msg();
  170. Log::error($msg . ',原始数据:' . $res);
  171. return ['code' => -4, 'msg' => $msg, 'data' => []];
  172. }
  173. // 接口返回格式:{"msg":"成功","success":true,"code":200,"data":{"orderNo":"...","data":{...}}}
  174. // 提取内层 data.data 作为解析结果
  175. if (isset($result['code']) && $result['code'] == 200 && isset($result['data']['data'])) {
  176. $data = $result['data']['data'];
  177. // 从 rrx_regions 表查询省/市/区/街道的 id
  178. $data = $this->appendRegionIds($data);
  179. return ['code' => 200, 'msg' => '成功', 'data' => $data];
  180. }
  181. // 如果外层 code 不是 200,返回接口的错误信息
  182. if (isset($result['code']) && $result['code'] != 200) {
  183. $msg = $result['msg'] ?? '地址解析失败';
  184. Log::error($msg . ',原始数据:' . $res);
  185. return ['code' => $result['code'], 'msg' => $msg, 'data' => []];
  186. }
  187. // 兜底:返回原始结果
  188. return ['code' => 200, 'msg' => '成功', 'data' => $result];
  189. } catch (\Throwable $e) {
  190. $msg = '地址解析异常:' . $e->getMessage();
  191. Log::error($msg . ',文件:' . $e->getFile() . ',行号:' . $e->getLine());
  192. return ['code' => -5, 'msg' => $msg, 'data' => []];
  193. }
  194. }
  195. /**
  196. * 根据省/市/区/街道名称从 rrx_regions 表查询对应的 id,并追加到返回数据中
  197. *
  198. * 注意:每次查询必须重新调用 Db::name('regions'),避免 ThinkPHP 查询构造器复用导致 where 条件叠加
  199. *
  200. * @param array $data 接口返回的地址解析数据,包含 province/city/district/street
  201. * @return array
  202. */
  203. private function appendRegionIds(array $data)
  204. {
  205. $provinceId = 0;
  206. $cityId = 0;
  207. $districtId = 0;
  208. $streetId = 0;
  209. // 查询省份 id (level = 1),未查到返回 0
  210. if (!empty($data['province'])) {
  211. $province = \think\facade\Db::name('regions')
  212. ->whereLike('name', '%' . $data['province'] . '%')
  213. ->where('level', 1)
  214. ->find();
  215. $provinceId = $province ? (int)$province['id'] : 0;
  216. }
  217. $data['province_id'] = $provinceId;
  218. // 查询城市 id (level = 2),未查到返回 0
  219. if (!empty($data['city'])) {
  220. $city = \think\facade\Db::name('regions')
  221. ->whereLike('name', '%' . $data['city'] . '%')
  222. ->where('level', 2)
  223. ->find();
  224. $cityId = $city ? (int)$city['id'] : 0;
  225. }
  226. $data['city_id'] = $cityId;
  227. // 查询区/县 id (level = 3),未查到返回 0
  228. if (!empty($data['district'])) {
  229. $district = \think\facade\Db::name('regions')
  230. ->whereLike('name', '%' . $data['district'] . '%')
  231. ->where('level', 3)
  232. ->find();
  233. $districtId = $district ? (int)$district['id'] : 0;
  234. }
  235. $data['district_id'] = $districtId;
  236. // 查询街道/乡镇 id (level = 4),未查到返回 0
  237. if (!empty($data['street'])) {
  238. $street = \think\facade\Db::name('regions')
  239. ->whereLike('name', '%' . $data['street'] . '%')
  240. ->where('level', 4)
  241. ->find();
  242. $streetId = $street ? (int)$street['id'] : 0;
  243. }
  244. $data['village'] = $data['street'];
  245. $data['village_id'] = $streetId;
  246. // 拼接 code_list,格式:province_id,city_id,district_id,street_id
  247. $codeList = implode(',', [$provinceId, $cityId, $districtId, $streetId]);
  248. $data['code_list'] = $codeList;
  249. return $data;
  250. }
  251. }