526 lines
18 KiB
PHP
526 lines
18 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace app\adminapi\controller\order;
|
||
|
||
use app\adminapi\controller\BaseAdminController;
|
||
use app\adminapi\lists\order\OrderLists;
|
||
use app\adminapi\logic\order\OrderActionLogLogic;
|
||
use app\adminapi\logic\order\OrderLogic;
|
||
use app\adminapi\validate\order\OrderValidate;
|
||
|
||
/**
|
||
* 订单管理控制器
|
||
* Class OrderController
|
||
* @package app\adminapi\controller\order
|
||
*/
|
||
class OrderController extends BaseAdminController
|
||
{
|
||
/**
|
||
* @notes 订单列表
|
||
* @return \think\response\Json
|
||
*/
|
||
public function lists()
|
||
{
|
||
return $this->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);
|
||
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);
|
||
|
||
$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;
|
||
|
||
$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 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']);
|
||
}
|
||
}
|
||
|
||
private function logOrderAction(int $orderId, string $action, string $summary = ''): void
|
||
{
|
||
OrderActionLogLogic::record($orderId, (int) $this->adminId, $this->adminInfo, $action, $summary);
|
||
}
|
||
}
|