更新
This commit is contained in:
@@ -6,6 +6,7 @@ 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;
|
||||
|
||||
@@ -140,15 +141,62 @@ class OrderController extends BaseAdminController
|
||||
public function detail()
|
||||
{
|
||||
$params = (new OrderValidate())->post()->goCheck('detail');
|
||||
$detail = OrderLogic::detail((int)$params['id']);
|
||||
$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
|
||||
@@ -157,11 +205,14 @@ class OrderController extends BaseAdminController
|
||||
{
|
||||
$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());
|
||||
}
|
||||
@@ -181,6 +232,10 @@ class OrderController extends BaseAdminController
|
||||
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'],
|
||||
@@ -189,6 +244,51 @@ class OrderController extends BaseAdminController
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 编辑订单(关联患者、订单类型)
|
||||
* 权限:超管或指定角色组可修改任意订单;其他用户只能修改自己创建的订单
|
||||
@@ -210,6 +310,7 @@ class OrderController extends BaseAdminController
|
||||
if (!$result) {
|
||||
return $this->fail(OrderLogic::getError());
|
||||
}
|
||||
$this->logOrderAction($orderId, 'edit', '编辑患者/订单类型等');
|
||||
|
||||
return $this->success('编辑成功');
|
||||
}
|
||||
@@ -261,6 +362,7 @@ class OrderController extends BaseAdminController
|
||||
if (!$result) {
|
||||
return $this->fail(OrderLogic::getError());
|
||||
}
|
||||
$this->logOrderAction($orderId, 'pay', '确认支付/标记已付,方式:' . (string)($params['payment_method'] ?? ''));
|
||||
|
||||
return $this->success('支付成功');
|
||||
}
|
||||
@@ -273,10 +375,12 @@ class OrderController extends BaseAdminController
|
||||
{
|
||||
$params = (new OrderValidate())->post()->goCheck('cancel');
|
||||
|
||||
$result = OrderLogic::cancel((int)$params['id']);
|
||||
$oid = (int)$params['id'];
|
||||
$result = OrderLogic::cancel($oid);
|
||||
if (!$result) {
|
||||
return $this->fail(OrderLogic::getError());
|
||||
}
|
||||
$this->logOrderAction($oid, 'cancel', '取消订单');
|
||||
|
||||
return $this->success('取消成功');
|
||||
}
|
||||
@@ -289,10 +393,12 @@ class OrderController extends BaseAdminController
|
||||
{
|
||||
$params = (new OrderValidate())->post()->goCheck('refund');
|
||||
|
||||
$result = OrderLogic::refund((int)$params['id']);
|
||||
$oid = (int)$params['id'];
|
||||
$result = OrderLogic::refund($oid);
|
||||
if (!$result) {
|
||||
return $this->fail(OrderLogic::getError());
|
||||
}
|
||||
$this->logOrderAction($oid, 'refund', '退款');
|
||||
|
||||
return $this->success('退款成功');
|
||||
}
|
||||
@@ -305,10 +411,12 @@ class OrderController extends BaseAdminController
|
||||
{
|
||||
$params = (new OrderValidate())->post()->goCheck('delete');
|
||||
|
||||
$result = OrderLogic::delete((int)$params['id']);
|
||||
$oid = (int)$params['id'];
|
||||
$result = OrderLogic::delete($oid);
|
||||
if (!$result) {
|
||||
return $this->fail(OrderLogic::getError());
|
||||
}
|
||||
$this->logOrderAction($oid, 'delete', '删除订单(软删)');
|
||||
|
||||
return $this->success('删除成功');
|
||||
}
|
||||
@@ -409,4 +517,9 @@ class OrderController extends BaseAdminController
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@ namespace app\adminapi\controller\tcm;
|
||||
|
||||
use app\adminapi\controller\BaseAdminController;
|
||||
use app\adminapi\lists\tcm\DiagnosisLists;
|
||||
use app\adminapi\logic\order\OrderActionLogLogic;
|
||||
use app\adminapi\logic\tcm\DiagnosisLogic;
|
||||
use app\adminapi\validate\tcm\DiagnosisValidate;
|
||||
use app\common\model\Order;
|
||||
use app\common\model\WechatChatRecord;
|
||||
|
||||
/**
|
||||
@@ -482,6 +484,19 @@ class DiagnosisController extends BaseAdminController
|
||||
if ($result === false) {
|
||||
return $this->fail(DiagnosisLogic::getError());
|
||||
}
|
||||
$orderNo = (string)($params['order_no'] ?? '');
|
||||
if ($orderNo !== '') {
|
||||
$po = Order::where('order_no', $orderNo)->find();
|
||||
if ($po) {
|
||||
OrderActionLogLogic::record(
|
||||
(int)$po->id,
|
||||
(int)$this->adminId,
|
||||
$this->adminInfo,
|
||||
'wx_qrcode',
|
||||
'生成订单小程序码'
|
||||
);
|
||||
}
|
||||
}
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
|
||||
@@ -257,12 +257,17 @@ class PrescriptionOrderController extends BaseAdminController
|
||||
public function complete()
|
||||
{
|
||||
$params = (new PrescriptionOrderValidate())->post()->goCheck('complete');
|
||||
$result = PrescriptionOrderLogic::complete((int) $params['id'], $this->adminId, $this->adminInfo);
|
||||
$result = PrescriptionOrderLogic::complete(
|
||||
(int) $params['id'],
|
||||
$this->adminId,
|
||||
$this->adminInfo,
|
||||
(int) $params['fulfillment_status']
|
||||
);
|
||||
if ($result === false) {
|
||||
return $this->fail(PrescriptionOrderLogic::getError());
|
||||
}
|
||||
|
||||
return $this->success('订单已完成', $result);
|
||||
return $this->success('操作成功', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,122 @@ use app\common\model\auth\AdminRole;
|
||||
*/
|
||||
class OrderLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
{
|
||||
/**
|
||||
* 将列表「创建时间」参数规范为可解析的日期时间(datetimerange 已是 Y-m-d H:i:s,不能再去拼接 00:00:00,否则会变成非法字符串)
|
||||
*/
|
||||
private function normalizeListDateTimeString(string $raw, bool $isEnd): string
|
||||
{
|
||||
$raw = trim($raw);
|
||||
if ($raw === '') {
|
||||
return '';
|
||||
}
|
||||
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $raw) === 1) {
|
||||
return $isEnd ? ($raw . ' 23:59:59') : ($raw . ' 00:00:00');
|
||||
}
|
||||
if (strlen($raw) === 19 && preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $raw) === 1) {
|
||||
return $raw;
|
||||
}
|
||||
$ts = strtotime($raw);
|
||||
if ($ts !== false) {
|
||||
return date('Y-m-d H:i:s', $ts);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 患者关键词:在诊单表按姓名、手机号或诊单ID匹配(子查询里显式 whereOr,避免 | 在闭包/子查下兼容问题)
|
||||
*
|
||||
* @return \Closure|array{} 无匹配关键词时返回空数组表示不加条件
|
||||
*/
|
||||
private function patientKeywordSubWhere()
|
||||
{
|
||||
$kw = trim((string) ($this->params['patient_keyword'] ?? ''));
|
||||
if ($kw === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
return function ($query) use ($kw) {
|
||||
$query->table('zyt_tcm_diagnosis')
|
||||
->whereNull('delete_time')
|
||||
->where(function ($q) use ($kw) {
|
||||
$q->where('patient_name', 'like', '%' . $kw . '%')
|
||||
->whereOr('phone', 'like', '%' . $kw . '%');
|
||||
if (preg_match('/^\d+$/', $kw) && (int) $kw > 0) {
|
||||
$q->whereOr('id', '=', (int) $kw);
|
||||
}
|
||||
})
|
||||
->field('id');
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建时间:支持 int 时间戳 与 字符串 datetime 两种存法(用 OR 包裹,至少命中一类)
|
||||
*/
|
||||
private function appendCreateTimeWhere(array &$where): void
|
||||
{
|
||||
$s = !empty($this->params['create_time_start'])
|
||||
? $this->normalizeListDateTimeString((string) $this->params['create_time_start'], false)
|
||||
: '';
|
||||
$e = !empty($this->params['create_time_end'])
|
||||
? $this->normalizeListDateTimeString((string) $this->params['create_time_end'], true)
|
||||
: '';
|
||||
if ($s === '' && $e === '') {
|
||||
return;
|
||||
}
|
||||
$i0 = $s !== '' ? (int) strtotime($s) : 0;
|
||||
$i1 = $e !== '' ? (int) strtotime($e) : 0;
|
||||
if ($i0 > 0 && $i1 > 0 && $i1 < $i0) {
|
||||
$t = $i0;
|
||||
$i0 = $i1;
|
||||
$i1 = $t;
|
||||
$ts = $s;
|
||||
$s = $e;
|
||||
$e = $ts;
|
||||
}
|
||||
$intOk = $i0 > 0 || $i1 > 0;
|
||||
$strOk = $s !== '' || $e !== '';
|
||||
if (!$intOk && !$strOk) {
|
||||
return;
|
||||
}
|
||||
|
||||
$where[] = function ($query) use ($s, $e, $i0, $i1, $intOk, $strOk) {
|
||||
$query->where(function ($q) use ($s, $e, $i0, $i1, $intOk, $strOk) {
|
||||
if ($intOk) {
|
||||
$q->where(function ($qi) use ($i0, $i1) {
|
||||
if ($i0 > 0) {
|
||||
$qi->where('create_time', '>=', $i0);
|
||||
}
|
||||
if ($i1 > 0) {
|
||||
$qi->where('create_time', '<=', $i1);
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($strOk) {
|
||||
if ($intOk) {
|
||||
$q->whereOr(function ($qs) use ($s, $e) {
|
||||
if ($s !== '') {
|
||||
$qs->where('create_time', '>=', $s);
|
||||
}
|
||||
if ($e !== '') {
|
||||
$qs->where('create_time', '<=', $e);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$q->where(function ($qs) use ($s, $e) {
|
||||
if ($s !== '') {
|
||||
$qs->where('create_time', '>=', $s);
|
||||
}
|
||||
if ($e !== '') {
|
||||
$qs->where('create_time', '<=', $e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 设置搜索条件
|
||||
* @return array
|
||||
@@ -24,15 +140,23 @@ class OrderLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
{
|
||||
return [
|
||||
'=' => ['order_type', 'status'],
|
||||
'like' => ['order_no'],
|
||||
// ListsSearchTrait 只识别 %like% / like% 等,「like」不会命中任何 case,订单号因此搜不到
|
||||
'%like%' => ['order_no'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 权限过滤 - 普通员工只能看自己创建的订单
|
||||
* @param array $where
|
||||
* @return void
|
||||
* 按医助筛选:与「创建人」一致(指派后创建人为该医助)
|
||||
*/
|
||||
private function appendAssistantFilter(array &$where): void
|
||||
{
|
||||
$assistantId = (int) ($this->params['assistant_id'] ?? 0);
|
||||
if ($assistantId <= 0) {
|
||||
return;
|
||||
}
|
||||
$where[] = ['creator_id', '=', $assistantId];
|
||||
}
|
||||
|
||||
private function filterByPermission(array &$where): void
|
||||
{
|
||||
// 检查是否是超管(root字段为1)
|
||||
@@ -78,35 +202,15 @@ class OrderLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
}
|
||||
|
||||
// 处理患者关键词搜索(从诊单表搜索)
|
||||
if (!empty($this->params['patient_keyword'])) {
|
||||
$where[] = ['patient_id', 'in', function ($query) {
|
||||
$query->table('zyt_tcm_diagnosis')
|
||||
->whereNull('delete_time')
|
||||
->where('patient_name|phone', 'like', '%' . $this->params['patient_keyword'] . '%')
|
||||
->field('id');
|
||||
}];
|
||||
$pkw = $this->patientKeywordSubWhere();
|
||||
if ($pkw !== []) {
|
||||
$where[] = ['patient_id', 'in', $pkw];
|
||||
}
|
||||
|
||||
// 按诊单医助筛选(订单 patient_id 存诊单 id)
|
||||
$assistantId = (int) ($this->params['assistant_id'] ?? 0);
|
||||
if ($assistantId > 0) {
|
||||
$where[] = ['patient_id', 'in', function ($query) use ($assistantId) {
|
||||
$query->table('zyt_tcm_diagnosis')
|
||||
->whereNull('delete_time')
|
||||
->where('assistant_id', '=', $assistantId)
|
||||
->field('id');
|
||||
}];
|
||||
}
|
||||
$this->appendAssistantFilter($where);
|
||||
|
||||
$this->appendCreateTimeWhere($where);
|
||||
|
||||
// 处理创建时间范围
|
||||
if (!empty($this->params['create_time_start'])) {
|
||||
$where[] = ['create_time', '>=', $this->params['create_time_start'] . ' 00:00:00'];
|
||||
}
|
||||
|
||||
if (!empty($this->params['create_time_end'])) {
|
||||
$where[] = ['create_time', '<=', $this->params['create_time_end'] . ' 23:59:59'];
|
||||
}
|
||||
|
||||
return Order::where($where)
|
||||
->with(['patient', 'creator'])
|
||||
->order(['create_time' => 'desc'])
|
||||
@@ -135,34 +239,15 @@ class OrderLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
}
|
||||
|
||||
// 处理患者关键词搜索(从诊单表搜索)
|
||||
if (!empty($this->params['patient_keyword'])) {
|
||||
$where[] = ['patient_id', 'in', function ($query) {
|
||||
$query->table('zyt_tcm_diagnosis')
|
||||
->whereNull('delete_time')
|
||||
->where('patient_name|phone', 'like', '%' . $this->params['patient_keyword'] . '%')
|
||||
->field('id');
|
||||
}];
|
||||
$pkw = $this->patientKeywordSubWhere();
|
||||
if ($pkw !== []) {
|
||||
$where[] = ['patient_id', 'in', $pkw];
|
||||
}
|
||||
|
||||
$assistantIdCount = (int) ($this->params['assistant_id'] ?? 0);
|
||||
if ($assistantIdCount > 0) {
|
||||
$where[] = ['patient_id', 'in', function ($query) use ($assistantIdCount) {
|
||||
$query->table('zyt_tcm_diagnosis')
|
||||
->whereNull('delete_time')
|
||||
->where('assistant_id', '=', $assistantIdCount)
|
||||
->field('id');
|
||||
}];
|
||||
}
|
||||
$this->appendAssistantFilter($where);
|
||||
|
||||
$this->appendCreateTimeWhere($where);
|
||||
|
||||
// 处理创建时间范围
|
||||
if (!empty($this->params['create_time_start'])) {
|
||||
$where[] = ['create_time', '>=', $this->params['create_time_start'] . ' 00:00:00'];
|
||||
}
|
||||
|
||||
if (!empty($this->params['create_time_end'])) {
|
||||
$where[] = ['create_time', '<=', $this->params['create_time_end'] . ' 23:59:59'];
|
||||
}
|
||||
|
||||
return Order::where($where)->count();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\logic\order;
|
||||
|
||||
use app\common\model\auth\Admin;
|
||||
use app\common\model\OrderActionLog;
|
||||
|
||||
/**
|
||||
* 支付单操作日志(写主库、失败忽略)
|
||||
*/
|
||||
class OrderActionLogLogic
|
||||
{
|
||||
/** @var array<string,string> 动作码 => 中文说明 */
|
||||
public const ACTION_LABELS = [
|
||||
'view_detail' => '查看详情',
|
||||
'edit' => '编辑订单',
|
||||
'wx_qrcode' => '小程序码',
|
||||
'pay' => '确认支付',
|
||||
'refund' => '退款',
|
||||
'cancel' => '取消订单',
|
||||
'delete' => '删除订单',
|
||||
'create' => '创建订单',
|
||||
'create_wechat_work' => '创建订单(企微对外收款)',
|
||||
'assign_assistant' => '变更创建人(指派医助)',
|
||||
];
|
||||
|
||||
public static function record(
|
||||
int $orderId,
|
||||
int $adminId,
|
||||
array $adminInfo,
|
||||
string $action,
|
||||
string $summary = ''
|
||||
): void {
|
||||
if ($orderId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$adminName = (string)($adminInfo['name'] ?? '');
|
||||
if ($adminName === '' && $adminId > 0) {
|
||||
$adminName = (string) Admin::where('id', $adminId)->value('name');
|
||||
}
|
||||
|
||||
$log = new OrderActionLog();
|
||||
$log->order_id = $orderId;
|
||||
$log->admin_id = $adminId;
|
||||
$log->admin_name = mb_substr($adminName, 0, 64);
|
||||
$log->action = mb_substr($action, 0, 32);
|
||||
$log->summary = mb_substr($summary, 0, 500);
|
||||
$log->create_time = time();
|
||||
|
||||
try {
|
||||
$log->save();
|
||||
} catch (\Throwable $e) {
|
||||
// 忽略日志表未创建等错误,不影响主业务
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单条支付单操作记录列表(新在前)
|
||||
*
|
||||
* @return array{lists: array<int, array<string,mixed>>, count: int}
|
||||
*/
|
||||
public static function listByOrderId(int $orderId, int $pageNo, int $pageSize): array
|
||||
{
|
||||
if ($orderId <= 0) {
|
||||
return ['lists' => [], 'count' => 0];
|
||||
}
|
||||
|
||||
$pageSize = max(1, min(100, $pageSize));
|
||||
$pageNo = max(1, $pageNo);
|
||||
$offset = ($pageNo - 1) * $pageSize;
|
||||
|
||||
$q = OrderActionLog::where('order_id', $orderId);
|
||||
$count = (int) $q->count();
|
||||
$rows = OrderActionLog::where('order_id', $orderId)
|
||||
->order('id', 'desc')
|
||||
->limit($offset, $pageSize)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
foreach ($rows as &$r) {
|
||||
$code = (string)($r['action'] ?? '');
|
||||
$r['action_label'] = self::ACTION_LABELS[$code] ?? $code;
|
||||
$r['create_time_text'] = !empty($r['create_time'])
|
||||
? date('Y-m-d H:i:s', (int) $r['create_time'])
|
||||
: '';
|
||||
}
|
||||
unset($r);
|
||||
|
||||
return ['lists' => $rows, 'count' => $count];
|
||||
}
|
||||
|
||||
/**
|
||||
* 按时间范围统计每人操作次数(仅统计有日志表的数据)
|
||||
*
|
||||
* @return array<int, array{admin_id: int, admin_name: string, cnt: int}>
|
||||
*/
|
||||
public static function statsByAdmin(int $startTime, int $endTime, int $limit = 50): array
|
||||
{
|
||||
if ($endTime < $startTime) {
|
||||
return [];
|
||||
}
|
||||
$limit = max(1, min(200, $limit));
|
||||
|
||||
try {
|
||||
$rows = OrderActionLog::field('admin_id, admin_name, COUNT(*) AS cnt')
|
||||
->whereBetween('create_time', [$startTime, $endTime])
|
||||
->group('admin_id, admin_name')
|
||||
->order('cnt', 'desc')
|
||||
->limit($limit)
|
||||
->select()
|
||||
->toArray();
|
||||
} catch (\Throwable $e) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$out = [];
|
||||
foreach ($rows as $r) {
|
||||
$out[] = [
|
||||
'admin_id' => (int)($r['admin_id'] ?? 0),
|
||||
'admin_name' => (string)($r['admin_name'] ?? ''),
|
||||
'cnt' => (int)($r['cnt'] ?? 0),
|
||||
];
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ namespace app\adminapi\logic\order;
|
||||
use app\common\model\Order;
|
||||
use app\common\model\OrderDetail;
|
||||
use app\common\model\auth\Admin;
|
||||
use app\common\model\auth\AdminRole;
|
||||
use app\common\model\tcm\Diagnosis;
|
||||
use app\common\model\tcm\PrescriptionOrder;
|
||||
use app\common\model\tcm\PrescriptionOrderPayOrder;
|
||||
@@ -38,13 +39,25 @@ class OrderLogic
|
||||
public static function create(array $params)
|
||||
{
|
||||
try {
|
||||
$channel = (string)($params['payment_channel'] ?? 'normal');
|
||||
if (!in_array($channel, ['normal', 'fubei'], true)) {
|
||||
$channel = 'normal';
|
||||
}
|
||||
$requirePaymentSlipAudit = (int)($params['require_payment_slip_audit'] ?? 0) === 1;
|
||||
|
||||
$order = new Order();
|
||||
$order->order_no = self::generateOrderNo();
|
||||
$order->patient_id = $params['patient_id'];
|
||||
$order->creator_id = $params['creator_id'];
|
||||
$order->order_type = $params['order_type'];
|
||||
$order->amount = $params['amount'];
|
||||
$order->status = 1; // 待支付
|
||||
// 付呗且申请审核支付单:待审核(5);否则待支付(1)
|
||||
if ($channel === 'fubei') {
|
||||
$order->payment_method = 'fubei';
|
||||
$order->status = $requirePaymentSlipAudit ? 5 : 1;
|
||||
} else {
|
||||
$order->status = 1; // 待支付
|
||||
}
|
||||
$order->remark = $params['remark'] ?? '';
|
||||
$order->save();
|
||||
|
||||
@@ -331,7 +344,13 @@ class OrderLogic
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($order->status != 1) {
|
||||
$st = (int)$order->status;
|
||||
// 1=待支付;5=待审核(付呗+申请支付单审核) 时允许录入手动到账
|
||||
if ($st === 1) {
|
||||
// 正常待支付
|
||||
} elseif ($st === 5 && (string)($order->payment_method ?? '') === 'fubei') {
|
||||
// 待审核的付呗单,通过人工确认后标记已支付
|
||||
} else {
|
||||
self::setError('订单状态不允许支付');
|
||||
return false;
|
||||
}
|
||||
@@ -362,8 +381,9 @@ class OrderLogic
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($order->status != 1) {
|
||||
self::setError('只有待支付的订单才能取消');
|
||||
$st = (int)$order->status;
|
||||
if ($st !== 1 && !($st === 5 && (string)($order->payment_method ?? '') === 'fubei')) {
|
||||
self::setError('只有待支付或待审核(付呗)的订单才能取消');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1097,4 +1117,84 @@ class OrderLogic
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量指派给医助:将「创建人」变更为目标医助(须为医助 role_id=2),用于列表筛选项为创建人=该医助
|
||||
*
|
||||
* @param int[] $orderIds
|
||||
* @return array{success: int, per_log: list<array{order_id: int, summary: string}>, fail: int, errors: list<string>}|false
|
||||
*/
|
||||
public static function batchAssignAssistant(array $orderIds, int $assistantAdminId)
|
||||
{
|
||||
$assistantAdminId = (int) $assistantAdminId;
|
||||
if ($assistantAdminId <= 0) {
|
||||
self::setError('请选择医助');
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
AdminRole::where('admin_id', $assistantAdminId)
|
||||
->where('role_id', 2)
|
||||
->count() === 0
|
||||
) {
|
||||
self::setError('目标账号不是医助角色');
|
||||
return false;
|
||||
}
|
||||
$ids = array_values(array_unique(array_filter(array_map('intval', $orderIds), static fn (int $id) => $id > 0)));
|
||||
if ($ids === []) {
|
||||
self::setError('请选择订单');
|
||||
return false;
|
||||
}
|
||||
if (count($ids) > 200) {
|
||||
self::setError('单次最多200单');
|
||||
return false;
|
||||
}
|
||||
|
||||
$targetName = (string) Admin::where('id', $assistantAdminId)->whereNull('delete_time')->value('name');
|
||||
if ($targetName === '') {
|
||||
$targetName = (string) $assistantAdminId;
|
||||
}
|
||||
$perLog = [];
|
||||
$errors = [];
|
||||
foreach ($ids as $oid) {
|
||||
$order = Order::where('id', $oid)->whereNull('delete_time')->find();
|
||||
if (! $order) {
|
||||
$errors[] = "订单{$oid}不存在或已删除";
|
||||
continue;
|
||||
}
|
||||
$oldCreatorId = (int) $order->creator_id;
|
||||
if ($oldCreatorId === $assistantAdminId) {
|
||||
$errors[] = "订单{$oid}创建人已是该医助,已跳过";
|
||||
continue;
|
||||
}
|
||||
$oldName = $oldCreatorId > 0
|
||||
? (string) Admin::where('id', $oldCreatorId)->whereNull('delete_time')->value('name')
|
||||
: '';
|
||||
if ($oldName === '' && $oldCreatorId > 0) {
|
||||
$oldName = (string) $oldCreatorId;
|
||||
} elseif ($oldName === '') {
|
||||
$oldName = '—';
|
||||
}
|
||||
$order->creator_id = $assistantAdminId;
|
||||
if (! $order->save()) {
|
||||
$errors[] = "订单{$oid}保存失败";
|
||||
continue;
|
||||
}
|
||||
$perLog[] = [
|
||||
'order_id' => (int) $order->id,
|
||||
'summary' => '创建人由「' . $oldName . '」变更为医助「' . $targetName . '」',
|
||||
];
|
||||
}
|
||||
|
||||
if ($perLog === []) {
|
||||
self::setError($errors !== [] ? implode(';', $errors) : '未能变更任何订单');
|
||||
return false;
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => count($perLog),
|
||||
'per_log' => $perLog,
|
||||
'fail' => count($errors),
|
||||
'errors' => $errors,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1513,14 +1513,25 @@ class PrescriptionOrderLogic
|
||||
return $out;
|
||||
}
|
||||
|
||||
/** 完成订单时可选的履约终态:3=已完成,7-12=业务结案类状态 */
|
||||
private const COMPLETE_FULFILLMENT_TARGETS = [3, 7, 8, 9, 10, 11, 12];
|
||||
|
||||
/** 完成时是否按「实付=关联支付单金额」重算并清空诊单医助(与 clearDiagnosisAssistantOnComplete 的终态判断一致) */
|
||||
private const COMPLETE_FULFILLMENT_TERMINAL = [3, 8, 9, 10, 11, 12];
|
||||
|
||||
/**
|
||||
* 将「已发货」(fulfillment_status=5) 且支付审核已通过的订单标记为「已完成」(3)。
|
||||
* 将「已发货/已签收」且支付审核已通过的订单结案:可选「已完成」或 7-12 业务状态。
|
||||
*
|
||||
* @return array<string,mixed>|false
|
||||
*/
|
||||
public static function complete(int $id, int $adminId, array $adminInfo)
|
||||
public static function complete(int $id, int $adminId, array $adminInfo, int $targetFulfillmentStatus = 3)
|
||||
{
|
||||
self::$error = '';
|
||||
if (!in_array($targetFulfillmentStatus, self::COMPLETE_FULFILLMENT_TARGETS, true)) {
|
||||
self::$error = '无效的完成状态';
|
||||
return false;
|
||||
}
|
||||
|
||||
$order = PrescriptionOrder::where('id', $id)->whereNull('delete_time')->find();
|
||||
if (!$order) {
|
||||
self::$error = '订单不存在';
|
||||
@@ -1530,8 +1541,9 @@ class PrescriptionOrderLogic
|
||||
self::$error = '无权限操作';
|
||||
return false;
|
||||
}
|
||||
if ((int) $order->fulfillment_status !== 5) {
|
||||
self::$error = '仅「已发货」状态可标记为已完成';
|
||||
$curFs = (int) $order->fulfillment_status;
|
||||
if ($curFs !== 5 && $curFs !== 6) {
|
||||
self::$error = '仅「已发货」或「已签收」状态可完成订单';
|
||||
return false;
|
||||
}
|
||||
if ((int) $order->payment_slip_audit_status !== 1) {
|
||||
@@ -1539,17 +1551,22 @@ class PrescriptionOrderLogic
|
||||
return false;
|
||||
}
|
||||
|
||||
// 计算关联订单总额
|
||||
$linkedPayOrderIds = self::linkedPayOrderIdList($id);
|
||||
$linkedPayOrders = self::linkedPayOrdersPayload($linkedPayOrderIds);
|
||||
$label = self::fulfillmentStatusLabel($targetFulfillmentStatus);
|
||||
|
||||
$linkedPayOrderIds = self::linkedPayOrderIdList($id);
|
||||
$linkedPayOrders = self::linkedPayOrdersPayload($linkedPayOrderIds);
|
||||
$linkedPayPaidTotal = 0.0;
|
||||
foreach ($linkedPayOrders as $o) {
|
||||
$linkedPayPaidTotal += (float) ($o['amount'] ?? 0);
|
||||
}
|
||||
$linkedPayPaidTotal = round($linkedPayPaidTotal, 2);
|
||||
|
||||
$order->fulfillment_status = 3;
|
||||
$order->paid = $linkedPayPaidTotal;
|
||||
if ($targetFulfillmentStatus === 3) {
|
||||
$order->fulfillment_status = 3;
|
||||
$order->paid = $linkedPayPaidTotal;
|
||||
} else {
|
||||
$order->fulfillment_status = $targetFulfillmentStatus;
|
||||
}
|
||||
|
||||
try {
|
||||
$order->save();
|
||||
@@ -1558,14 +1575,22 @@ class PrescriptionOrderLogic
|
||||
return false;
|
||||
}
|
||||
|
||||
$unassignSummary = self::clearDiagnosisAssistantOnComplete((int) $order->diagnosis_id);
|
||||
$unassignSummary = '';
|
||||
if (in_array($targetFulfillmentStatus, self::COMPLETE_FULFILLMENT_TERMINAL, true)) {
|
||||
$unassignSummary = self::clearDiagnosisAssistantOnComplete((int) $order->diagnosis_id);
|
||||
}
|
||||
|
||||
if ($targetFulfillmentStatus === 3) {
|
||||
$logLine = '订单完成,状态变更为「' . $label . '」,实付金额更新为 ¥' . $linkedPayPaidTotal . $unassignSummary;
|
||||
} else {
|
||||
$logLine = '订单处理完成,状态变更为「' . $label . '」' . $unassignSummary;
|
||||
}
|
||||
self::writeLog(
|
||||
$id,
|
||||
$adminId,
|
||||
$adminInfo,
|
||||
'complete',
|
||||
'订单完成,状态变更为「已完成」,实付金额更新为 ¥' . $linkedPayPaidTotal . $unassignSummary
|
||||
$logLine
|
||||
);
|
||||
|
||||
$out = $order->toArray();
|
||||
@@ -1575,6 +1600,26 @@ class PrescriptionOrderLogic
|
||||
return $out;
|
||||
}
|
||||
|
||||
private static function fulfillmentStatusLabel(int $fs): string
|
||||
{
|
||||
$m = [
|
||||
1 => '待双审通过',
|
||||
2 => '待发货',
|
||||
3 => '已完成',
|
||||
4 => '已取消',
|
||||
5 => '已发货',
|
||||
6 => '已签收',
|
||||
7 => '进行中',
|
||||
8 => '暂不制药',
|
||||
9 => '拒收',
|
||||
10 => '退款',
|
||||
11 => '保留药方',
|
||||
12 => '制药缓发',
|
||||
];
|
||||
|
||||
return $m[$fs] ?? ('状态' . (string) $fs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成订单时把关联诊单的 assistant_id 清空,让患者回到「待分配」状态
|
||||
* 仅当该患者没有其它进行中的处方订单时才清空,避免误覆盖
|
||||
@@ -1600,7 +1645,7 @@ class PrescriptionOrderLogic
|
||||
// 同一诊单下若仍有未完成/未取消的处方订单,则不清空
|
||||
$pendingCount = PrescriptionOrder::where('diagnosis_id', $diagnosisId)
|
||||
->whereNull('delete_time')
|
||||
->whereNotIn('fulfillment_status', [3, 4])
|
||||
->whereNotIn('fulfillment_status', [3, 4, 8, 9, 10, 11, 12])
|
||||
->count();
|
||||
if ($pendingCount > 0) {
|
||||
return '';
|
||||
@@ -1635,7 +1680,7 @@ class PrescriptionOrderLogic
|
||||
if ($pa !== 1) {
|
||||
return false;
|
||||
}
|
||||
if ($fs === 3 || $fs === 4) {
|
||||
if (in_array($fs, [3, 4, 8, 9, 10, 11, 12], true)) {
|
||||
return false;
|
||||
}
|
||||
if (trim((string) ($item['gancao_reciperl_order_no'] ?? '')) !== '') {
|
||||
|
||||
@@ -20,10 +20,12 @@ class OrderValidate extends BaseValidate
|
||||
'order_type' => 'require|in:1,2,3,4,5,6,7',
|
||||
'amount' => 'require|float',
|
||||
'status' => 'require|in:1,2,3,4',
|
||||
'payment_method' => 'in:alipay,wechat,wechat_work',
|
||||
'payment_method' => 'in:alipay,wechat,wechat_work,fubei,manual',
|
||||
'remark' => 'string|max:500',
|
||||
'order_no' => 'string|max:50',
|
||||
'is_supplement' => 'in:0,1',
|
||||
'order_ids' => 'require|array',
|
||||
'assistant_id' => 'require|integer|gt:0',
|
||||
];
|
||||
|
||||
protected $message = [
|
||||
@@ -44,5 +46,6 @@ class OrderValidate extends BaseValidate
|
||||
'cancel' => ['id'],
|
||||
'refund' => ['id'],
|
||||
'delete' => ['id'],
|
||||
'assign_assistant' => ['order_ids', 'assistant_id'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ class PrescriptionOrderValidate extends BaseValidate
|
||||
'remark_extra' => 'max:500',
|
||||
'action' => 'require|in:approve,reject',
|
||||
'remark' => 'max:500',
|
||||
'fulfillment_status' => 'require|integer|in:3,7,8,9,10,11,12',
|
||||
];
|
||||
|
||||
protected $message = [
|
||||
@@ -64,7 +65,7 @@ class PrescriptionOrderValidate extends BaseValidate
|
||||
'paidPayOrders' => ['diagnosis_id'],
|
||||
'addPayOrder' => ['id', 'order_type', 'pay_amount', 'pay_remark'],
|
||||
'linkPayOrder' => ['id', 'pay_order_id'],
|
||||
'complete' => ['id'],
|
||||
'complete' => ['id', 'fulfillment_status'],
|
||||
'submitGancaoRecipel' => ['id'],
|
||||
'previewGancaoRecipel' => ['id'],
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user