MyOrderLogic.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. <?php
  2. namespace plugin\saiadmin\app\logic\order;
  3. use plugin\saiadmin\app\model\order\RentalOrder;
  4. use plugin\saiadmin\app\model\system\SystemUser;
  5. use plugin\saiadmin\basic\think\BaseLogic;
  6. use plugin\saiadmin\exception\ApiException;
  7. use support\think\Db;
  8. /**
  9. * “我的订单”查询逻辑。
  10. *
  11. * 参照旧项目 OrderSearch::query_orders_new() 的筛选和可见范围语义,
  12. * 新系统只查询 sa_rental_order,不调用旧 ThinkPHP 项目。
  13. */
  14. class MyOrderLogic extends BaseLogic
  15. {
  16. private const DEVICE_TYPES = [
  17. 'vehicle' => [107],
  18. 'k-battery' => [100, 102],
  19. 'bluetooth' => [104],
  20. 'power-bank' => [1, 101],
  21. 'password' => [103],
  22. 'password-bluetooth' => [105],
  23. ];
  24. private const DEVICE_NAMES = [
  25. 1 => '充电底座', 100 => 'K霸宝', 101 => '充电宝', 102 => 'K霸宝 Blue',
  26. 103 => '密码线充', 104 => '蓝牙线充', 105 => '密码蓝牙线充', 107 => '车载充电器',
  27. ];
  28. private const STATUSES = [
  29. 0 => ['code' => 'pending', 'name' => '待支付'],
  30. 1 => ['code' => 'using', 'name' => '使用中'],
  31. 2 => ['code' => 'done', 'name' => '已完成'],
  32. 3 => ['code' => 'timeout', 'name' => '超时完成'],
  33. 4 => ['code' => 'rent_failed', 'name' => '租借失败'],
  34. 5 => ['code' => 'deduct_failed', 'name' => '扣款失败'],
  35. 6 => ['code' => 'refund', 'name' => '申请退款'],
  36. ];
  37. public function __construct()
  38. {
  39. $this->model = new RentalOrder();
  40. }
  41. public function indexList(array $filters): array
  42. {
  43. return $this->listOrders($filters);
  44. }
  45. /**
  46. * 当前账号下级代理管理的订单分页列表。
  47. */
  48. public function subordinateIndexList(array $filters): array
  49. {
  50. return $this->listOrders($filters, true);
  51. }
  52. private function listOrders(array $filters, bool $subordinate = false): array
  53. {
  54. $page = max(1, (int) request()->input('page', 1));
  55. $limit = min(100, max(1, (int) request()->input('limit', 20)));
  56. $result = $this->buildQuery($filters, $subordinate)
  57. ->order('o.rent_at', 'desc')
  58. ->order('o.id', 'desc')
  59. ->paginate(['page' => $page, 'list_rows' => $limit])
  60. ->toArray();
  61. $result['data'] = array_map(fn (array $item) => $this->formatOrder($item), $result['data'] ?? []);
  62. $result['summary'] = $this->summary($filters, $subordinate);
  63. return $result;
  64. }
  65. public function readOrder(int $id): array
  66. {
  67. return $this->readScopedOrder($id);
  68. }
  69. /**
  70. * 获取下级代理管理的订单详情。
  71. */
  72. public function readSubordinateOrder(int $id): array
  73. {
  74. return $this->readScopedOrder($id, true);
  75. }
  76. private function readScopedOrder(int $id, bool $subordinate = false): array
  77. {
  78. if ($id <= 0) {
  79. throw new ApiException('订单参数错误');
  80. }
  81. $order = $this->buildQuery([], $subordinate)->where('o.id', $id)->find();
  82. if (empty($order)) {
  83. throw new ApiException('订单不存在或无权查看');
  84. }
  85. return $this->formatOrder($order, true);
  86. }
  87. private function buildQuery(array $filters, bool $subordinate = false)
  88. {
  89. $query = Db::table('sa_rental_order')->alias('o')
  90. ->leftJoin('sa_device d', 'd.id = o.device_id AND d.delete_time IS NULL')
  91. ->leftJoin('sa_merchant rent', 'rent.id = o.rent_merchant_id AND rent.delete_time IS NULL')
  92. ->leftJoin('sa_merchant back', 'back.id = o.return_merchant_id AND back.delete_time IS NULL')
  93. ->leftJoin('sa_system_user manager', 'manager.id = o.manage_agent_id AND manager.delete_time IS NULL')
  94. ->whereNull('o.delete_time')
  95. ->field(implode(',', [
  96. 'o.id,o.order_sn,o.sub_order_sn,o.transaction_id,o.merchant_order_no,o.device_id,o.device_type,o.source,o.pay_type',
  97. 'o.user_id,o.user_name,o.user_phone,o.rent_merchant_id,o.return_merchant_id,o.rent_at,o.return_at,o.status',
  98. 'o.paid_amount,o.income_amount,o.refund_amount,o.package_description,o.remark,o.manage_agent_id,o.create_time',
  99. 'd.goods_sn AS device_sn,d.device_code AS device_code,d.goods_name AS device_name',
  100. 'rent.name AS rent_merchant_name,back.name AS return_merchant_name',
  101. 'manager.realname AS manager_realname,manager.username AS manager_username,manager.phone AS manager_phone',
  102. ]));
  103. if ($subordinate) {
  104. $managerIds = $this->subordinateManagerIds();
  105. if ($managerIds === []) {
  106. $query->where('o.id', 0);
  107. } else {
  108. $query->whereIn('o.manage_agent_id', $managerIds);
  109. }
  110. } elseif ($this->currentAdminId() !== 1) {
  111. $query->where('o.manage_agent_id', $this->currentAdminId());
  112. }
  113. $category = trim((string) ($filters['category'] ?? 'all'));
  114. if ($category !== 'all' && isset(self::DEVICE_TYPES[$category])) {
  115. $query->whereIn('o.device_type', self::DEVICE_TYPES[$category]);
  116. }
  117. $status = trim((string) ($filters['status'] ?? 'all'));
  118. if ($status === 'today') {
  119. $query->whereTime('o.rent_at', 'today');
  120. } elseif ($status !== 'all') {
  121. $statusValue = array_search($status, array_column(self::STATUSES, 'code'), true);
  122. if ($statusValue !== false) {
  123. $query->where('o.status', (int) $statusValue);
  124. }
  125. }
  126. $this->like($query, 'o.order_sn', $filters['order_no'] ?? '');
  127. $this->like($query, 'o.user_phone', $filters['phone'] ?? '');
  128. $this->like($query, 'o.user_id', $filters['user_id'] ?? '');
  129. $this->like($query, 'rent.name', $filters['merchant'] ?? '');
  130. $this->like($query, 'd.goods_sn', $filters['device_sn'] ?? '');
  131. $this->like($query, 'o.transaction_id', $filters['transaction_id'] ?? '');
  132. $agentId = (int) ($filters['agent_id'] ?? 0);
  133. if ($agentId > 0) {
  134. $query->where('o.manage_agent_id', $agentId);
  135. }
  136. $source = trim((string) ($filters['source'] ?? ''));
  137. if (in_array($source, ['1', '2'], true)) {
  138. $query->where('o.source', (int) $source);
  139. }
  140. $payType = trim((string) ($filters['pay_type'] ?? ''));
  141. if (in_array($payType, ['deposit', 'free'], true)) {
  142. $query->where('o.pay_type', $payType);
  143. }
  144. $startAt = trim((string) ($filters['start_at'] ?? ''));
  145. $endAt = trim((string) ($filters['end_at'] ?? ''));
  146. if ($startAt !== '') {
  147. $query->where('o.rent_at', '>=', $startAt . ' 00:00:00');
  148. }
  149. if ($endAt !== '') {
  150. $query->where('o.rent_at', '<=', $endAt . ' 23:59:59');
  151. }
  152. return $query;
  153. }
  154. private function summary(array $filters, bool $subordinate = false): array
  155. {
  156. $result = ['all' => 0, 'today' => 0];
  157. foreach (self::STATUSES as $status) {
  158. $result[$status['code']] = 0;
  159. }
  160. $rows = $this->buildQuery(array_merge($filters, ['status' => 'all']), $subordinate)
  161. ->field('o.status,COUNT(*) AS total')->group('o.status')->select()->toArray();
  162. foreach ($rows as $row) {
  163. $status = self::STATUSES[(int) $row['status']] ?? null;
  164. if ($status) {
  165. $result[$status['code']] = (int) $row['total'];
  166. $result['all'] += (int) $row['total'];
  167. }
  168. }
  169. $result['today'] = $this->buildQuery(array_merge($filters, ['status' => 'today']), $subordinate)->count();
  170. return $result;
  171. }
  172. private function formatOrder(array $order, bool $detail = false): array
  173. {
  174. $status = self::STATUSES[(int) ($order['status'] ?? 0)] ?? self::STATUSES[0];
  175. $source = (int) ($order['source'] ?? 0);
  176. $payType = (string) ($order['pay_type'] ?? 'deposit');
  177. return [
  178. 'id' => (int) $order['id'], 'order_sn' => (string) $order['order_sn'], 'sub_order_sn' => (string) ($order['sub_order_sn'] ?? ''),
  179. 'transaction_id' => (string) ($order['transaction_id'] ?? ''), 'merchant_order_no' => (string) ($order['merchant_order_no'] ?? ''),
  180. 'device_type' => (int) ($order['device_type'] ?? 0), 'device_type_name' => self::DEVICE_NAMES[(int) ($order['device_type'] ?? 0)] ?? '未知设备',
  181. 'device_name' => (string) ($order['device_name'] ?? ''), 'device_sn' => (string) ($order['device_sn'] ?? ''), 'device_code' => (string) ($order['device_code'] ?? ''),
  182. 'source' => $source, 'source_name' => $source === 1 ? '微信' : ($source === 2 ? '支付宝' : '未知'),
  183. 'pay_type' => $payType, 'pay_type_name' => $payType === 'free' ? '免押金' : '押金',
  184. 'user_id' => (int) ($order['user_id'] ?? 0), 'user_name' => (string) ($order['user_name'] ?? ''), 'user_phone' => (string) ($order['user_phone'] ?? ''),
  185. 'rent_merchant' => (string) ($order['rent_merchant_name'] ?? ''), 'return_merchant' => (string) ($order['return_merchant_name'] ?? ''),
  186. 'rent_at' => (string) ($order['rent_at'] ?? ''), 'return_at' => (string) ($order['return_at'] ?? ''),
  187. 'status' => $status['code'], 'status_name' => $status['name'], 'paid_amount' => (float) ($order['paid_amount'] ?? 0),
  188. 'income_amount' => (float) ($order['income_amount'] ?? 0), 'refund_amount' => (float) ($order['refund_amount'] ?? 0),
  189. 'package_description' => (string) ($order['package_description'] ?? ''), 'remark' => (string) ($order['remark'] ?? ''),
  190. 'manage_agent_id' => (int) ($order['manage_agent_id'] ?? 0), 'create_time' => (string) ($order['create_time'] ?? ''),
  191. 'manager_name' => (string) (($order['manager_realname'] ?? '') ?: ($order['manager_username'] ?? '未分配')),
  192. 'manager_phone' => (string) ($order['manager_phone'] ?? ''),
  193. 'detail' => $detail,
  194. ];
  195. }
  196. private function currentAdminId(): int
  197. {
  198. return (int) ($this->adminInfo['id'] ?? 0);
  199. }
  200. /** @return int[] */
  201. private function subordinateManagerIds(): array
  202. {
  203. $currentId = $this->currentAdminId();
  204. if ($currentId <= 0) {
  205. return [];
  206. }
  207. if ($currentId === 1) {
  208. return array_map('intval', SystemUser::where('id', '<>', 1)
  209. ->whereNull('delete_time')->column('id'));
  210. }
  211. $dept = $this->adminInfo['deptList'] ?? [];
  212. if (empty($dept['id']) || !isset($dept['level'])) {
  213. return [];
  214. }
  215. $prefix = (string) $dept['level'] . (int) $dept['id'] . ',';
  216. $deptIds = Db::table('sa_system_dept')->whereNull('delete_time')
  217. ->whereLike('level', $prefix . '%')->column('id');
  218. if ($deptIds === []) {
  219. return [];
  220. }
  221. return array_map('intval', SystemUser::whereIn('dept_id', $deptIds)
  222. ->whereNull('delete_time')->column('id'));
  223. }
  224. private function like($query, string $field, mixed $value): void
  225. {
  226. $value = trim((string) $value);
  227. if ($value !== '') {
  228. $query->whereLike($field, '%' . $value . '%');
  229. }
  230. }
  231. }