dataLists(new OrderLists()); } /** * @notes 指定诊单下已支付支付单列表(创建/编辑处方业务订单时多选关联) * @return \think\response\Json */ public function paidOrdersForDiagnosis() { $diagnosisId = (int) $this->request->get('diagnosis_id', 0); if ($diagnosisId <= 0) { return $this->fail('诊单ID必填'); } $exceptPo = (int) $this->request->get('prescription_order_id', 0); $lists = OrderLogic::listPaidOrdersForDiagnosis( $diagnosisId, $this->adminId, $this->adminInfo, $exceptPo > 0 ? $exceptPo : null ); return $this->success('', ['lists' => $lists]); } /** * @notes 同步企业微信对外收款账单到订单 * 员工直接在企业微信发起收款(未经过后台创建)时,通过此接口拉取并创建订单 * @return \think\response\Json */ public function syncWechatWorkBills() { $beginStr = $this->request->get('begin_time', date('Y-m-d', strtotime('-7 days'))); $endStr = $this->request->get('end_time', date('Y-m-d')); $beginTime = strtotime($beginStr . ' 00:00:00'); $endTime = strtotime($endStr . ' 23:59:59'); if ($beginTime >= $endTime) { return $this->fail('开始时间必须早于结束时间'); } if ($endTime - $beginTime > 31 * 86400) { return $this->fail('企业微信接口限制:时间范围不能超过31天'); } $result = OrderLogic::syncFromWechatWorkBills($beginTime, $endTime); if (!empty($result['errors']) && $result['created'] === 0 && $result['updated'] === 0) { return $this->fail(implode(';', $result['errors'])); } return $this->success('同步完成', $result); } /** * @notes 调试:测试企业微信对外收款 API 连接(返回原始响应,用于排查同步为0的问题) * @return \think\response\Json */ public function syncWechatWorkBillsDebug() { $beginStr = $this->request->get('begin_time', date('Y-m-d')); $endStr = $this->request->get('end_time', date('Y-m-d')); $payeeUserid = trim((string)$this->request->get('payee_userid', '')); $beginTime = strtotime($beginStr . ' 00:00:00'); $endTime = strtotime($endStr . ' 23:59:59'); $service = new \app\common\service\wechat\WechatWorkExternalPayService(); $config = config('pay.wechat_work', []); $debug = [ 'config_ok' => !empty($config['corp_id']) && !empty($config['external_pay_secret']), 'corp_id' => $config['corp_id'] ? substr($config['corp_id'], 0, 8) . '***' : '(空)', 'time_range' => [$beginStr, $endStr], 'payee_userid' => $payeeUserid ?: '(未指定,查全部)', 'access_token' => $service->getAccessToken() ? '已获取' : '获取失败', ]; $resp = $service->getBillList($beginTime, $endTime, $payeeUserid, '', 100); $debug['api_errcode'] = $resp['errcode'] ?? -999; $debug['api_errmsg'] = $resp['errmsg'] ?? ''; $debug['bill_count'] = count($resp['bill_list'] ?? []); $debug['next_cursor'] = !empty($resp['next_cursor']) ? '有' : '无'; $debug['page1'] = $resp; if (!empty($resp['next_cursor']) && $debug['bill_count'] === 0) { $resp2 = $service->getBillList($beginTime, $endTime, $payeeUserid, $resp['next_cursor'], 100); $debug['page2_errcode'] = $resp2['errcode'] ?? -999; $debug['page2_bill_count'] = count($resp2['bill_list'] ?? []); $debug['page2_sample'] = isset($resp2['bill_list'][0]) ? $resp2['bill_list'][0] : null; } if ($debug['bill_count'] === 0 && empty($payeeUserid)) { $admins = \app\common\model\auth\Admin::where('work_wechat_userid', '<>', '') ->whereNull('delete_time')->column('work_wechat_userid', 'id'); $debug['try_payee_userids'] = array_values(array_filter(array_unique($admins))); $debug['tip'] = '若 try_payee_userids 有值,可传 payee_userid=xxx 重试(如 ?payee_userid=GaoXingLiang)'; } return $this->success('调试信息', $debug); } /** * @notes 今日收益(按角色权限) * @return \think\response\Json */ public function todayRevenue() { $result = OrderLogic::todayRevenue($this->adminId, $this->adminInfo); return $this->data($result); } /** * @notes 订单统计(挂号费/药品费用) * @return \think\response\Json */ public function orderStats() { $params = $this->request->get(); $result = OrderLogic::orderStats($params, (int) $this->adminId, $this->adminInfo); return $this->data($result); } /** * @notes 订单详情 * @return \think\response\Json */ public function detail() { $params = (new OrderValidate())->post()->goCheck('detail'); $orderId = (int)$params['id']; $detail = OrderLogic::detail($orderId); if (!$detail) { return $this->fail('订单不存在'); } $this->logOrderAction($orderId, 'view_detail', '查看订单详情'); return $this->data($detail); } /** * @notes 支付单操作日志(单条订单,分页) */ public function actionLogs() { $orderId = (int)$this->request->get('order_id', 0); if ($orderId <= 0) { return $this->fail('order_id 必填'); } $pageNo = (int)$this->request->get('page_no', 1); $pageSize = (int)$this->request->get('page_size', 20); $data = OrderActionLogLogic::listByOrderId($orderId, $pageNo, $pageSize); return $this->success('', $data); } /** * @notes 按人统计操作次数(时间范围内) * @return \think\response\Json */ public function actionLogStats() { $start = trim((string)$this->request->get('start_time', '')); $end = trim((string)$this->request->get('end_time', '')); if ($start === '' || $end === '') { $endTime = time(); $startTime = $endTime - 7 * 86400; } else { $startTime = strtotime($start . ' 00:00:00') ?: 0; $endTime = strtotime($end . ' 23:59:59') ?: 0; } if ($startTime <= 0 || $endTime < $startTime) { return $this->fail('时间范围无效'); } $limit = (int)$this->request->get('limit', 50); $rows = OrderActionLogLogic::statsByAdmin($startTime, $endTime, $limit); return $this->success('', [ 'list' => $rows, 'start_time' => date('Y-m-d H:i:s', $startTime), 'end_time' => date('Y-m-d H:i:s', $endTime), ]); } /** * @notes 创建订单 * @return \think\response\Json */ public function create() { $params = (new OrderValidate())->post()->goCheck('create'); $params['creator_id'] = $this->adminId; $params['payment_channel'] = (string)$this->request->post('payment_channel', 'normal'); $params['require_payment_slip_audit'] = (int)$this->request->post('require_payment_slip_audit', 0); // 创建方式:优先取前端透传的 create_type,否则按 payment_channel 派生(fubei→fubei,其余 normal) $createTypeReq = (string)$this->request->post('create_type', ''); $params['create_type'] = in_array($createTypeReq, ['normal', 'wechat_work', 'fubei'], true) ? $createTypeReq : ($params['payment_channel'] === 'fubei' ? 'fubei' : 'normal'); $result = OrderLogic::create($params); if (!$result) { return $this->fail(OrderLogic::getError()); } $this->logOrderAction((int)$result->id, 'create', '创建支付单(普通/付呗等)'); return $this->success('创建成功', $result->toArray()); } /** * @notes 创建企业微信对外收款订单 * 返回 order_no,供员工在企业微信发起收款时作为商户订单号(out_trade_no)使用 * 收款成功后,微信支付回调会自动更新订单为已支付 * @return \think\response\Json */ public function createForWechatWork() { $params = (new OrderValidate())->post()->goCheck('create'); $params['creator_id'] = $this->adminId; $params['create_type'] = 'wechat_work'; $result = OrderLogic::createForWechatWork($params); if (!$result) { return $this->fail(OrderLogic::getError()); } $ord = $result['order'] ?? null; if (is_array($ord) && !empty($ord['id'])) { $this->logOrderAction((int)$ord['id'], 'create_wechat_work', '创建支付单(企业微信对外收款)'); } return $this->success('创建成功,请在企业微信收款时使用以下订单号', [ 'order_no' => $result['order_no'], 'order' => $result['order'], 'tip' => '在企业微信发起对外收款时,将上述订单号填写为「商户订单号」', ]); } /** * @notes 批量指派医助(写入 order.assistant_id,并记操作日志) */ public function assignAssistant() { $params = (new OrderValidate())->post()->goCheck('assign_assistant'); $rawIds = $params['order_ids'] ?? []; if (!\is_array($rawIds) || $rawIds === []) { return $this->fail('请选择订单'); } $orderIds = array_map('intval', $rawIds); $orderIds = array_values(array_unique(array_filter($orderIds, static fn (int $id) => $id > 0))); if ($orderIds === []) { return $this->fail('请选择有效订单'); } $allowed = []; foreach ($orderIds as $id) { $order = \app\common\model\Order::find($id); if (!$order) { continue; } if (!$this->canEditOrder($order)) { return $this->fail('无权限操作订单:' . (string) ($order->order_no ?? $id)); } $allowed[] = $id; } if ($allowed === []) { return $this->fail('没有可指派的订单'); } $assistantId = (int) $params['assistant_id']; $result = OrderLogic::batchAssignAssistant($allowed, $assistantId); if ($result === false) { return $this->fail(OrderLogic::getError()); } foreach ($result['per_log'] as $row) { $this->logOrderAction((int) $row['order_id'], 'assign_assistant', (string) ($row['summary'] ?? '')); } $msg = '已变更「创建人」' . (int) $result['success'] . ' 单'; if (!empty($result['errors'])) { $msg .= ',说明:' . implode(';', $result['errors']); } return $this->success($msg, $result); } /** * @notes 拆分订单:生成多笔子单(待支付/待审核/已支付继承支付信息),金额合计须等于原单;原单软删除;业务订单关联自动迁移 * @return \think\response\Json */ public function split() { $params = (new OrderValidate())->post()->goCheck('split'); $orderId = (int) $params['id']; $amounts = $params['amounts'] ?? []; if (! is_array($amounts)) { return $this->fail('子单金额格式错误'); } $orderTypes = $params['order_types'] ?? []; if (! is_array($orderTypes)) { return $this->fail('子单订单类型格式错误'); } $order = \app\common\model\Order::find($orderId); if (! $order) { return $this->fail('订单不存在'); } if (! $this->canEditOrder($order)) { return $this->fail('无权限拆分此订单'); } $result = OrderLogic::split($orderId, $amounts, $orderTypes); if ($result === false) { return $this->fail(OrderLogic::getError()); } $summary = '拆分为子单 id=' . implode(',', $result['new_order_ids'] ?? []); $this->logOrderAction($orderId, 'split', $summary); foreach ($result['new_order_ids'] ?? [] as $nid) { $this->logOrderAction((int) $nid, 'split_child', '来源原单 id=' . $orderId . ' ' . (string) ($order->order_no ?? '')); } return $this->success('拆分成功', $result); } /** * @notes 编辑订单(关联患者、订单类型) * 权限:超管或指定角色组可修改任意订单;其他用户只能修改自己创建的订单 * @return \think\response\Json */ public function edit() { $params = (new OrderValidate())->post()->goCheck('edit'); $orderId = (int)$params['id']; $order = \app\common\model\Order::find($orderId); if (!$order) { return $this->fail('订单不存在'); } if (!$this->canEditOrder($order)) { return $this->fail('无权限修改此订单'); } $result = OrderLogic::edit($orderId, $params); if (!$result) { return $this->fail(OrderLogic::getError()); } $this->logOrderAction($orderId, 'edit', '编辑患者/订单类型等'); return $this->success('编辑成功'); } /** * @notes 是否可编辑指定订单(超管或指定角色可编辑任意,否则只能编辑自己创建的) */ private function canEditOrder($order): bool { if (!empty($this->adminInfo['root']) && $this->adminInfo['root'] == 1) { return true; } $editAllRoles = config('project.order_edit_all_roles', [0, 3]); $roleIds = \app\common\model\auth\AdminRole::where('admin_id', $this->adminId)->column('role_id'); if (count(array_intersect($roleIds, $editAllRoles)) > 0) { return true; } return (int)$order->creator_id === $this->adminId; } /** * @notes 支付订单 * @return \think\response\Json */ public function pay() { $params = (new OrderValidate())->post()->goCheck('pay'); // 判断是否是补单支付 if (!empty($params['is_supplement']) && $params['is_supplement'] == 1) { // 补单支付:通过订单号查找订单 if (empty($params['order_no'])) { return $this->fail('补单支付需要提供订单号'); } $order = \app\common\model\Order::where('order_no', $params['order_no'])->find(); if (!$order) { return $this->fail('订单不存在'); } $orderId = $order->id; } else { // 正常支付:通过订单ID if (empty($params['id'])) { return $this->fail('订单ID不能为空'); } $orderId = (int)$params['id']; } $result = OrderLogic::pay($orderId, $params['payment_method']); if (!$result) { return $this->fail(OrderLogic::getError()); } $this->logOrderAction($orderId, 'pay', '确认支付/标记已付,方式:' . (string)($params['payment_method'] ?? '')); return $this->success('支付成功'); } /** * @notes 取消订单 * @return \think\response\Json */ public function cancel() { $params = (new OrderValidate())->post()->goCheck('cancel'); $oid = (int)$params['id']; $result = OrderLogic::cancel($oid); if (!$result) { return $this->fail(OrderLogic::getError()); } $this->logOrderAction($oid, 'cancel', '取消订单'); return $this->success('取消成功'); } /** * @notes 退款订单 * @return \think\response\Json */ public function refund() { $params = (new OrderValidate())->post()->goCheck('refund'); $oid = (int)$params['id']; $result = OrderLogic::refund($oid); if (!$result) { return $this->fail(OrderLogic::getError()); } $this->logOrderAction($oid, 'refund', '退款'); return $this->success('退款成功'); } /** * @notes 删除订单 * @return \think\response\Json */ public function delete() { $params = (new OrderValidate())->post()->goCheck('delete'); $oid = (int)$params['id']; $result = OrderLogic::delete($oid); if (!$result) { return $this->fail(OrderLogic::getError()); } $this->logOrderAction($oid, 'delete', '删除订单(软删)'); return $this->success('删除成功'); } /** * @notes 导出订单 * @return \think\response\Json */ public function export() { return $this->dataLists(new OrderLists()); } /** * @notes 支付宝支付 * @return \think\response\Json */ public function alipay() { $params = (new OrderValidate())->post()->goCheck('pay'); // 补单支付不需要校验订单是否存在 if (!empty($params['is_supplement']) && $params['is_supplement'] == 1) { // 补单支付:直接返回支付URL,不校验订单 $payUrl = OrderLogic::alipayPay(['order_no' => $params['order_no']]); if (!$payUrl) { return $this->fail(OrderLogic::getError()); } return $this->data(['pay_url' => $payUrl]); } // 正常支付需要校验订单 $order = $this->getOrderByParams($params); if (!$order) { return $this->fail('订单不存在'); } // 调用支付宝支付接口 $payUrl = OrderLogic::alipayPay($order); if (!$payUrl) { return $this->fail(OrderLogic::getError()); } return $this->data(['pay_url' => $payUrl]); } /** * @notes 微信支付 * @return \think\response\Json */ public function wechat() { $params = (new OrderValidate())->post()->goCheck('pay'); // 补单支付不需要校验订单是否存在 if (!empty($params['is_supplement']) && $params['is_supplement'] == 1) { // 补单支付:直接返回支付数据,不校验订单 $payData = OrderLogic::wechatPay(['order_no' => $params['order_no']]); if (!$payData) { return $this->fail(OrderLogic::getError()); } return $this->data($payData); } // 正常支付需要校验订单 $order = $this->getOrderByParams($params); if (!$order) { return $this->fail('订单不存在'); } // 调用微信支付接口 $payData = OrderLogic::wechatPay($order); if (!$payData) { return $this->fail(OrderLogic::getError()); } return $this->data($payData); } /** * @notes 根据参数获取订单 * @param array $params * @return mixed */ private function getOrderByParams(array $params) { if (!empty($params['is_supplement']) && $params['is_supplement'] == 1) { // 补单支付:通过订单号查找 if (empty($params['order_no'])) { return null; } return \app\common\model\Order::where('order_no', $params['order_no'])->find(); } else { // 正常支付:通过订单ID if (empty($params['id'])) { return null; } return \app\common\model\Order::find((int)$params['id']); } } public function setExempt() { $id = (int) $this->request->post('id', 0); $isExempt = (int) $this->request->post('is_exempt', 0); if ($id <= 0) { return $this->fail('参数错误'); } if (!in_array($isExempt, [0, 1], true)) { return $this->fail('is_exempt 值无效'); } $result = OrderLogic::setExempt($id, $isExempt); if (!$result) { return $this->fail(OrderLogic::getError()); } $this->logOrderAction($id, 'set_exempt', $isExempt === 1 ? '设置豁免权' : '取消豁免权'); return $this->success($isExempt === 1 ? '已设置豁免权' : '已取消豁免权'); } private function logOrderAction(int $orderId, string $action, string $summary = ''): void { OrderActionLogLogic::record($orderId, (int) $this->adminId, $this->adminInfo, $action, $summary); } }