更新
This commit is contained in:
@@ -41,6 +41,9 @@ UNIQUE_IDENTIFICATION = likeadmin
|
||||
# 演示环境
|
||||
DEMO_ENV = false
|
||||
|
||||
; 处方业务订单:关联收款每笔金额须大于等于该值(元),业务订单金额也须大于等于该值;0=不校验
|
||||
PRESCRIPTION_ORDER_LINK_PAY_MIN_AMOUNT = 0
|
||||
|
||||
# 物流轨迹(快递100):https://api.kuaidi100.com
|
||||
; ThinkPHP .env 为 INI:下列建议写在分区声明之前;若误写在 trtc 分区内,config 已兼容
|
||||
# 同时填 CUSTOMER、KEY 即启用;临时关闭设 LOGISTICS_KUAIDI100_DISABLE = true
|
||||
|
||||
@@ -68,6 +68,7 @@ class PrescriptionController extends BaseAdminController
|
||||
{
|
||||
$params = (new PrescriptionValidate())->get()->goCheck('detail');
|
||||
$detail = PrescriptionLogic::detail((int) $params['id'], (int) $this->adminId, $this->adminInfo);
|
||||
|
||||
if (!$detail) {
|
||||
$msg = PrescriptionLogic::getError();
|
||||
|
||||
@@ -129,8 +130,12 @@ class PrescriptionController extends BaseAdminController
|
||||
if (!$appointmentId) {
|
||||
return $this->fail('预约ID不能为空');
|
||||
}
|
||||
$prescription = PrescriptionLogic::getByAppointment($appointmentId);
|
||||
return $this->data($prescription ?? []);
|
||||
$prescription = PrescriptionLogic::getByAppointment($appointmentId, (int)$this->adminId, $this->adminInfo);
|
||||
if ($prescription === null) {
|
||||
//$msg = PrescriptionLogic::getError();
|
||||
return '';
|
||||
}
|
||||
return $this->data($prescription);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,7 +34,19 @@ class PrescriptionOrderController extends BaseAdminController
|
||||
$exceptPo > 0 ? $exceptPo : null
|
||||
);
|
||||
|
||||
return $this->success('', ['lists' => $lists]);
|
||||
$min = PrescriptionOrderLogic::depositMinAmount();
|
||||
|
||||
// if ($min > 0) {
|
||||
// $lists = array_values(array_filter(
|
||||
// $lists,
|
||||
// static fn(array $r): bool => round((float) ($r['amount'] ?? 0), 2) >= $min
|
||||
// ));
|
||||
// }
|
||||
|
||||
return $this->success('', [
|
||||
'lists' => $lists,
|
||||
'deposit_min_amount' => $min,
|
||||
]);
|
||||
}
|
||||
|
||||
public function create()
|
||||
@@ -107,6 +119,24 @@ class PrescriptionOrderController extends BaseAdminController
|
||||
return $this->success('操作成功', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回处方审核
|
||||
*/
|
||||
public function revokeRxAudit()
|
||||
{
|
||||
$params = (new PrescriptionOrderValidate())->post()->goCheck('detail');
|
||||
$result = PrescriptionOrderLogic::revokeRxAudit(
|
||||
(int) $params['id'],
|
||||
$this->adminId,
|
||||
$this->adminInfo
|
||||
);
|
||||
if ($result === false) {
|
||||
return $this->fail(PrescriptionOrderLogic::getError());
|
||||
}
|
||||
|
||||
return $this->success('处方审核已撤回', $result);
|
||||
}
|
||||
|
||||
public function auditPayment()
|
||||
{
|
||||
$params = (new PrescriptionOrderValidate())->post()->goCheck('auditPayment');
|
||||
@@ -124,6 +154,44 @@ class PrescriptionOrderController extends BaseAdminController
|
||||
return $this->success('操作成功', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回支付单审核
|
||||
*/
|
||||
public function revokePayAudit()
|
||||
{
|
||||
$params = (new PrescriptionOrderValidate())->post()->goCheck('detail');
|
||||
$result = PrescriptionOrderLogic::revokePayAudit(
|
||||
(int) $params['id'],
|
||||
$this->adminId,
|
||||
$this->adminInfo
|
||||
);
|
||||
if ($result === false) {
|
||||
return $this->fail(PrescriptionOrderLogic::getError());
|
||||
}
|
||||
|
||||
return $this->success('支付单审核已撤回', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认发货:将履约状态推进到 5(已发货),同时更新快递信息
|
||||
*/
|
||||
public function ship()
|
||||
{
|
||||
$params = (new PrescriptionOrderValidate())->post()->goCheck('ship');
|
||||
$result = PrescriptionOrderLogic::ship(
|
||||
(int) $params['id'],
|
||||
(string) ($params['express_company'] ?? 'auto'),
|
||||
(string) ($params['tracking_number'] ?? ''),
|
||||
$this->adminId,
|
||||
$this->adminInfo
|
||||
);
|
||||
if ($result === false) {
|
||||
return $this->fail(PrescriptionOrderLogic::getError());
|
||||
}
|
||||
|
||||
return $this->success('发货成功', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 医助/创建人撤回:仅「待双审通过」可撤(未通过或双审均驳回等仍为该状态)
|
||||
*/
|
||||
@@ -137,4 +205,48 @@ class PrescriptionOrderController extends BaseAdminController
|
||||
|
||||
return $this->success('已撤回', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取业务订单操作日志
|
||||
*/
|
||||
public function logs()
|
||||
{
|
||||
$params = (new PrescriptionOrderValidate())->get()->goCheck('logs');
|
||||
$result = PrescriptionOrderLogic::getLogs((int) $params['id'], $this->adminId, $this->adminInfo);
|
||||
|
||||
// 遇到没有权限或者订单不存在,逻辑里设置了 self::$error
|
||||
if ($result === [] && PrescriptionOrderLogic::getError() !== '') {
|
||||
return $this->fail(PrescriptionOrderLogic::getError());
|
||||
}
|
||||
|
||||
return $this->success('', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为「已发货」订单新增一条关联支付单,并重置支付单审核状态为待审核
|
||||
*/
|
||||
public function addPayOrder()
|
||||
{
|
||||
$params = (new PrescriptionOrderValidate())->post()->goCheck('addPayOrder');
|
||||
$result = PrescriptionOrderLogic::addPayOrder($params, $this->adminId, $this->adminInfo);
|
||||
if ($result === false) {
|
||||
return $this->fail(PrescriptionOrderLogic::getError());
|
||||
}
|
||||
|
||||
return $this->success('新增支付单成功', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将「已发货」且支付审核已通过的订单标记为「已完成」
|
||||
*/
|
||||
public function complete()
|
||||
{
|
||||
$params = (new PrescriptionOrderValidate())->post()->goCheck('complete');
|
||||
$result = PrescriptionOrderLogic::complete((int) $params['id'], $this->adminId, $this->adminInfo);
|
||||
if ($result === false) {
|
||||
return $this->fail(PrescriptionOrderLogic::getError());
|
||||
}
|
||||
|
||||
return $this->success('订单已完成', $result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ class OrderLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
$where[] = ['patient_id', 'in', function ($query) {
|
||||
$query->table('zyt_tcm_diagnosis')
|
||||
->whereNull('delete_time')
|
||||
->where('patient_name|patient_phone', 'like', '%' . $this->params['patient_keyword'] . '%')
|
||||
->where('patient_name|phone', 'like', '%' . $this->params['patient_keyword'] . '%')
|
||||
->field('id');
|
||||
}];
|
||||
}
|
||||
@@ -128,7 +128,7 @@ class OrderLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
$where[] = ['patient_id', 'in', function ($query) {
|
||||
$query->table('zyt_tcm_diagnosis')
|
||||
->whereNull('delete_time')
|
||||
->where('patient_name|patient_phone', 'like', '%' . $this->params['patient_keyword'] . '%')
|
||||
->where('patient_name|phone', 'like', '%' . $this->params['patient_keyword'] . '%')
|
||||
->field('id');
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -6,12 +6,14 @@ namespace app\adminapi\lists\tcm;
|
||||
|
||||
use app\adminapi\lists\BaseAdminDataLists;
|
||||
use app\adminapi\logic\tcm\PrescriptionOrderLogic;
|
||||
use app\common\lists\ListsExtendInterface;
|
||||
use app\common\lists\ListsSearchInterface;
|
||||
use app\common\model\Order;
|
||||
use app\common\model\tcm\Diagnosis;
|
||||
use app\common\model\tcm\PrescriptionOrder;
|
||||
use app\common\model\tcm\PrescriptionOrderPayOrder;
|
||||
|
||||
class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchInterface, ListsExtendInterface
|
||||
{
|
||||
public function setSearch(): array
|
||||
{
|
||||
@@ -38,18 +40,115 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
->limit($this->limitOffset, $this->limitLength)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
$poIds = array_values(array_filter(array_map('intval', array_column($lists, 'id'))));
|
||||
$paidSumByPo = [];
|
||||
if ($poIds !== []) {
|
||||
$links = PrescriptionOrderPayOrder::whereIn('prescription_order_id', $poIds)
|
||||
->field(['prescription_order_id', 'pay_order_id'])
|
||||
->select()
|
||||
->toArray();
|
||||
$byPo = [];
|
||||
foreach ($links as $l) {
|
||||
$poid = (int) ($l['prescription_order_id'] ?? 0);
|
||||
$oid = (int) ($l['pay_order_id'] ?? 0);
|
||||
if ($poid > 0 && $oid > 0) {
|
||||
$byPo[$poid][] = $oid;
|
||||
}
|
||||
}
|
||||
$allOids = [];
|
||||
foreach ($byPo as $oids) {
|
||||
$allOids = array_merge($allOids, $oids);
|
||||
}
|
||||
$allOids = array_values(array_unique($allOids));
|
||||
$amountByOid = [];
|
||||
if ($allOids !== []) {
|
||||
$amountByOid = Order::whereIn('id', $allOids)->whereNull('delete_time')->column('amount', 'id');
|
||||
}
|
||||
foreach ($poIds as $pid) {
|
||||
$s = 0.0;
|
||||
foreach ($byPo[$pid] ?? [] as $oid) {
|
||||
$s += (float) ($amountByOid[$oid] ?? 0);
|
||||
}
|
||||
$paidSumByPo[$pid] = round($s, 2);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取创建人姓名
|
||||
$creatorIds = array_values(array_unique(array_filter(array_map('intval', array_column($lists, 'creator_id')))));
|
||||
$creatorNames = [];
|
||||
if ($creatorIds !== []) {
|
||||
$creatorNames = \app\common\model\auth\Admin::whereIn('id', $creatorIds)->column('name', 'id');
|
||||
}
|
||||
|
||||
// 获取处方ID对应的开方人姓名
|
||||
$prescriptionIds = array_values(array_unique(array_filter(array_map('intval', array_column($lists, 'prescription_id')))));
|
||||
$prescriptionCreators = [];
|
||||
if ($prescriptionIds !== []) {
|
||||
$prescriptions = \app\common\model\tcm\Prescription::whereIn('id', $prescriptionIds)
|
||||
->whereNull('delete_time')
|
||||
->field(['id', 'creator_id', 'doctor_name'])
|
||||
->select()
|
||||
->toArray();
|
||||
$doctorIds = [];
|
||||
foreach ($prescriptions as $rx) {
|
||||
$rxId = (int) ($rx['id'] ?? 0);
|
||||
$doctorId = (int) ($rx['creator_id'] ?? 0);
|
||||
$doctorName = (string) ($rx['doctor_name'] ?? '');
|
||||
if ($rxId > 0) {
|
||||
$prescriptionCreators[$rxId] = [
|
||||
'doctor_id' => $doctorId,
|
||||
'doctor_name' => $doctorName
|
||||
];
|
||||
if ($doctorId > 0) {
|
||||
$doctorIds[] = $doctorId;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果 doctor_name 为空,从 Admin 表获取
|
||||
$doctorIds = array_values(array_unique($doctorIds));
|
||||
if ($doctorIds !== []) {
|
||||
$doctorNamesFromAdmin = \app\common\model\auth\Admin::whereIn('id', $doctorIds)->column('name', 'id');
|
||||
foreach ($prescriptionCreators as &$pc) {
|
||||
if (empty($pc['doctor_name']) && $pc['doctor_id'] > 0) {
|
||||
$pc['doctor_name'] = $doctorNamesFromAdmin[$pc['doctor_id']] ?? '';
|
||||
}
|
||||
}
|
||||
unset($pc);
|
||||
}
|
||||
}
|
||||
|
||||
$depMin = PrescriptionOrderLogic::depositMinAmount();
|
||||
foreach ($lists as &$item) {
|
||||
PrescriptionOrderLogic::maskInternalCostIfNeeded($item, $this->adminInfo);
|
||||
$pid = (int) ($item['id'] ?? 0);
|
||||
$item['linked_pay_order_count'] = (int) PrescriptionOrderPayOrder::where(
|
||||
'prescription_order_id',
|
||||
(int) ($item['id'] ?? 0)
|
||||
$pid
|
||||
)->count();
|
||||
$item['linked_pay_paid_total'] = $paidSumByPo[$pid] ?? 0.0;
|
||||
$item['deposit_min_amount'] = $depMin;
|
||||
|
||||
// 添加创建人姓名
|
||||
$creatorId = (int) ($item['creator_id'] ?? 0);
|
||||
$item['creator_name'] = $creatorId > 0 ? ($creatorNames[$creatorId] ?? '') : '';
|
||||
|
||||
// 添加开方人姓名
|
||||
$rxId = (int) ($item['prescription_id'] ?? 0);
|
||||
$item['doctor_name'] = $rxId > 0 ? ($prescriptionCreators[$rxId]['doctor_name'] ?? '') : '';
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return $lists;
|
||||
}
|
||||
|
||||
public function extend(): array
|
||||
{
|
||||
return [
|
||||
'deposit_min_amount' => PrescriptionOrderLogic::depositMinAmount(),
|
||||
];
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
$query = PrescriptionOrder::where($this->searchWhere)->whereNull('delete_time');
|
||||
|
||||
@@ -47,7 +47,7 @@ class OrderLogic
|
||||
$order->status = 1; // 待支付
|
||||
$order->remark = $params['remark'] ?? '';
|
||||
$order->save();
|
||||
|
||||
|
||||
return $order;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
@@ -102,146 +102,146 @@ class OrderLogic
|
||||
$cursor = '';
|
||||
do {
|
||||
$resp = $service->getBillList($beginTime, $endTime, $payeeUserid, $cursor, 100);
|
||||
if (($resp['errcode'] ?? -1) !== 0) {
|
||||
$result['errors'][] = '获取账单失败: ' . ($resp['errmsg'] ?? '未知错误');
|
||||
break;
|
||||
}
|
||||
$billList = $resp['bill_list'] ?? [];
|
||||
$cursor = $resp['next_cursor'] ?? '';
|
||||
|
||||
foreach ($billList as $bill) {
|
||||
$tradeState = (int)($bill['trade_state'] ?? 0);
|
||||
// 1:已完成(已支付) 3:已完成有退款 — 见企业微信 get_bill_list 文档
|
||||
if (!in_array($tradeState, [1, 3], true)) {
|
||||
$result['skipped']++;
|
||||
continue;
|
||||
if (($resp['errcode'] ?? -1) !== 0) {
|
||||
$result['errors'][] = '获取账单失败: ' . ($resp['errmsg'] ?? '未知错误');
|
||||
break;
|
||||
}
|
||||
$targetStatus = $tradeState === 3 ? 4 : 2; // 4=已退款 2=已支付
|
||||
$billList = $resp['bill_list'] ?? [];
|
||||
$cursor = $resp['next_cursor'] ?? '';
|
||||
|
||||
$orderNo = (string)($bill['out_trade_no'] ?? $bill['trade_no'] ?? '');
|
||||
$totalFee = (int)($bill['total_fee'] ?? $bill['total_amount'] ?? 0);
|
||||
$tradeNo = (string)($bill['transaction_id'] ?? $bill['trade_no'] ?? '');
|
||||
$payTime = $bill['pay_time'] ?? $bill['time_end'] ?? '';
|
||||
$billPayeeUserid = (string)($bill['payee_userid'] ?? '');
|
||||
|
||||
if (empty($orderNo) || $totalFee <= 0) {
|
||||
$result['skipped']++;
|
||||
continue;
|
||||
}
|
||||
$amount = round($totalFee / 100, 2);
|
||||
$orderType = ($amount == 5.00) ? 1 : $defaultOrderType;
|
||||
|
||||
$creatorId = 0;
|
||||
if ($billPayeeUserid) {
|
||||
$admin = Admin::where('work_wechat_userid', $billPayeeUserid)->whereNull('delete_time')->find();
|
||||
if ($admin) {
|
||||
$creatorId = (int)$admin->id;
|
||||
}
|
||||
}
|
||||
|
||||
$payerExternalUserid = (string)($bill['external_userid'] ?? '');
|
||||
$paymentTimeStr = $payTime
|
||||
? (is_numeric($payTime) ? date('Y-m-d H:i:s', (int)$payTime) : (string)$payTime)
|
||||
: date('Y-m-d H:i:s');
|
||||
|
||||
$order = Order::where('order_no', $orderNo)->find();
|
||||
if ($order) {
|
||||
$needSave = false;
|
||||
$currentStatus = (int)$order->status;
|
||||
|
||||
if ($currentStatus !== $targetStatus) {
|
||||
// 待支付:随账单变为已支付或已退款(含未同步到「已支付」即发生退款)
|
||||
if ($currentStatus === 1) {
|
||||
$order->status = $targetStatus;
|
||||
$needSave = true;
|
||||
} elseif ($currentStatus === 2 && $targetStatus === 4) {
|
||||
$order->status = 4;
|
||||
$needSave = true;
|
||||
} elseif ($currentStatus === 4 && $targetStatus === 2) {
|
||||
// 账单从「有退款」变回「已完成」等异常纠偏
|
||||
$order->status = 2;
|
||||
$needSave = true;
|
||||
}
|
||||
// 已取消(3) 不自动改支付状态,避免覆盖后台取消
|
||||
}
|
||||
|
||||
if ($currentStatus === 1 || $targetStatus === 2) {
|
||||
if ($order->payment_method !== 'wechat') {
|
||||
$order->payment_method = 'wechat';
|
||||
$needSave = true;
|
||||
}
|
||||
if ($paymentTimeStr !== '' && (string)$order->payment_time !== $paymentTimeStr) {
|
||||
$order->payment_time = $paymentTimeStr;
|
||||
$needSave = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($tradeNo !== '' && (string)$order->trade_no !== $tradeNo) {
|
||||
$order->trade_no = $tradeNo;
|
||||
$needSave = true;
|
||||
}
|
||||
|
||||
if (abs((float)$order->amount - $amount) > 0.000001) {
|
||||
$order->amount = $amount;
|
||||
$needSave = true;
|
||||
}
|
||||
|
||||
$defaultCreatorIdInt = (int)$defaultCreatorId;
|
||||
if ($billPayeeUserid && $creatorId !== $defaultCreatorIdInt && (int)$order->creator_id === $defaultCreatorIdInt) {
|
||||
$order->payee_userid = $billPayeeUserid;
|
||||
$order->payer_external_userid = $payerExternalUserid;
|
||||
$order->creator_id = $creatorId;
|
||||
$needSave = true;
|
||||
} elseif ($billPayeeUserid !== '' && (string)$order->payee_userid !== $billPayeeUserid) {
|
||||
$order->payee_userid = $billPayeeUserid;
|
||||
$needSave = true;
|
||||
}
|
||||
if ($payerExternalUserid !== '' && (string)$order->payer_external_userid !== $payerExternalUserid) {
|
||||
$order->payer_external_userid = $payerExternalUserid;
|
||||
$needSave = true;
|
||||
}
|
||||
|
||||
if ($amount == 5.00 && (int)$order->order_type !== 1) {
|
||||
$order->order_type = 1;
|
||||
$needSave = true;
|
||||
}
|
||||
|
||||
if ($needSave) {
|
||||
$order->save();
|
||||
$result['updated']++;
|
||||
} else {
|
||||
foreach ($billList as $bill) {
|
||||
$tradeState = (int)($bill['trade_state'] ?? 0);
|
||||
// 1:已完成(已支付) 3:已完成有退款 — 见企业微信 get_bill_list 文档
|
||||
if (!in_array($tradeState, [1, 3], true)) {
|
||||
$result['skipped']++;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$order = new Order();
|
||||
$order->order_no = $orderNo;
|
||||
$order->patient_id = null;
|
||||
$order->creator_id = $creatorId;
|
||||
$order->order_type = $orderType;
|
||||
$order->amount = $amount;
|
||||
$order->status = $targetStatus;
|
||||
$order->payment_method = 'wechat';
|
||||
$order->payment_time = $paymentTimeStr;
|
||||
$order->trade_no = $tradeNo;
|
||||
$remark = '企业微信直接收款同步,待关联患者';
|
||||
if ($tradeState === 3) {
|
||||
$refundCent = (int)($bill['total_refund_fee'] ?? 0);
|
||||
if ($refundCent > 0) {
|
||||
$remark .= '(账单含退款' . round($refundCent / 100, 2) . '元)';
|
||||
$targetStatus = $tradeState === 3 ? 4 : 2; // 4=已退款 2=已支付
|
||||
|
||||
$orderNo = (string)($bill['out_trade_no'] ?? $bill['trade_no'] ?? '');
|
||||
$totalFee = (int)($bill['total_fee'] ?? $bill['total_amount'] ?? 0);
|
||||
$tradeNo = (string)($bill['transaction_id'] ?? $bill['trade_no'] ?? '');
|
||||
$payTime = $bill['pay_time'] ?? $bill['time_end'] ?? '';
|
||||
$billPayeeUserid = (string)($bill['payee_userid'] ?? '');
|
||||
|
||||
if (empty($orderNo) || $totalFee <= 0) {
|
||||
$result['skipped']++;
|
||||
continue;
|
||||
}
|
||||
$amount = round($totalFee / 100, 2);
|
||||
$orderType = ($amount == 5.00) ? 1 : $defaultOrderType;
|
||||
|
||||
$creatorId = 0;
|
||||
if ($billPayeeUserid) {
|
||||
$admin = Admin::where('work_wechat_userid', $billPayeeUserid)->whereNull('delete_time')->find();
|
||||
if ($admin) {
|
||||
$creatorId = (int)$admin->id;
|
||||
}
|
||||
}
|
||||
|
||||
$payerExternalUserid = (string)($bill['external_userid'] ?? '');
|
||||
$paymentTimeStr = $payTime
|
||||
? (is_numeric($payTime) ? date('Y-m-d H:i:s', (int)$payTime) : (string)$payTime)
|
||||
: date('Y-m-d H:i:s');
|
||||
|
||||
$order = Order::where('order_no', $orderNo)->find();
|
||||
if ($order) {
|
||||
$needSave = false;
|
||||
$currentStatus = (int)$order->status;
|
||||
|
||||
if ($currentStatus !== $targetStatus) {
|
||||
// 待支付:随账单变为已支付或已退款(含未同步到「已支付」即发生退款)
|
||||
if ($currentStatus === 1) {
|
||||
$order->status = $targetStatus;
|
||||
$needSave = true;
|
||||
} elseif ($currentStatus === 2 && $targetStatus === 4) {
|
||||
$order->status = 4;
|
||||
$needSave = true;
|
||||
} elseif ($currentStatus === 4 && $targetStatus === 2) {
|
||||
// 账单从「有退款」变回「已完成」等异常纠偏
|
||||
$order->status = 2;
|
||||
$needSave = true;
|
||||
}
|
||||
// 已取消(3) 不自动改支付状态,避免覆盖后台取消
|
||||
}
|
||||
|
||||
if ($currentStatus === 1 || $targetStatus === 2) {
|
||||
if ($order->payment_method !== 'wechat') {
|
||||
$order->payment_method = 'wechat';
|
||||
$needSave = true;
|
||||
}
|
||||
if ($paymentTimeStr !== '' && (string)$order->payment_time !== $paymentTimeStr) {
|
||||
$order->payment_time = $paymentTimeStr;
|
||||
$needSave = true;
|
||||
}
|
||||
}
|
||||
$order->remark = $remark;
|
||||
$order->payee_userid = $billPayeeUserid;
|
||||
$order->payer_external_userid = $payerExternalUserid;
|
||||
$order->save();
|
||||
$result['created']++;
|
||||
} catch (\Exception $e) {
|
||||
$result['errors'][] = "订单 {$orderNo}: " . $e->getMessage();
|
||||
Log::error("企业微信账单同步创建订单失败: {$orderNo}, " . $e->getMessage());
|
||||
|
||||
if ($tradeNo !== '' && (string)$order->trade_no !== $tradeNo) {
|
||||
$order->trade_no = $tradeNo;
|
||||
$needSave = true;
|
||||
}
|
||||
|
||||
if (abs((float)$order->amount - $amount) > 0.000001) {
|
||||
$order->amount = $amount;
|
||||
$needSave = true;
|
||||
}
|
||||
|
||||
$defaultCreatorIdInt = (int)$defaultCreatorId;
|
||||
if ($billPayeeUserid && $creatorId !== $defaultCreatorIdInt && (int)$order->creator_id === $defaultCreatorIdInt) {
|
||||
$order->payee_userid = $billPayeeUserid;
|
||||
$order->payer_external_userid = $payerExternalUserid;
|
||||
$order->creator_id = $creatorId;
|
||||
$needSave = true;
|
||||
} elseif ($billPayeeUserid !== '' && (string)$order->payee_userid !== $billPayeeUserid) {
|
||||
$order->payee_userid = $billPayeeUserid;
|
||||
$needSave = true;
|
||||
}
|
||||
if ($payerExternalUserid !== '' && (string)$order->payer_external_userid !== $payerExternalUserid) {
|
||||
$order->payer_external_userid = $payerExternalUserid;
|
||||
$needSave = true;
|
||||
}
|
||||
|
||||
if ($amount == 5.00 && (int)$order->order_type !== 1) {
|
||||
$order->order_type = 1;
|
||||
$needSave = true;
|
||||
}
|
||||
|
||||
if ($needSave) {
|
||||
$order->save();
|
||||
$result['updated']++;
|
||||
} else {
|
||||
$result['skipped']++;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$order = new Order();
|
||||
$order->order_no = $orderNo;
|
||||
$order->patient_id = null;
|
||||
$order->creator_id = $creatorId;
|
||||
$order->order_type = $orderType;
|
||||
$order->amount = $amount;
|
||||
$order->status = $targetStatus;
|
||||
$order->payment_method = 'wechat';
|
||||
$order->payment_time = $paymentTimeStr;
|
||||
$order->trade_no = $tradeNo;
|
||||
$remark = '企业微信直接收款同步,待关联患者';
|
||||
if ($tradeState === 3) {
|
||||
$refundCent = (int)($bill['total_refund_fee'] ?? 0);
|
||||
if ($refundCent > 0) {
|
||||
$remark .= '(账单含退款' . round($refundCent / 100, 2) . '元)';
|
||||
}
|
||||
}
|
||||
$order->remark = $remark;
|
||||
$order->payee_userid = $billPayeeUserid;
|
||||
$order->payer_external_userid = $payerExternalUserid;
|
||||
$order->save();
|
||||
$result['created']++;
|
||||
} catch (\Exception $e) {
|
||||
$result['errors'][] = "订单 {$orderNo}: " . $e->getMessage();
|
||||
Log::error("企业微信账单同步创建订单失败: {$orderNo}, " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ($cursor);
|
||||
}
|
||||
|
||||
@@ -295,7 +295,7 @@ class OrderLogic
|
||||
self::setError('订单不存在');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (isset($params['remark'])) {
|
||||
$order->remark = $params['remark'];
|
||||
}
|
||||
@@ -307,7 +307,7 @@ class OrderLogic
|
||||
if (isset($params['order_type'])) {
|
||||
$order->order_type = (int)$params['order_type'];
|
||||
}
|
||||
|
||||
|
||||
$order->save();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
@@ -330,17 +330,17 @@ class OrderLogic
|
||||
self::setError('订单不存在');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if ($order->status != 1) {
|
||||
self::setError('订单状态不允许支付');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$order->status = 2; // 已支付
|
||||
$order->payment_method = $paymentMethod;
|
||||
$order->payment_time = date('Y-m-d H:i:s');
|
||||
$order->save();
|
||||
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
@@ -361,15 +361,15 @@ class OrderLogic
|
||||
self::setError('订单不存在');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if ($order->status != 1) {
|
||||
self::setError('只有待支付的订单才能取消');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$order->status = 3; // 已取消
|
||||
$order->save();
|
||||
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
@@ -390,15 +390,15 @@ class OrderLogic
|
||||
self::setError('订单不存在');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if ($order->status != 2) {
|
||||
self::setError('只有已支付的订单才能退款');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$order->status = 4; // 已退款
|
||||
$order->save();
|
||||
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
@@ -419,10 +419,10 @@ class OrderLogic
|
||||
self::setError('订单不存在');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 删除订单
|
||||
$order->delete();
|
||||
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
@@ -466,11 +466,11 @@ class OrderLogic
|
||||
{
|
||||
$order = Order::with(['patient', 'creator'])
|
||||
->find($id);
|
||||
|
||||
|
||||
if (!$order) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return $order->toArray();
|
||||
}
|
||||
|
||||
@@ -496,10 +496,10 @@ class OrderLogic
|
||||
{
|
||||
// 移除sign字段
|
||||
unset($params['sign']);
|
||||
|
||||
|
||||
// 按键排序
|
||||
ksort($params);
|
||||
|
||||
|
||||
// 构建签名字符串
|
||||
$signStr = '';
|
||||
foreach ($params as $key => $value) {
|
||||
@@ -508,12 +508,12 @@ class OrderLogic
|
||||
}
|
||||
}
|
||||
$signStr = rtrim($signStr, '&');
|
||||
|
||||
|
||||
// 使用私钥签名
|
||||
$privateKeyResource = openssl_pkey_get_private($privateKey);
|
||||
openssl_sign($signStr, $sign, $privateKeyResource, OPENSSL_ALGO_SHA256);
|
||||
openssl_free_key($privateKeyResource);
|
||||
|
||||
|
||||
return base64_encode($sign);
|
||||
}
|
||||
|
||||
@@ -527,10 +527,10 @@ class OrderLogic
|
||||
{
|
||||
// 移除sign字段
|
||||
unset($params['sign']);
|
||||
|
||||
|
||||
// 按键排序
|
||||
ksort($params);
|
||||
|
||||
|
||||
// 构建签名字符串
|
||||
$signStr = '';
|
||||
foreach ($params as $key => $value) {
|
||||
@@ -539,7 +539,7 @@ class OrderLogic
|
||||
}
|
||||
}
|
||||
$signStr .= 'key=' . $apiKey;
|
||||
|
||||
|
||||
// MD5签名
|
||||
return strtoupper(md5($signStr));
|
||||
}
|
||||
@@ -606,14 +606,14 @@ class OrderLogic
|
||||
// 获取订单号
|
||||
$orderNo = is_array($order) ? $order['order_no'] : $order->order_no;
|
||||
$amount = is_array($order) ? ($order['amount'] ?? 0) : $order->amount;
|
||||
|
||||
|
||||
// 获取支付宝配置
|
||||
$alipayConfig = config('pay.alipay');
|
||||
|
||||
|
||||
if (empty($alipayConfig['app_id']) || empty($alipayConfig['merchant_private_key'])) {
|
||||
throw new \Exception('支付宝配置不完整');
|
||||
}
|
||||
|
||||
|
||||
// 使用支付宝SDK生成支付URL
|
||||
$params = [
|
||||
'app_id' => $alipayConfig['app_id'],
|
||||
@@ -632,14 +632,14 @@ class OrderLogic
|
||||
'body' => '订单号:' . $orderNo,
|
||||
], JSON_UNESCAPED_UNICODE),
|
||||
];
|
||||
|
||||
|
||||
// 生成签名
|
||||
$sign = self::generateAlipaySign($params, $alipayConfig['merchant_private_key']);
|
||||
$params['sign'] = $sign;
|
||||
|
||||
|
||||
// 生成支付URL
|
||||
$payUrl = $alipayConfig['gateway_url'] . '?' . http_build_query($params);
|
||||
|
||||
|
||||
return $payUrl;
|
||||
} catch (\Exception $e) {
|
||||
self::setError($e->getMessage());
|
||||
@@ -658,14 +658,14 @@ class OrderLogic
|
||||
// 获取订单号
|
||||
$orderNo = is_array($order) ? $order['order_no'] : $order->order_no;
|
||||
$amount = is_array($order) ? ($order['amount'] ?? 0) : $order->amount;
|
||||
|
||||
|
||||
// 获取微信配置
|
||||
$wechatConfig = config('pay.wechat');
|
||||
|
||||
|
||||
if (empty($wechatConfig['app_id']) || empty($wechatConfig['mch_id']) || empty($wechatConfig['api_key'])) {
|
||||
throw new \Exception('微信支付配置不完整');
|
||||
}
|
||||
|
||||
|
||||
// 生成微信支付数据
|
||||
$payData = [
|
||||
'appid' => $wechatConfig['app_id'],
|
||||
@@ -678,11 +678,11 @@ class OrderLogic
|
||||
'notify_url' => $wechatConfig['notify_url'] ?: config('app.url') . '/api/order/wechat-notify',
|
||||
'trade_type' => 'NATIVE', // 二维码支付
|
||||
];
|
||||
|
||||
|
||||
// 生成签名
|
||||
$sign = self::generateWechatSign($payData, $wechatConfig['api_key']);
|
||||
$payData['sign'] = $sign;
|
||||
|
||||
|
||||
// 返回支付数据
|
||||
return [
|
||||
'pay_url' => $wechatConfig['gateway_url'] . '/pay/unifiedorder',
|
||||
@@ -918,7 +918,7 @@ class OrderLogic
|
||||
if (!empty($adminInfo['root']) && (int) $adminInfo['root'] === 1) {
|
||||
return true;
|
||||
}
|
||||
$supervisorRoles = [0, 3];
|
||||
$supervisorRoles = config('project.order_edit_all_roles', [0, 3]);
|
||||
$myRoles = array_map('intval', $adminInfo['role_id'] ?? []);
|
||||
|
||||
return count(array_intersect($myRoles, $supervisorRoles)) > 0;
|
||||
@@ -985,6 +985,7 @@ class OrderLogic
|
||||
if ($busy && ! $allowed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$out[] = $row;
|
||||
}
|
||||
|
||||
@@ -998,7 +999,7 @@ class OrderLogic
|
||||
*/
|
||||
public static function assertPayOrdersLinkable(array $ids, int $diagnosisId, int $adminId, array $adminInfo): ?string
|
||||
{
|
||||
$ids = array_values(array_unique(array_filter(array_map('intval', $ids), static fn (int $v): bool => $v > 0)));
|
||||
$ids = array_values(array_unique(array_filter(array_map('intval', $ids), static fn(int $v): bool => $v > 0)));
|
||||
if ($diagnosisId <= 0) {
|
||||
return '诊单无效';
|
||||
}
|
||||
|
||||
@@ -74,30 +74,54 @@ class PrescriptionLogic
|
||||
*/
|
||||
public static function canViewPrescription($row, int $adminId, array $adminInfo): bool
|
||||
{
|
||||
// 超级管理员
|
||||
if (!empty($adminInfo['root']) && (int) $adminInfo['root'] === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 创建人
|
||||
$creatorId = is_array($row) ? (int) ($row['creator_id'] ?? 0) : (int) $row->creator_id;
|
||||
if ($creatorId === $adminId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 医助
|
||||
$assistantId = is_array($row) ? (int) ($row['assistant_id'] ?? 0) : (int) ($row->assistant_id ?? 0);
|
||||
if ($assistantId > 0 && $assistantId === $adminId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 共享处方
|
||||
$isShared = is_array($row) ? (int) ($row['is_shared'] ?? 0) : (int) $row->is_shared;
|
||||
if ($isShared === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 可见角色
|
||||
$vis = is_array($row) ? (string) ($row['visible_role_ids'] ?? '') : (string) ($row->visible_role_ids ?? '');
|
||||
$allowed = array_filter(array_map('intval', explode(',', $vis)));
|
||||
$myRoles = array_map('intval', $adminInfo['role_id'] ?? []);
|
||||
if (count(array_intersect($myRoles, $allowed)) > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return count(array_intersect($myRoles, $allowed)) > 0;
|
||||
// 有开方权限的医生可以查看所有处方(只读)
|
||||
// 这样其他医生可以查看患者的历史处方记录
|
||||
$permissions = $adminInfo['permissions'] ?? [];
|
||||
if (in_array('tcm.diagnosis/kaifang', $permissions, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 有业务订单管理权限的角色(如药师)可以查看处方(只读)
|
||||
// 这样药师可以在业务订单中查看关联的处方信息
|
||||
$orderEditAllRoles = Config::get('project.order_edit_all_roles', [0, 3]);
|
||||
$myRoles = array_map('intval', $adminInfo['role_id'] ?? []);
|
||||
$allowedRoles = array_map('intval', is_array($orderEditAllRoles) ? $orderEditAllRoles : []);
|
||||
if (count(array_intersect($myRoles, $allowedRoles)) > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function generateSn(): string
|
||||
@@ -261,13 +285,10 @@ class PrescriptionLogic
|
||||
return false;
|
||||
}
|
||||
|
||||
// 已通过且未作废:不可编辑;例外:业务订单「处方审核」已驳回时允许医生改方,保存后业务订单侧审核会重置为待审
|
||||
// 已通过且未作废:不可编辑
|
||||
if ((int) ($prescription->audit_status ?? 1) === 1 && (int) ($prescription->void_status ?? 0) === 0) {
|
||||
if (!PrescriptionOrderLogic::allowDoctorEditApprovedPrescriptionDueToBusinessReject((int) $params['id'])) {
|
||||
self::setError('该处方已通过审核,不可编辑');
|
||||
|
||||
return false;
|
||||
}
|
||||
self::setError('该处方已通过审核,不可编辑');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 已驳回:仅当该诊单+当天+同一创建人下没有另一条「已通过且未作废」的处方时允许重新编辑(改后待审核、作废一并清除)
|
||||
@@ -435,6 +456,51 @@ class PrescriptionLogic
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务订单「处方审核」通过时:消费者处方仍为待审核则一并记为已通过,
|
||||
* 避免消费者处方列表筛选「未通过」仍包含该条、与业务侧已通过不一致。
|
||||
*/
|
||||
public static function syncApproveConsumerPrescriptionIfPending(
|
||||
int $prescriptionId,
|
||||
int $adminId,
|
||||
array $adminInfo,
|
||||
string $remark
|
||||
): void {
|
||||
if ($prescriptionId <= 0) {
|
||||
return;
|
||||
}
|
||||
$row = Prescription::find($prescriptionId);
|
||||
if (!$row) {
|
||||
return;
|
||||
}
|
||||
if ((int) ($row->void_status ?? 0) === 1) {
|
||||
return;
|
||||
}
|
||||
if ((int) ($row->audit_status ?? 1) !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$lastAuditWecomNotify = null;
|
||||
$name = (string) ($adminInfo['name'] ?? '');
|
||||
$now = time();
|
||||
$finalRemark = trim($remark) !== '' ? trim($remark) : '业务订单处方审核通过';
|
||||
$finalRemark = mb_substr($finalRemark, 0, 500);
|
||||
|
||||
$row->audit_status = 1;
|
||||
$row->audit_time = $now;
|
||||
$row->audit_by = $adminId;
|
||||
$row->audit_by_name = $name;
|
||||
$row->audit_remark = $finalRemark;
|
||||
|
||||
try {
|
||||
if ($row->save()) {
|
||||
self::$lastAuditWecomNotify = self::notifyCreatorAuditResult($row, 'approve', $finalRemark, $adminInfo);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('syncApproveConsumerPrescriptionIfPending: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
@@ -601,15 +667,28 @@ class PrescriptionLogic
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据预约ID获取处方
|
||||
* 根据预约ID获取处方(带权限检查)
|
||||
*/
|
||||
public static function getByAppointment(int $appointmentId): ?array
|
||||
public static function getByAppointment(int $appointmentId, int $viewerAdminId, array $viewerAdminInfo): ?array
|
||||
{
|
||||
self::$error = '';
|
||||
$row = Prescription::where('appointment_id', $appointmentId)
|
||||
->where('creator_id',$viewerAdminId)
|
||||
->whereNull('delete_time')
|
||||
->order('id', 'desc')
|
||||
->find();
|
||||
return $row ? $row->toArray() : null;
|
||||
|
||||
if (!$row) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 权限检查:只返回当前用户有权限查看的处方
|
||||
if (!self::canViewPrescription($row, $viewerAdminId, $viewerAdminInfo)) {
|
||||
self::setError('无权限查看此处方');
|
||||
return null;
|
||||
}
|
||||
|
||||
return $row->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,11 +4,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\logic\tcm;
|
||||
|
||||
use app\adminapi\logic\auth\AuthLogic;
|
||||
use app\adminapi\logic\order\OrderLogic;
|
||||
use app\common\model\Order;
|
||||
use app\common\model\tcm\Diagnosis;
|
||||
use app\common\model\tcm\Prescription;
|
||||
use app\common\model\tcm\PrescriptionOrder;
|
||||
use app\common\model\tcm\PrescriptionOrderLog;
|
||||
use app\common\model\tcm\PrescriptionOrderPayOrder;
|
||||
use app\common\model\auth\Admin;
|
||||
use app\common\service\ExpressTrackService;
|
||||
use think\facade\Config;
|
||||
|
||||
@@ -84,6 +88,20 @@ class PrescriptionOrderLogic
|
||||
return count(array_intersect($myRoles, $allow)) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务订单详情内是否返回/展示处方「药材明细」(菜单权限 tcm.prescriptionOrder/viewRxHerbs)
|
||||
*/
|
||||
private static function canViewPrescriptionHerbsInOrderDetail(array $adminInfo): bool
|
||||
{
|
||||
if (!empty($adminInfo['root']) && (int) $adminInfo['root'] === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$perms = AuthLogic::getAuthByAdminId((int) ($adminInfo['admin_id'] ?? 0));
|
||||
|
||||
return in_array('tcm.prescriptionOrder/viewRxHerbs', $perms, true);
|
||||
}
|
||||
|
||||
public static function canAuditPrescriptionOrder(array $adminInfo): bool
|
||||
{
|
||||
if (!empty($adminInfo['root']) && (int) $adminInfo['root'] === 1) {
|
||||
@@ -114,6 +132,47 @@ class PrescriptionOrderLogic
|
||||
}
|
||||
}
|
||||
|
||||
/** 定金门槛(元),0 表示不校验 */
|
||||
public static function depositMinAmount(): float
|
||||
{
|
||||
return round((float) Config::get('prescription_order.link_pay_min_amount', 0), 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $payOrderIds
|
||||
*/
|
||||
public static function assertDepositAndLinkPayRules(float $orderAmount, array $payOrderIds): ?string
|
||||
{
|
||||
$min = self::depositMinAmount();
|
||||
if ($min <= 0) {
|
||||
return null;
|
||||
}
|
||||
if ($orderAmount < $min) {
|
||||
return '订单金额须大于等于定金门槛 ¥' . $min . '(当前 ¥' . $orderAmount . ')';
|
||||
}
|
||||
if ($payOrderIds === []) {
|
||||
return '未关联支付单,关联支付单总金额须大于等于定金门槛 ¥' . $min;
|
||||
}
|
||||
|
||||
// 检查关联支付单的总金额
|
||||
$rows = Order::whereIn('id', $payOrderIds)->whereNull('delete_time')->column('amount', 'id');
|
||||
$totalAmount = 0.0;
|
||||
foreach ($payOrderIds as $pid) {
|
||||
$pid = (int) $pid;
|
||||
if ($pid <= 0) {
|
||||
continue;
|
||||
}
|
||||
$amt = round((float) ($rows[$pid] ?? 0), 2);
|
||||
$totalAmount += $amt;
|
||||
}
|
||||
|
||||
if ($totalAmount < $min) {
|
||||
return '关联支付单总金额须大于等于定金门槛 ¥' . $min . '(当前总金额 ¥' . $totalAmount . ')';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function generateOrderNo(): string
|
||||
{
|
||||
return 'PO' . date('YmdHis') . mt_rand(100000, 999999);
|
||||
@@ -279,6 +338,36 @@ class PrescriptionOrderLogic
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
$creatorIds = array_values(array_unique(array_filter(array_map('intval', array_column($rows, 'creator_id')))));
|
||||
$creatorNames = [];
|
||||
if ($creatorIds !== []) {
|
||||
$creatorNames = \app\common\model\auth\Admin::whereIn('id', $creatorIds)->column('name', 'id');
|
||||
}
|
||||
$typeMap = [
|
||||
1 => '挂号费',
|
||||
2 => '问诊费',
|
||||
3 => '药品费用',
|
||||
4 => '首付费用',
|
||||
5 => '尾款费用',
|
||||
6 => '其他费用',
|
||||
7 => '全部费用',
|
||||
];
|
||||
$statusMap = [
|
||||
1 => '待支付',
|
||||
2 => '已支付',
|
||||
3 => '已取消',
|
||||
4 => '已退款',
|
||||
];
|
||||
foreach ($rows as &$r) {
|
||||
$ot = (int) ($r['order_type'] ?? 0);
|
||||
$st = (int) ($r['status'] ?? 0);
|
||||
$r['order_type_desc'] = $typeMap[$ot] ?? '—';
|
||||
$r['status_desc'] = $statusMap[$st] ?? '—';
|
||||
$cid = (int) ($r['creator_id'] ?? 0);
|
||||
$r['creator_name'] = $cid > 0 ? (string) ($creatorNames[$cid] ?? '') : '';
|
||||
}
|
||||
unset($r);
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
@@ -291,12 +380,20 @@ class PrescriptionOrderLogic
|
||||
if ($id <= 0) {
|
||||
$arr['pay_order_ids'] = [];
|
||||
$arr['linked_pay_orders'] = [];
|
||||
$arr['linked_pay_paid_total'] = 0.0;
|
||||
$arr['deposit_min_amount'] = self::depositMinAmount();
|
||||
|
||||
return;
|
||||
}
|
||||
$ids = self::linkedPayOrderIdList($id);
|
||||
$arr['pay_order_ids'] = $ids;
|
||||
$arr['linked_pay_orders'] = self::linkedPayOrdersPayload($ids);
|
||||
$sum = 0.0;
|
||||
foreach ($arr['linked_pay_orders'] as $o) {
|
||||
$sum += (float) ($o['amount'] ?? 0);
|
||||
}
|
||||
$arr['linked_pay_paid_total'] = round($sum, 2);
|
||||
$arr['deposit_min_amount'] = self::depositMinAmount();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -352,6 +449,14 @@ class PrescriptionOrderLogic
|
||||
}
|
||||
}
|
||||
|
||||
$orderAmtPreview = round((float) ($params['amount'] ?? 0), 2);
|
||||
$depErrCreate = self::assertDepositAndLinkPayRules($orderAmtPreview, $payOrderIds);
|
||||
if ($depErrCreate !== null) {
|
||||
self::$error = $depErrCreate;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$internalCost = null;
|
||||
if (isset($params['internal_cost']) && $params['internal_cost'] !== '' && $params['internal_cost'] !== null) {
|
||||
if (self::canViewInternalCost($adminInfo)) {
|
||||
@@ -396,6 +501,8 @@ class PrescriptionOrderLogic
|
||||
return false;
|
||||
}
|
||||
|
||||
self::writeLog((int) $order->id, $adminId, $adminInfo, 'create', '创建业务订单');
|
||||
|
||||
if ($payOrderIds !== []) {
|
||||
self::replacePayOrderLinks((int) $order->id, $payOrderIds);
|
||||
}
|
||||
@@ -425,6 +532,44 @@ class PrescriptionOrderLogic
|
||||
self::maskInternalCostIfNeeded($arr, $adminInfo);
|
||||
self::attachLinkedPayOrders($arr);
|
||||
|
||||
// 添加创建人姓名
|
||||
$creatorId = (int) ($arr['creator_id'] ?? 0);
|
||||
if ($creatorId > 0) {
|
||||
$creatorName = Admin::where('id', $creatorId)->value('name');
|
||||
$arr['creator_name'] = $creatorName ? (string) $creatorName : '';
|
||||
} else {
|
||||
$arr['creator_name'] = '';
|
||||
}
|
||||
|
||||
$rxId = (int) ($arr['prescription_id'] ?? 0);
|
||||
$arr['prescription'] = null;
|
||||
$arr['prescription_detail_error'] = '';
|
||||
$arr['doctor_name'] = '';
|
||||
if ($rxId > 0) {
|
||||
$rxDetail = PrescriptionLogic::detail($rxId, $adminId, $adminInfo);
|
||||
if ($rxDetail !== null) {
|
||||
$arr['prescription'] = $rxDetail;
|
||||
// 添加开方人姓名(优先使用处方的 doctor_name,否则从 creator_id 获取)
|
||||
$doctorName = (string) ($rxDetail['doctor_name'] ?? '');
|
||||
if ($doctorName === '') {
|
||||
$rxCreatorId = (int) ($rxDetail['creator_id'] ?? 0);
|
||||
if ($rxCreatorId > 0) {
|
||||
$doctorName = (string) (Admin::where('id', $rxCreatorId)->value('name') ?? '');
|
||||
}
|
||||
}
|
||||
$arr['doctor_name'] = $doctorName;
|
||||
} else {
|
||||
$err = PrescriptionLogic::getError();
|
||||
$arr['prescription_detail_error'] = $err !== '' ? $err : '处方不存在';
|
||||
}
|
||||
}
|
||||
|
||||
$canHerbs = self::canViewPrescriptionHerbsInOrderDetail($adminInfo);
|
||||
$arr['prescription_detail_herbs_visible'] = $canHerbs;
|
||||
if (! $canHerbs && isset($arr['prescription']) && is_array($arr['prescription'])) {
|
||||
unset($arr['prescription']['herbs']);
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
@@ -581,9 +726,10 @@ class PrescriptionOrderLogic
|
||||
$order->linked_pay_order_id = $single[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (array_key_exists('internal_cost', $params) && self::canViewInternalCost($adminInfo)) {
|
||||
$raw = $params['internal_cost'];
|
||||
|
||||
if ($raw === '' || $raw === null) {
|
||||
$order->internal_cost = null;
|
||||
} else {
|
||||
@@ -591,6 +737,14 @@ class PrescriptionOrderLogic
|
||||
}
|
||||
}
|
||||
|
||||
$finalPayIds = self::linkedPayOrderIdList((int) $order->id);
|
||||
$depErr = self::assertDepositAndLinkPayRules(round((float) $order->amount, 2), $finalPayIds);
|
||||
if ($depErr !== null) {
|
||||
self::$error = $depErr;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$order->save();
|
||||
} catch (\Throwable $e) {
|
||||
@@ -599,6 +753,71 @@ class PrescriptionOrderLogic
|
||||
return false;
|
||||
}
|
||||
|
||||
self::writeLog((int) $order->id, $adminId, $adminInfo, 'edit', '编辑了业务订单及相关信息');
|
||||
|
||||
$out = $order->toArray();
|
||||
self::maskInternalCostIfNeeded($out, $adminInfo);
|
||||
self::attachLinkedPayOrders($out);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认发货:更新快递信息并将 fulfillment_status 从 2(履约中)推进到 5(已发货)
|
||||
*
|
||||
* @return array<string,mixed>|false
|
||||
*/
|
||||
public static function ship(int $id, string $expressCompany, string $trackingNumber, int $adminId, array $adminInfo)
|
||||
{
|
||||
self::$error = '';
|
||||
|
||||
$trackingNumber = trim($trackingNumber);
|
||||
if ($trackingNumber === '') {
|
||||
self::$error = '快递单号不能为空';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$order = PrescriptionOrder::where('id', $id)->whereNull('delete_time')->find();
|
||||
if (!$order) {
|
||||
self::$error = '订单不存在';
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!self::canAccessOrder($order, $adminId, $adminInfo)) {
|
||||
self::$error = '无权限操作';
|
||||
|
||||
return false;
|
||||
}
|
||||
if ((int) $order->fulfillment_status !== 2 && (int) $order->fulfillment_status !== 5) {
|
||||
self::$error = '仅「履约中」或「已发货」状态的订单可更新快递信息';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$order->express_company = self::normalizeExpressCompany($expressCompany);
|
||||
$order->tracking_number = $trackingNumber;
|
||||
// 仅履约中(2)时才推进到已发货(5),已发货(5)则只更新快递信息保持状态
|
||||
$isFirstShip = false;
|
||||
if ((int) $order->fulfillment_status === 2) {
|
||||
$order->fulfillment_status = 5;
|
||||
$isFirstShip = true;
|
||||
}
|
||||
|
||||
try {
|
||||
$order->save();
|
||||
} catch (\Throwable $e) {
|
||||
self::$error = $e->getMessage();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($isFirstShip) {
|
||||
self::writeLog((int) $order->id, $adminId, $adminInfo, 'ship', '确认发货,运单:' . $trackingNumber . '(' . $expressCompany . ')');
|
||||
} else {
|
||||
self::writeLog((int) $order->id, $adminId, $adminInfo, 'fill_tracking', '更新发货信息,运单:' . $trackingNumber . '(' . $expressCompany . ')');
|
||||
}
|
||||
|
||||
$out = $order->toArray();
|
||||
self::maskInternalCostIfNeeded($out, $adminInfo);
|
||||
self::attachLinkedPayOrders($out);
|
||||
@@ -634,12 +853,29 @@ class PrescriptionOrderLogic
|
||||
|
||||
try {
|
||||
$order->save();
|
||||
|
||||
// 撤回订单后,将关联的处方审核状态改回"待审核"
|
||||
$prescriptionId = (int) $order->prescription_id;
|
||||
if ($prescriptionId > 0) {
|
||||
Prescription::where('id', $prescriptionId)
|
||||
->whereNull('delete_time')
|
||||
->update([
|
||||
'audit_status' => 0, // 0=待审核
|
||||
'audit_time' => null,
|
||||
'audit_by' => null,
|
||||
'audit_by_name' => '',
|
||||
'audit_remark' => '',
|
||||
'update_time' => time(),
|
||||
]);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
self::$error = $e->getMessage();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
self::writeLog((int) $order->id, $adminId, $adminInfo, 'withdraw', '撤回订单,状态变更为「已取消」');
|
||||
|
||||
$out = $order->toArray();
|
||||
self::maskInternalCostIfNeeded($out, $adminInfo);
|
||||
self::attachLinkedPayOrders($out);
|
||||
@@ -704,6 +940,20 @@ class PrescriptionOrderLogic
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($action === 'approve') {
|
||||
$syncRemark = trim($remark) !== '' ? trim($remark) : '业务订单处方审核通过';
|
||||
PrescriptionLogic::syncApproveConsumerPrescriptionIfPending(
|
||||
(int) $order->prescription_id,
|
||||
$adminId,
|
||||
$adminInfo,
|
||||
$syncRemark
|
||||
);
|
||||
}
|
||||
|
||||
$actionName = $action === 'approve' ? 'audit_rx_approve' : 'audit_rx_reject';
|
||||
$summary = $action === 'approve' ? '处方审核通过' . ($remark ? ',意见:' . $remark : '') : '处方审核驳回,意见:' . $remark;
|
||||
self::writeLog((int) $order->id, $adminId, $adminInfo, $actionName, $summary);
|
||||
|
||||
$out = $order->toArray();
|
||||
self::maskInternalCostIfNeeded($out, $adminInfo);
|
||||
self::attachLinkedPayOrders($out);
|
||||
@@ -744,6 +994,7 @@ class PrescriptionOrderLogic
|
||||
|
||||
return false;
|
||||
}
|
||||
// 已发货状态(5)允许重新发起支付单审核(新增支付单场景)
|
||||
|
||||
if ($action === 'reject' && trim($remark) === '') {
|
||||
self::$error = '驳回时请填写审核意见';
|
||||
@@ -771,10 +1022,306 @@ class PrescriptionOrderLogic
|
||||
return false;
|
||||
}
|
||||
|
||||
$actionName = $action === 'approve' ? 'audit_pay_approve' : 'audit_pay_reject';
|
||||
$summary = $action === 'approve' ? '支付单审核通过' . ($remark ? ',意见:' . $remark : '') : '支付单审核驳回,意见:' . $remark;
|
||||
self::writeLog((int) $order->id, $adminId, $adminInfo, $actionName, $summary);
|
||||
|
||||
$out = $order->toArray();
|
||||
self::maskInternalCostIfNeeded($out, $adminInfo);
|
||||
self::attachLinkedPayOrders($out);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回处方审核:将已通过/已驳回的处方审核重置为待审核
|
||||
*
|
||||
* @return array<string,mixed>|false
|
||||
*/
|
||||
public static function revokeRxAudit(int $id, int $adminId, array $adminInfo)
|
||||
{
|
||||
self::$error = '';
|
||||
$order = PrescriptionOrder::where('id', $id)->whereNull('delete_time')->find();
|
||||
if (!$order) {
|
||||
self::$error = '订单不存在';
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!self::canAuditPrescriptionOrder($adminInfo)) {
|
||||
self::$error = '无处方审核权限';
|
||||
|
||||
return false;
|
||||
}
|
||||
$rxStatus = (int) $order->prescription_audit_status;
|
||||
if ($rxStatus === 0) {
|
||||
self::$error = '当前已是待审核状态,无需撤回';
|
||||
|
||||
return false;
|
||||
}
|
||||
$fs = (int) $order->fulfillment_status;
|
||||
if ($fs === 3 || $fs === 4) {
|
||||
self::$error = '订单已结束,不可撤回审核';
|
||||
|
||||
return false;
|
||||
}
|
||||
// 如果处方审核已通过,且支付审核也已通过或已驳回,不允许撤回处方审核
|
||||
if ($rxStatus === 1 && (int) $order->payment_slip_audit_status !== 0) {
|
||||
self::$error = '支付单审核已进行,请先撤回支付单审核';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$order->prescription_audit_status = 0;
|
||||
$order->prescription_audit_remark = '';
|
||||
// 如果支付审核不是待审核,也一并重置
|
||||
if ((int) $order->payment_slip_audit_status !== 0) {
|
||||
$order->payment_slip_audit_status = 0;
|
||||
$order->payment_slip_audit_remark = '';
|
||||
}
|
||||
self::syncFulfillmentStatus($order);
|
||||
|
||||
try {
|
||||
$order->save();
|
||||
} catch (\Throwable $e) {
|
||||
self::$error = $e->getMessage();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
self::writeLog((int) $order->id, $adminId, $adminInfo, 'revoke_rx_audit', '撤回处方审核,状态重置为待审核');
|
||||
|
||||
$out = $order->toArray();
|
||||
self::maskInternalCostIfNeeded($out, $adminInfo);
|
||||
self::attachLinkedPayOrders($out);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回支付单审核:将已通过/已驳回的支付单审核重置为待审核
|
||||
*
|
||||
* @return array<string,mixed>|false
|
||||
*/
|
||||
public static function revokePayAudit(int $id, int $adminId, array $adminInfo)
|
||||
{
|
||||
self::$error = '';
|
||||
$order = PrescriptionOrder::where('id', $id)->whereNull('delete_time')->find();
|
||||
if (!$order) {
|
||||
self::$error = '订单不存在';
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!self::canAuditPaymentSlipOrder($adminInfo)) {
|
||||
self::$error = '无支付单审核权限';
|
||||
|
||||
return false;
|
||||
}
|
||||
if ((int) $order->prescription_audit_status !== 1) {
|
||||
self::$error = '处方审核未通过,无法撤回支付单审核';
|
||||
|
||||
return false;
|
||||
}
|
||||
$payStatus = (int) $order->payment_slip_audit_status;
|
||||
if ($payStatus === 0) {
|
||||
self::$error = '当前已是待审核状态,无需撤回';
|
||||
|
||||
return false;
|
||||
}
|
||||
$fs = (int) $order->fulfillment_status;
|
||||
if ($fs === 3 || $fs === 4) {
|
||||
self::$error = '订单已结束,不可撤回审核';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$order->payment_slip_audit_status = 0;
|
||||
$order->payment_slip_audit_remark = '';
|
||||
self::syncFulfillmentStatus($order);
|
||||
|
||||
try {
|
||||
$order->save();
|
||||
} catch (\Throwable $e) {
|
||||
self::$error = $e->getMessage();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
self::writeLog((int) $order->id, $adminId, $adminInfo, 'revoke_pay_audit', '撤回支付单审核,状态重置为待审核');
|
||||
|
||||
$out = $order->toArray();
|
||||
self::maskInternalCostIfNeeded($out, $adminInfo);
|
||||
self::attachLinkedPayOrders($out);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public static function getLogs(int $orderId, int $adminId, array $adminInfo): array
|
||||
{
|
||||
self::$error = '';
|
||||
$order = PrescriptionOrder::where('id', $orderId)->whereNull('delete_time')->find();
|
||||
if (!$order) {
|
||||
self::$error = '订单不存在';
|
||||
|
||||
return [];
|
||||
}
|
||||
if (!self::canAccessOrder($order, $adminId, $adminInfo)) {
|
||||
self::$error = '无权限查看';
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
return PrescriptionOrderLog::where('prescription_order_id', $orderId)
|
||||
->order('id', 'desc')
|
||||
->select()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 为「已发货」(fulfillment_status=5) 的业务订单新增一条关联支付单(zyt_order),
|
||||
* 创建后将支付单链接到业务订单,并将处方/支付审核状态重置为待审核以启动再次审核流程。
|
||||
*
|
||||
* @param array<string,mixed> $params id, order_type, amount, remark
|
||||
* @return array<string,mixed>|false
|
||||
*/
|
||||
public static function addPayOrder(array $params, int $adminId, array $adminInfo)
|
||||
{
|
||||
self::$error = '';
|
||||
$id = (int) $params['id'];
|
||||
$order = PrescriptionOrder::where('id', $id)->whereNull('delete_time')->find();
|
||||
if (!$order) {
|
||||
self::$error = '订单不存在';
|
||||
return false;
|
||||
}
|
||||
if (!self::canAccessOrder($order, $adminId, $adminInfo)) {
|
||||
self::$error = '无权限操作';
|
||||
return false;
|
||||
}
|
||||
if ((int) $order->fulfillment_status !== 5) {
|
||||
self::$error = '仅「已发货」状态的订单可新增支付单';
|
||||
return false;
|
||||
}
|
||||
|
||||
$orderType = (int) ($params['order_type'] ?? 3);
|
||||
$amount = round((float) ($params['pay_amount'] ?? 0), 2);
|
||||
$remark = mb_substr(trim((string) ($params['pay_remark'] ?? '')), 0, 200);
|
||||
|
||||
if ($amount <= 0) {
|
||||
self::$error = '支付单金额须大于 0';
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建 zyt_order 并标记为已支付(后台直接确认)
|
||||
$payOrder = new Order();
|
||||
$payOrder->order_no = OrderLogic::generateOrderNo();
|
||||
$payOrder->patient_id = (int) $order->diagnosis_id;
|
||||
$payOrder->creator_id = $adminId;
|
||||
$payOrder->order_type = $orderType;
|
||||
$payOrder->amount = $amount;
|
||||
$payOrder->status = 2; // 已支付
|
||||
$payOrder->payment_method = 'manual';
|
||||
$payOrder->payment_time = date('Y-m-d H:i:s');
|
||||
$payOrder->remark = $remark;
|
||||
|
||||
try {
|
||||
$payOrder->save();
|
||||
} catch (\Throwable $e) {
|
||||
self::$error = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 将新支付单追加到业务订单关联列表
|
||||
$existingIds = self::linkedPayOrderIdList($id);
|
||||
$newIds = array_unique(array_merge($existingIds, [(int) $payOrder->id]));
|
||||
self::replacePayOrderLinks($id, $newIds);
|
||||
$order->linked_pay_order_id = $newIds[0];
|
||||
|
||||
// 重置支付单审核为待审核,以启动新一轮审核(处方审核保持通过状态不变)
|
||||
$order->payment_slip_audit_status = 0;
|
||||
$order->payment_slip_audit_remark = '';
|
||||
|
||||
try {
|
||||
$order->save();
|
||||
} catch (\Throwable $e) {
|
||||
self::$error = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
|
||||
self::writeLog($id, $adminId, $adminInfo, 'add_pay_order',
|
||||
'新增支付单 #' . $payOrder->id . '(¥' . $amount . '),类型:' . $orderType . ',等待支付审核');
|
||||
|
||||
$out = $order->toArray();
|
||||
self::maskInternalCostIfNeeded($out, $adminInfo);
|
||||
self::attachLinkedPayOrders($out);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将「已发货」(fulfillment_status=5) 且支付审核已通过的订单标记为「已完成」(3)。
|
||||
*
|
||||
* @return array<string,mixed>|false
|
||||
*/
|
||||
public static function complete(int $id, int $adminId, array $adminInfo)
|
||||
{
|
||||
self::$error = '';
|
||||
$order = PrescriptionOrder::where('id', $id)->whereNull('delete_time')->find();
|
||||
if (!$order) {
|
||||
self::$error = '订单不存在';
|
||||
return false;
|
||||
}
|
||||
if (!self::canAccessOrder($order, $adminId, $adminInfo)) {
|
||||
self::$error = '无权限操作';
|
||||
return false;
|
||||
}
|
||||
if ((int) $order->fulfillment_status !== 5) {
|
||||
self::$error = '仅「已发货」状态可标记为已完成';
|
||||
return false;
|
||||
}
|
||||
if ((int) $order->payment_slip_audit_status !== 1) {
|
||||
self::$error = '支付单审核尚未通过,不可完成订单';
|
||||
return false;
|
||||
}
|
||||
|
||||
$order->fulfillment_status = 3;
|
||||
|
||||
try {
|
||||
$order->save();
|
||||
} catch (\Throwable $e) {
|
||||
self::$error = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
|
||||
self::writeLog($id, $adminId, $adminInfo, 'complete', '订单完成,状态变更为「已完成」');
|
||||
|
||||
$out = $order->toArray();
|
||||
self::maskInternalCostIfNeeded($out, $adminInfo);
|
||||
self::attachLinkedPayOrders($out);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
private static function writeLog(int $orderId, int $adminId, array $adminInfo, string $action, string $summary): void
|
||||
{
|
||||
$adminName = $adminInfo['name'] ?? '';
|
||||
if ($adminName === '' && $adminId > 0) {
|
||||
$adminName = (string) \app\common\model\auth\Admin::where('id', $adminId)->value('name');
|
||||
}
|
||||
|
||||
$log = new PrescriptionOrderLog();
|
||||
$log->prescription_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) {
|
||||
// 忽略日志写入错误
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,24 +9,27 @@ use app\common\validate\BaseValidate;
|
||||
class PrescriptionOrderValidate extends BaseValidate
|
||||
{
|
||||
protected $rule = [
|
||||
'id' => 'require|integer',
|
||||
'diagnosis_id' => 'require|integer|gt:0',
|
||||
'prescription_id' => 'require|integer',
|
||||
'pay_order_ids' => 'array',
|
||||
'recipient_name' => 'require|max:50',
|
||||
'recipient_phone' => 'require|max:20',
|
||||
'id' => 'require|integer',
|
||||
'diagnosis_id' => 'require|integer|gt:0',
|
||||
'prescription_id' => 'require|integer',
|
||||
'pay_order_ids' => 'array',
|
||||
'recipient_name' => 'require|max:50',
|
||||
'recipient_phone' => 'require|max:20',
|
||||
'shipping_address' => 'require|max:500',
|
||||
'is_follow_up' => 'in:0,1',
|
||||
'prev_staff' => 'max:100',
|
||||
'service_channel' => 'max:100',
|
||||
'service_package' => 'max:100',
|
||||
'tracking_number' => 'max:80',
|
||||
'express_company' => 'max:20',
|
||||
'fee_type' => 'require|in:1,2,3,4,5,6,7',
|
||||
'amount' => 'require|float',
|
||||
'remark_extra' => 'max:500',
|
||||
'action' => 'require|in:approve,reject',
|
||||
'remark' => 'max:500',
|
||||
'is_follow_up' => 'in:0,1',
|
||||
'prev_staff' => 'max:100',
|
||||
'service_channel' => 'max:100',
|
||||
'service_package' => 'max:100',
|
||||
'tracking_number' => 'max:80',
|
||||
'express_company' => 'max:20',
|
||||
'fee_type' => 'require|in:1,2,3,4,5,6,7',
|
||||
'amount' => 'require|float',
|
||||
'order_type' => 'require|in:1,2,3,4,5,6,7',
|
||||
'pay_amount' => 'require|float|gt:0',
|
||||
'pay_remark' => 'max:200',
|
||||
'remark_extra' => 'max:500',
|
||||
'action' => 'require|in:approve,reject',
|
||||
'remark' => 'max:500',
|
||||
];
|
||||
|
||||
protected $message = [
|
||||
@@ -56,6 +59,10 @@ class PrescriptionOrderValidate extends BaseValidate
|
||||
'auditPrescription' => ['id', 'action', 'remark'],
|
||||
'auditPayment' => ['id', 'action', 'remark'],
|
||||
'withdraw' => ['id'],
|
||||
'ship' => ['id', 'tracking_number', 'express_company'],
|
||||
'logs' => ['id'],
|
||||
'paidPayOrders' => ['diagnosis_id'],
|
||||
'addPayOrder' => ['id', 'order_type', 'pay_amount', 'pay_remark'],
|
||||
'complete' => ['id'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -3,13 +3,40 @@
|
||||
namespace app\api\controller;
|
||||
|
||||
use app\api\logic\ChatNotifyLogic;
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
* 聊天相关接口(C端-患者)
|
||||
*/
|
||||
class ChatController extends BaseApiController
|
||||
{
|
||||
public array $notNeedLogin = ['notifyOpen'];
|
||||
public array $notNeedLogin = ['notifyOpen', 'logAvPermission'];
|
||||
|
||||
/**
|
||||
* 上报音视频权限拒绝日志
|
||||
* 患者小程序请求摄像头/麦克风权限被拒绝,且用户点击「取消」未前往设置页时调用
|
||||
*/
|
||||
public function logAvPermission()
|
||||
{
|
||||
$patientId = trim((string) ($this->request->post('patient_id', '')));
|
||||
$doctorId = trim((string) ($this->request->post('doctor_id', '')));
|
||||
$deniedScope = trim((string) ($this->request->post('denied_scope', '')));
|
||||
$scene = trim((string) ($this->request->post('scene', '')));
|
||||
$action = trim((string) ($this->request->post('action', 'cancel')));
|
||||
$wxVersion = trim((string) ($this->request->post('wx_version', '')));
|
||||
|
||||
Db::name('av_permission_log')->insert([
|
||||
'patient_id' => $patientId,
|
||||
'doctor_id' => $doctorId,
|
||||
'denied_scope' => $deniedScope ?: 'unknown',
|
||||
'scene' => $scene ?: 'unknown',
|
||||
'action' => in_array($action, ['cancel', 'open_setting']) ? $action : 'cancel',
|
||||
'wx_version' => $wxVersion,
|
||||
'create_time' => time(),
|
||||
]);
|
||||
|
||||
return $this->success('已记录');
|
||||
}
|
||||
|
||||
/**
|
||||
* 患者打开会话时通知医生
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\common\model\tcm;
|
||||
|
||||
use app\common\model\BaseModel;
|
||||
|
||||
/**
|
||||
* 处方业务订单操作日志
|
||||
*/
|
||||
class PrescriptionOrderLog extends BaseModel
|
||||
{
|
||||
protected $name = 'tcm_prescription_order_log';
|
||||
|
||||
protected $autoWriteTimestamp = false; // 时间戳由代码里显式写
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 处方业务订单(履约单)
|
||||
*
|
||||
* .env 示例:PRESCRIPTION_ORDER_LINK_PAY_MIN_AMOUNT = 100
|
||||
* 为 0 或未配置时不校验。
|
||||
*/
|
||||
return [
|
||||
// 定金门槛(元):关联的每笔已支付单金额须 **大于等于** 此值;业务订单金额须 **大于等于** 此值
|
||||
'link_pay_min_amount' => (float) env('PRESCRIPTION_ORDER_LINK_PAY_MIN_AMOUNT', 0),
|
||||
];
|
||||
@@ -102,20 +102,20 @@ return [
|
||||
'tabbar_style' => ['default_color' => '#999999', 'selected_color' => '#c455ff'],
|
||||
],
|
||||
|
||||
// 订单编辑权限:拥有以下角色ID的用户可修改任意订单,其他用户只能修改自己创建的订单
|
||||
'order_edit_all_roles' => [0, 3],
|
||||
// 医助创建订单权限:拥有以下角色ID的用户可修改任意订单,其他用户只能修改自己创建的订单
|
||||
'order_edit_all_roles' => [0, 3, 6],
|
||||
|
||||
// 处方库:以下角色ID + 超级管理员(root) 可查看/编辑/删除全部;他人仅可管理自己创建的,公开处方对他人只读
|
||||
'prescription_library_manage_all_roles' => [0, 3],
|
||||
|
||||
// 消费者处方单:以下角色 + root 可对「待审核」处方进行通过/驳回(驳回即作废处方)
|
||||
'prescription_audit_roles' => [0, 3,6],
|
||||
'prescription_audit_roles' => [0, 3, 6],
|
||||
|
||||
// 处方业务订单 internal_cost 等财务字段:以下角色 ID + root 可见
|
||||
'prescription_order_finance_roles' => [0, 3],
|
||||
'prescription_order_finance_roles' => [0, 3, 6],
|
||||
|
||||
// 业务订单「关联支付单」审核:以下角色 ID + root 可操作(可与财务角色一致)
|
||||
'prescription_order_payment_audit_roles' => [0, 3],
|
||||
'prescription_order_payment_audit_roles' => [0, 3, 6],
|
||||
|
||||
// 腾讯云实时音视频(TRTC)配置
|
||||
'trtc' => [
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
-- 音视频权限拒绝日志表
|
||||
CREATE TABLE IF NOT EXISTS `zyt_av_permission_log` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`patient_id` varchar(64) NOT NULL DEFAULT '' COMMENT '患者ID(patient_xxx格式)',
|
||||
`doctor_id` varchar(64) NOT NULL DEFAULT '' COMMENT '医生ID(doctor_xxx格式)',
|
||||
`denied_scope` varchar(32) NOT NULL DEFAULT '' COMMENT '被拒绝的权限 scope(camera/record)',
|
||||
`scene` varchar(32) NOT NULL DEFAULT '' COMMENT '触发场景(makeCall / incoming)',
|
||||
`action` varchar(16) NOT NULL DEFAULT 'cancel' COMMENT '用户行为:cancel=取消 / open_setting=去设置',
|
||||
`wx_version` varchar(32) NOT NULL DEFAULT '' COMMENT '微信基础库版本',
|
||||
`create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间(Unix秒)',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_patient` (`patient_id`),
|
||||
KEY `idx_doctor` (`doctor_id`),
|
||||
KEY `idx_create_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='音视频权限拒绝行为日志';
|
||||
@@ -0,0 +1,13 @@
|
||||
-- 处方业务订单详情:查看处方药材明细(无此权限时接口不返回 herbs)
|
||||
SET @po_menu_id := (SELECT id FROM zyt_system_menu WHERE perms = 'tcm.prescriptionOrder/lists' LIMIT 1);
|
||||
|
||||
INSERT INTO zyt_system_menu (
|
||||
pid, type, name, icon, sort, perms, paths, component,
|
||||
selected, params, is_cache, is_show, is_disable, create_time, update_time
|
||||
)
|
||||
SELECT
|
||||
@po_menu_id, 'A', '药材明细', '', 9, 'tcm.prescriptionOrder/viewRxHerbs', '', '',
|
||||
'', '', 0, 1, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()
|
||||
FROM DUAL
|
||||
WHERE @po_menu_id IS NOT NULL
|
||||
AND NOT EXISTS (SELECT 1 FROM zyt_system_menu WHERE perms = 'tcm.prescriptionOrder/viewRxHerbs');
|
||||
Binary file not shown.
Reference in New Issue
Block a user