Files
zyt/server/app/adminapi/lists/firstvisit/MyPatientOrderLists.php
T
2026-08-06 10:57:35 +08:00

347 lines
14 KiB
PHP

<?php
declare(strict_types=1);
namespace app\adminapi\lists\firstvisit;
use app\adminapi\lists\BaseAdminDataLists;
use app\adminapi\logic\firstvisit\MyPatientLogic;
use app\adminapi\logic\stats\YejiStatsLogic;
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\auth\Admin;
use app\common\model\tcm\Prescription;
use app\common\model\tcm\PrescriptionOrder;
use app\common\model\tcm\PrescriptionOrderPayOrder;
use think\db\Query;
use think\facade\Db;
/**
* “我的患者”内嵌订单列表。
*
* 订单可见性始终锚定 diagnosis 别名 d,并复用 MyPatientLogic;订单创建人仅用于展示,
* 不能作为患者归属或数据范围条件。
*/
class MyPatientOrderLists extends BaseAdminDataLists implements ListsSearchInterface, ListsExtendInterface
{
public function setSearch(): array
{
return [];
}
public function lists(): array
{
$rows = $this->buildQuery()
->field([
'po.id', 'po.order_no', 'po.prescription_id', 'po.diagnosis_id', 'po.creator_id',
'po.recipient_name', 'po.recipient_phone', 'po.fee_type', 'po.amount',
'po.prescription_audit_status', 'po.payment_slip_audit_status',
'po.fulfillment_status', 'po.express_company', 'po.tracking_number', 'po.ship_mode',
'po.gancao_reciperl_order_no', 'po.ej_pharmacy_order_no',
'po.gancao_submit_time', 'po.ej_pharmacy_submit_time',
'po.ej_pharmacy_status', 'po.ej_pharmacy_review_status', 'po.refund_amount',
'po.create_time',
'd.patient_name', 'd.phone AS patient_phone', 'd.assistant_id',
])
->order('po.id', 'desc')
->limit($this->limitOffset, $this->limitLength)
->select()
->toArray();
return $this->appendRelations($rows);
}
public function count(): int
{
return (int) $this->buildQuery()->count('po.id');
}
public function extend(): array
{
$query = $this->buildQuery();
$pendingQuery = clone $query;
$effectiveAmountQuery = clone $query;
YejiStatsLogic::applyPrescriptionOrderEffectiveAmountQuery($effectiveAmountQuery, 'po');
return [
'summary' => [
'orders' => (int) (clone $query)->count('po.id'),
'amount' => round((float) $effectiveAmountQuery->sum('po.amount'), 2),
'pending' => (int) $pendingQuery
->where(function ($q) {
$q->where('po.prescription_audit_status', 0)
->whereOr('po.payment_slip_audit_status', 0);
})
->count('po.id'),
'completed' => (int) (clone $query)->whereIn('po.fulfillment_status', [3, 6])->count('po.id'),
],
'scope' => MyPatientLogic::scopeMeta($this->adminId, $this->adminInfo),
];
}
private function buildQuery(): Query
{
$query = PrescriptionOrder::alias('po')
->join('tcm_diagnosis d', 'po.diagnosis_id = d.id')
->whereNull('po.delete_time')
->whereNull('d.delete_time')
->where('d.status', 1);
MyPatientLogic::applyScope($query, $this->adminId, $this->adminInfo);
$this->applyKeyword($query);
$this->applyStatusFilters($query);
$this->applyDateFilter($query);
return $query;
}
private function applyKeyword(Query $query): void
{
$keyword = trim((string) ($this->params['keyword'] ?? ''));
if ($keyword === '') {
return;
}
$query->where(function ($q) use ($keyword) {
$like = '%' . $keyword . '%';
$q->whereLike('po.order_no', $like)
->whereOr('d.patient_name', 'like', $like)
->whereOr('d.phone', 'like', $like)
->whereOr('po.recipient_name', 'like', $like)
->whereOr('po.recipient_phone', 'like', $like);
if (preg_match('/^\d+$/', $keyword)) {
$id = (int) $keyword;
if ($id > 0) {
$q->whereOr('po.id', $id)
->whereOr('po.prescription_id', $id)
->whereOr('po.diagnosis_id', $id);
}
}
});
}
private function applyStatusFilters(Query $query): void
{
foreach (['prescription_audit_status', 'payment_slip_audit_status', 'fulfillment_status'] as $field) {
$raw = $this->params[$field] ?? '';
if ($raw === '' || $raw === null) {
continue;
}
$query->where('po.' . $field, (int) $raw);
}
}
private function applyDateFilter(Query $query): void
{
[$startDate, $endDate] = $this->dateRange();
if ($startDate !== '') {
$query->where('po.create_time', '>=', strtotime($startDate . ' 00:00:00'));
}
if ($endDate !== '') {
$query->where('po.create_time', '<=', strtotime($endDate . ' 23:59:59'));
}
}
/** @return array{0:string,1:string} */
private function dateRange(): array
{
$startDate = $this->normalizeDate($this->params['start_date'] ?? '');
$endDate = $this->normalizeDate($this->params['end_date'] ?? '');
if ($startDate === '' && $endDate !== '') {
$startDate = $endDate;
}
if ($endDate === '' && $startDate !== '') {
$endDate = $startDate;
}
if ($startDate !== '' && $endDate !== '' && $startDate > $endDate) {
[$startDate, $endDate] = [$endDate, $startDate];
}
return [$startDate, $endDate];
}
private function normalizeDate($value): string
{
$value = trim((string) $value);
return preg_match('/^\d{4}-\d{2}-\d{2}$/', $value) ? $value : '';
}
/**
* @param array<int, array<string, mixed>> $rows
* @return array<int, array<string, mixed>>
*/
private function appendRelations(array $rows): array
{
if ($rows === []) {
return [];
}
$orderIds = array_values(array_unique(array_filter(array_map('intval', array_column($rows, 'id')))));
$prescriptionIds = array_values(array_unique(array_filter(array_map('intval', array_column($rows, 'prescription_id')))));
$creatorIds = array_values(array_unique(array_filter(array_map('intval', array_column($rows, 'creator_id')))));
$assistantIds = array_values(array_unique(array_filter(array_map('intval', array_column($rows, 'assistant_id')))));
$prescriptionMap = [];
$doctorIds = [];
if ($prescriptionIds !== []) {
$prescriptions = Prescription::whereIn('id', $prescriptionIds)
->whereNull('delete_time')
->field(['id', 'creator_id', 'doctor_name'])
->select()
->toArray();
foreach ($prescriptions as $prescription) {
$prescriptionId = (int) ($prescription['id'] ?? 0);
$doctorId = (int) ($prescription['creator_id'] ?? 0);
if ($prescriptionId > 0) {
$prescriptionMap[$prescriptionId] = $prescription;
}
if ($doctorId > 0) {
$doctorIds[] = $doctorId;
}
}
}
$adminIds = array_values(array_unique(array_merge($creatorIds, $assistantIds, $doctorIds)));
$adminNames = $adminIds === []
? []
: Admin::whereIn('id', $adminIds)->whereNull('delete_time')->column('name', 'id');
$linkCounts = [];
$paidTotals = [];
if ($orderIds !== []) {
$linkRows = PrescriptionOrderPayOrder::whereIn('prescription_order_id', $orderIds)
->field(['prescription_order_id', 'pay_order_id'])
->select()
->toArray();
$payOrderIds = array_values(array_unique(array_filter(array_map('intval', array_column($linkRows, 'pay_order_id')))));
$payOrders = $payOrderIds === []
? []
: Order::whereIn('id', $payOrderIds)
->whereNull('delete_time')
->field(['id', 'amount', 'status'])
->select()
->toArray();
$payOrderMap = [];
foreach ($payOrders as $payOrder) {
$payOrderMap[(int) ($payOrder['id'] ?? 0)] = $payOrder;
}
foreach ($linkRows as $linkRow) {
$orderId = (int) ($linkRow['prescription_order_id'] ?? 0);
if ($orderId > 0) {
$linkCounts[$orderId] = ($linkCounts[$orderId] ?? 0) + 1;
}
$payOrder = $payOrderMap[(int) ($linkRow['pay_order_id'] ?? 0)] ?? [];
if ($orderId > 0 && in_array((int) ($payOrder['status'] ?? 0), [2, 5], true)) {
$paidTotals[$orderId] = round(
(float) ($paidTotals[$orderId] ?? 0) + (float) ($payOrder['amount'] ?? 0),
2
);
}
}
}
$assistantByDiagnosis = [];
foreach ($rows as $row) {
$diagnosisId = (int) ($row['diagnosis_id'] ?? 0);
if ($diagnosisId > 0) {
$assistantByDiagnosis[$diagnosisId] = (int) ($row['assistant_id'] ?? 0);
}
}
$claimByOrder = [];
if ($orderIds !== []) {
$claimRows = Db::name('pharmacy_submission_claim')
->whereIn('prescription_order_id', $orderIds)
->field(['prescription_order_id', 'target', 'status', 'lease_expires_at'])
->order('source_revision', 'desc')
->select()
->toArray();
foreach ($claimRows as $claimRow) {
$orderId = (int) ($claimRow['prescription_order_id'] ?? 0);
if ($orderId > 0 && !isset($claimByOrder[$orderId])) {
$claimByOrder[$orderId] = $claimRow;
}
}
}
foreach ($rows as &$row) {
$prescription = $prescriptionMap[(int) ($row['prescription_id'] ?? 0)] ?? [];
$doctorId = (int) ($prescription['creator_id'] ?? 0);
$doctorName = trim((string) ($prescription['doctor_name'] ?? ''));
$creatorId = (int) ($row['creator_id'] ?? 0);
$assistantId = (int) ($row['assistant_id'] ?? 0);
$row['patient_phone_masked'] = $this->maskPhone((string) ($row['patient_phone'] ?? ''));
$row['recipient_phone_masked'] = $this->maskPhone((string) ($row['recipient_phone'] ?? ''));
unset($row['patient_phone'], $row['recipient_phone']);
$row['creator_name'] = (string) ($adminNames[$creatorId] ?? '—');
$row['assistant_name'] = (string) ($adminNames[$assistantId] ?? '未分配');
$row['doctor_name'] = $doctorName !== '' ? $doctorName : (string) ($adminNames[$doctorId] ?? '—');
$row['linked_pay_order_count'] = (int) ($linkCounts[(int) $row['id']] ?? 0);
$row['linked_pay_paid_total'] = (float) ($paidTotals[(int) $row['id']] ?? 0);
$claim = $claimByOrder[(int) $row['id']] ?? [];
$row['pharmacy_claim_target'] = (string) ($claim['target'] ?? '');
$row['pharmacy_claim_status'] = (string) ($claim['status'] ?? '');
$row['pharmacy_claim_lease_expires_at'] = (int) ($claim['lease_expires_at'] ?? 0);
$row['can_upload_pharmacy'] = PrescriptionOrderLogic::canUploadToPharmacy(
$row,
$this->adminId,
$this->adminInfo,
$assistantByDiagnosis
);
$row['create_time_text'] = $this->formatTimestamp($row['create_time'] ?? 0);
$row['fee_type_text'] = $this->feeTypeText((int) ($row['fee_type'] ?? 0));
$row['prescription_audit_text'] = $this->auditStatusText((int) ($row['prescription_audit_status'] ?? 0));
$row['payment_slip_audit_text'] = $this->auditStatusText((int) ($row['payment_slip_audit_status'] ?? 0));
$row['fulfillment_text'] = $this->fulfillmentStatusText((int) ($row['fulfillment_status'] ?? 0));
$fulfillmentStatus = (int) ($row['fulfillment_status'] ?? 0);
$refundAmount = round((float) ($row['refund_amount'] ?? 0), 2);
$amountIncluded = !in_array(
$fulfillmentStatus,
YejiStatsLogic::PRESCRIPTION_ORDER_FULFILLMENT_EXCLUDED_FROM_PERFORMANCE,
true
) && $refundAmount <= 0;
$row['amount_included'] = $amountIncluded;
$row['effective_amount'] = $amountIncluded ? round((float) ($row['amount'] ?? 0), 2) : 0.0;
$row['amount_exclusion_text'] = $amountIncluded
? ''
: ($refundAmount > 0 || $fulfillmentStatus === 10 ? '退款不计入' : $row['fulfillment_text'] . '不计入');
}
unset($row);
return $rows;
}
private function maskPhone(string $phone): string
{
return preg_replace('/^(\d{3})\d{4}(\d{4})$/', '$1****$2', $phone) ?: $phone;
}
private function formatTimestamp($value): string
{
return is_numeric($value) && (int) $value > 0 ? date('Y-m-d H:i', (int) $value) : '';
}
private function auditStatusText(int $status): string
{
return [0 => '待审核', 1 => '已通过', 2 => '已驳回'][$status] ?? '未知';
}
private function feeTypeText(int $type): string
{
return [1 => '挂号', 2 => '问诊', 3 => '药品', 4 => '首付', 5 => '尾款', 6 => '其他', 7 => '全部'][$type] ?? '其他';
}
private function fulfillmentStatusText(int $status): string
{
return [
1 => '待双审通过', 2 => '待发货', 3 => '已完成', 4 => '已取消',
5 => '已发货', 6 => '已签收', 7 => '进行中', 8 => '暂不制药',
9 => '拒收', 10 => '退款', 11 => '保留药方', 12 => '制药缓发',
][$status] ?? '未知';
}
}