DadaAdapter.php 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. <?php
  2. namespace common\components\delivery\services\adapter;
  3. use common\components\delivery\helpers\HttpClient;
  4. use common\components\delivery\helpers\SignHelper;
  5. use common\components\delivery\platform\dada\Dada;
  6. use Yii;
  7. /**
  8. * 达达(DaDa)配送平台适配器
  9. *
  10. * 基于达达开放平台 API 接口规范进行封装
  11. * API 文档参考:达达开放平台 - 接口调用协议与签名算法
  12. */
  13. class DadaAdapter extends Dada implements Adapter
  14. {
  15. /**
  16. * 获取已开通城市列表
  17. *
  18. * 通过此接口可获取到达达配送业务所有已开通的城市列表信息
  19. *
  20. * @return array 包含城市列表的响应数据
  21. * 示例结构:
  22. * [
  23. * 'status' => 'success',
  24. * 'code' => 0,
  25. * 'msg' => '成功',
  26. * 'result' => [
  27. * 'cityList' => [
  28. * [
  29. * 'cityCode' => '110101',
  30. * 'cityName' => '东城区',
  31. * 'provinceName' => '北京市',
  32. * ],
  33. * // ... 更多城市
  34. * ]
  35. * ]
  36. * ]
  37. */
  38. public function cityList()
  39. {
  40. // 业务参数为空时使用空字符串
  41. $body = '';
  42. // 构建完整的 API 请求参数
  43. $payload = $this->buildRequestPayload('cityList', $body);
  44. // 发送 POST 请求
  45. $resp = HttpClient::post("{$this->baseUrl}/cityList", $payload);
  46. return $resp;
  47. }
  48. /**
  49. * 创建订单(指派订单给配送员)
  50. *
  51. * 根据达达官方文档实现下单功能
  52. *
  53. * @param array $data 订单数据,结构参考:
  54. * [
  55. * 'order_id' => '订单编号' (必需), // 商户系统内部订单编号
  56. * 'shop_no' => '门店编号' (必需), // 门店编号
  57. * 'transporter_id' => 配送员ID (必需), // 指定配送员ID(可选,为空则由系统自动分配)
  58. * 'receiver_name' => '收货人名称' (可选),
  59. * 'receiver_address' => '收货地址' (可选),
  60. * 'receiver_phone' => '收货人电话' (可选),
  61. * 'receiver_lng' => 经度 (可选),
  62. * 'receiver_lat' => 纬度 (可选),
  63. * 'cargo_price' => 货物价格(分) (可选),
  64. * 'tips' => 小费(分) (可选),
  65. * 'distance' => 距离(米) (可选),
  66. * 'prepare_time' => 商品准备时间 (可选), // 单位:秒
  67. * 'remark' => '备注' (可选),
  68. * 'callback' => '回调地址' (可选),
  69. * ]
  70. * @return array API 响应结果
  71. */
  72. public function createOrder($data)
  73. {
  74. // 验证必需字段
  75. if (empty($data['order_id']) || empty($data['shop_no'])) {
  76. return [
  77. 'code' => -1,
  78. 'msg' => '缺少必需参数: order_id 和 shop_no'
  79. ];
  80. }
  81. // 构建业务参数(按达达 API 规范)
  82. $apiData = [
  83. 'order_id' => $data['order_id'],
  84. 'shop_no' => $data['shop_no'],
  85. ];
  86. // 如果指定了配送员ID,添加到请求
  87. if (isset($data['transporter_id'])) {
  88. $apiData['transporter_id'] = (int)$data['transporter_id'];
  89. }
  90. // 可选参数
  91. if (!empty($data['receiver_name'])) {
  92. $apiData['receiver_name'] = $data['receiver_name'];
  93. }
  94. if (!empty($data['receiver_address'])) {
  95. $apiData['receiver_address'] = $data['receiver_address'];
  96. }
  97. if (!empty($data['receiver_phone'])) {
  98. $apiData['receiver_phone'] = $data['receiver_phone'];
  99. }
  100. if (isset($data['receiver_lng']) && $data['receiver_lng'] !== '') {
  101. $apiData['receiver_lng'] = (double)$data['receiver_lng'];
  102. }
  103. if (isset($data['receiver_lat']) && $data['receiver_lat'] !== '') {
  104. $apiData['receiver_lat'] = (double)$data['receiver_lat'];
  105. }
  106. if (isset($data['cargo_price'])) {
  107. $apiData['cargo_price'] = (int)$data['cargo_price'];
  108. }
  109. if (isset($data['tips'])) {
  110. $apiData['tips'] = (int)$data['tips'];
  111. }
  112. if (isset($data['distance'])) {
  113. $apiData['distance'] = (int)$data['distance'];
  114. }
  115. if (isset($data['prepare_time'])) {
  116. $apiData['prepare_time'] = (int)$data['prepare_time'];
  117. }
  118. if (!empty($data['remark'])) {
  119. $apiData['remark'] = $data['remark'];
  120. }
  121. if (!empty($data['callback'])) {
  122. $apiData['callback'] = $data['callback'];
  123. }
  124. // 将业务参数转换为 JSON 字符串
  125. $body = json_encode($apiData, JSON_UNESCAPED_UNICODE);
  126. // 构建完整的 API 请求参数
  127. $payload = $this->buildRequestPayload('orderAddRequest', $body);
  128. // 发送 POST 请求
  129. $resp = HttpClient::post("{$this->baseUrl}/orderAddRequest", $payload);
  130. return $resp;
  131. }
  132. /**
  133. * 获取订单运费报价
  134. *
  135. * 根据达达官方文档实现订单运费估价功能
  136. *
  137. * @param array $data 运费估价参数,结构参考:
  138. * [
  139. * 'shop_no' => '门店编号' (必需),
  140. * 'origin_id' => '原始订单ID' (必需), // 商户侧订单编号
  141. * 'receiver_address' => '收货地址' (必需),
  142. * 'receiver_phone' => '收货人电话' (必需),
  143. * 'receiver_lng' => 经度 (必需),
  144. * 'receiver_lat' => 纬度 (必需),
  145. * 'cargo_price' => 货物价格(分) (可选), // 商品价格,单位为分
  146. * 'distance' => 距离(米) (可选),
  147. * 'prepare_time' => 商品准备时间 (可选), // 单位:秒
  148. * ]
  149. * @return array|null 运费报价结果
  150. */
  151. public function getPrice($data)
  152. {
  153. // 验证必需字段
  154. if (empty($data['shop_no']) || empty($data['origin_id']) ||
  155. empty($data['receiver_address']) || empty($data['receiver_phone']) ||
  156. !isset($data['receiver_lng']) || !isset($data['receiver_lat'])) {
  157. return [
  158. 'code' => -1,
  159. 'msg' => '缺少必需参数'
  160. ];
  161. }
  162. // 构建业务参数(按达达 API 规范)
  163. $apiData = [
  164. 'shop_no' => $data['shop_no'],
  165. 'origin_id' => $data['origin_id'],
  166. 'receiver_address' => $data['receiver_address'],
  167. 'receiver_phone' => $data['receiver_phone'],
  168. 'receiver_lng' => (double)$data['receiver_lng'],
  169. 'receiver_lat' => (double)$data['receiver_lat'],
  170. ];
  171. // 可选参数
  172. if (isset($data['cargo_price'])) {
  173. $apiData['cargo_price'] = (int)$data['cargo_price'];
  174. }
  175. if (isset($data['distance'])) {
  176. $apiData['distance'] = (int)$data['distance'];
  177. }
  178. if (isset($data['prepare_time'])) {
  179. $apiData['prepare_time'] = (int)$data['prepare_time'];
  180. }
  181. // 将业务参数转换为 JSON 字符串
  182. $body = json_encode($apiData, JSON_UNESCAPED_UNICODE);
  183. // 构建完整的 API 请求参数
  184. $payload = $this->buildRequestPayload('queryDeliverFee', $body);
  185. // 发送 POST 请求
  186. $resp = HttpClient::post("{$this->baseUrl}/queryDeliverFee", $payload);
  187. // 处理响应
  188. if (isset($resp['status']) && $resp['status'] === 'success' && $resp['code'] == 0) {
  189. $result = $resp['result'] ?? [];
  190. return [
  191. 'code' => 0,
  192. 'platform' => 'dada',
  193. 'deliver_fee' => $result['deliver_fee'] ?? 0, // 配送费(分)
  194. 'distance' => $result['distance'] ?? 0, // 距离(米)
  195. 'duration' => $result['duration'] ?? 0, // 配送时间(分钟)
  196. 'tips' => $result['tips'] ?? 0, // 小费(分)
  197. ];
  198. }
  199. return [
  200. 'code' => $resp['code'] ?? -1,
  201. 'msg' => $resp['msg'] ?? 'Unknown error',
  202. 'data' => null
  203. ];
  204. }
  205. /**
  206. * 生成随机数字符串(备用方法,如需要使用)
  207. *
  208. * @param int $length 长度
  209. * @return string 随机字符串
  210. */
  211. protected function generateNonceStr($length = 16)
  212. {
  213. $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  214. $str = '';
  215. for ($i = 0; $i < $length; $i++) {
  216. $str .= $chars[random_int(0, strlen($chars) - 1)];
  217. }
  218. return $str;
  219. }
  220. public function addTip($data)
  221. {
  222. // TODO: Implement addTip() method.
  223. }
  224. public function buildPriceRequest($data, $orderTime)
  225. {
  226. // TODO: Implement buildPriceRequest() method.
  227. }
  228. }