first commit

This commit is contained in:
Your Name
2026-09-08 11:40:15 +08:00
commit a5353f7eb5
9568 changed files with 1646214 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,73 @@
<?php
// +----------------------------------------------------------------------
// | likeadmin快速开发前后端分离管理后台(PHP版)
// +----------------------------------------------------------------------
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
// | 开源版本可自由商用,可去除界面版权logo
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
// | github下载:https://github.com/likeshop-github/likeadmin
// | 访问官网:https://www.likeadmin.cn
// | likeadmin团队 版权所有 拥有最终解释权
// +----------------------------------------------------------------------
// | author: likeadminTeam
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\adminapi\lists\tcm;
use app\adminapi\lists\BaseAdminDataLists;
use app\adminapi\logic\tcm\DiagnosisTodoLogic;
use app\common\lists\ListsSearchInterface;
use app\common\model\tcm\DiagnosisTodo;
/**
* 诊单待办事项列表
*
* 默认按 remind_time desc 排序;按 diagnosis_id 过滤。
*
* @package app\adminapi\lists\tcm
*/
class DiagnosisTodoLists extends BaseAdminDataLists implements ListsSearchInterface
{
public function setSearch(): array
{
return [
'=' => ['diagnosis_id', 'status', 'creator_id'],
];
}
public function lists(): array
{
$lists = DiagnosisTodo::where($this->searchWhere)
->field([
'id', 'diagnosis_id', 'patient_id', 'content', 'remind_time',
'status', 'creator_id', 'creator_name', 'notified_at', 'error',
'cancelled_at', 'cancelled_by', 'create_time', 'update_time',
])
->append([
'status_text',
'remind_time_text',
'notified_at_text',
'cancelled_at_text',
])
->order('remind_time', 'desc')
->order('id', 'desc')
->limit($this->limitOffset, $this->limitLength)
->select()
->toArray();
// 业务态:是否能被「我」取消(前端按钮显隐用)
$adminId = (int) $this->adminId;
foreach ($lists as &$item) {
$item['can_cancel'] = DiagnosisTodoLogic::canCancel($item, $adminId);
}
return $lists;
}
public function count(): int
{
return DiagnosisTodo::where($this->searchWhere)->count();
}
}
@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
namespace app\adminapi\lists\tcm;
use app\adminapi\lists\BaseAdminDataLists;
use app\adminapi\logic\tcm\PrescriptionLibraryLogic;
use app\common\model\tcm\PrescriptionLibrary;
use app\common\lists\ListsSearchInterface;
/**
* 处方库列表
*/
class PrescriptionLibraryLists extends BaseAdminDataLists implements ListsSearchInterface
{
/**
* @notes 设置搜索条件
*/
public function setSearch(): array
{
return [
'%like%' => ['prescription_name'],
'=' => ['is_public', 'creator_id', 'formula_type']
];
}
/**
* @notes 列表数据范围:超管/管理员角色看全部;普通账号仅看自己创建 + 公开处方
*/
private function applyDataScope($query)
{
if (PrescriptionLibraryLogic::canManageAllPrescriptions($this->adminId, $this->adminInfo)) {
return $query;
}
// 开方页导入专用参数(勿与搜索项 creator_id 混用,否则无法 OR 出他人公开模板)
$prescribingCreatorId = (int) ($this->params['prescribing_creator_id'] ?? 0);
if ($prescribingCreatorId > 0
&& PrescriptionLibraryLogic::canListLibraryForCreator($this->adminId, $this->adminInfo, $prescribingCreatorId)) {
return $query->where(function ($q) use ($prescribingCreatorId) {
$q->where('creator_id', $prescribingCreatorId)
->whereOr('is_public', 1);
});
}
return $query->where(function ($q) {
$q->where('creator_id', $this->adminId)
->whereOr('is_public', 1);
});
}
/**
* @notes 获取列表
*/
public function lists(): array
{
$field = [
'id', 'prescription_name', 'formula_type', 'herbs', 'is_public', 'disable_edit',
'creator_id', 'creator_name', 'create_time', 'update_time'
];
$query = PrescriptionLibrary::where($this->searchWhere);
$this->applyDataScope($query);
$lists = $query
->field($field)
->limit($this->limitOffset, $this->limitLength)
->order('id', 'desc')
->select()
->toArray();
// 解析药材JSON
foreach ($lists as &$item) {
if (!empty($item['herbs'])) {
$item['herbs'] = json_decode($item['herbs'], true);
} else {
$item['herbs'] = [];
}
}
return $lists;
}
/**
* @notes 获取数量
*/
public function count(): int
{
$query = PrescriptionLibrary::where($this->searchWhere);
$this->applyDataScope($query);
return $query->count();
}
}
@@ -0,0 +1,343 @@
<?php
declare(strict_types=1);
namespace app\adminapi\lists\tcm;
use app\adminapi\lists\BaseAdminDataLists;
use app\common\lists\ListsSearchInterface;
use app\common\lists\Traits\HasDataScopeFilter;
use app\adminapi\logic\tcm\PrescriptionLogic;
use app\common\model\tcm\Prescription;
use app\common\model\tcm\PrescriptionOrder;
/**
* 处方列表
*/
class PrescriptionLists extends BaseAdminDataLists implements ListsSearchInterface
{
use HasDataScopeFilter;
/**
* 识别「有业务订单且处方药材为空白/重复」的处方 ID(用于全局置顶排序)
*
* @param array<int,int|string> $candidateIds
* @return array<int,int>
*/
private function collectRiskPrescriptionIds(array $candidateIds): array
{
$candidateIds = array_values(array_unique(array_filter(array_map('intval', $candidateIds), static function (int $id): bool {
return $id > 0;
})));
if ($candidateIds === []) {
return [];
}
// 仅在「存在有效业务订单」的处方里做风险判定
$orderRxIds = PrescriptionOrder::whereIn('prescription_id', $candidateIds)
->whereNull('delete_time')
->where('fulfillment_status', '<>', 4)
->column('prescription_id');
$orderRxIds = array_values(array_unique(array_filter(array_map('intval', $orderRxIds), static function (int $id): bool {
return $id > 0;
})));
if ($orderRxIds === []) {
return [];
}
$rows = Prescription::whereIn('id', $orderRxIds)
->whereNull('delete_time')
->field(['id', 'herbs'])
->select()
->toArray();
$riskIds = [];
foreach ($rows as $row) {
$rid = (int) ($row['id'] ?? 0);
if ($rid <= 0) {
continue;
}
if ($this->isRiskHerbs($row['herbs'] ?? null)) {
$riskIds[] = $rid;
}
}
return array_values(array_unique($riskIds));
}
/**
* 药材风险判定:空白 or 重复(按名称,忽略空格与大小写)
*/
private function isRiskHerbs($herbsRaw): bool
{
$herbs = [];
if (is_array($herbsRaw)) {
$herbs = $herbsRaw;
} elseif (is_string($herbsRaw) && $herbsRaw !== '') {
$decoded = json_decode($herbsRaw, true);
if (is_array($decoded)) {
$herbs = $decoded;
}
}
$names = [];
foreach ($herbs as $h) {
if (!is_array($h)) {
continue;
}
$name = trim((string) ($h['name'] ?? ''));
if ($name !== '') {
$names[] = $name;
}
}
// 空白药材
if ($names === []) {
return true;
}
// 重复药材
$seen = [];
foreach ($names as $name) {
$key = strtolower(preg_replace('/\s+/', '', $name) ?? '');
if ($key === '') {
continue;
}
if (isset($seen[$key])) {
return true;
}
$seen[$key] = true;
}
return false;
}
/**
* @notes 搜索条件
*/
public function setSearch(): array
{
return [
'%like%' => ['patient_name', 'sn'],
'between_time' => 'create_time',
];
}
/** 创建人(医师账号)多选,参数 creator_ids:数组或逗号分隔 ID */
private function applyCreatorIdsFilter($query): void
{
$raw = $this->params['creator_ids'] ?? null;
if ($raw === null || $raw === '') {
return;
}
$ids = \is_array($raw) ? $raw : explode(',', (string) $raw);
$ids = array_values(array_filter(array_map('intval', $ids), static function (int $id): bool {
return $id > 0;
}));
if ($ids === []) {
return;
}
$query->whereIn('creator_id', $ids);
}
/**
* audit_filterpassed=已通过,not_passed=未通过(待审+驳回),pendingrejected
*/
private function applyAuditFilter($query): void
{
$af = (string) ($this->params['audit_filter'] ?? '');
if ($af === '' || $af === 'all') {
return;
}
switch ($af) {
case 'passed':
$query->where('audit_status', '=', 1);
break;
case 'not_passed':
$query->whereIn('audit_status', [0, 2]);
break;
case 'pending':
$query->where('audit_status', '=', 0);
break;
case 'rejected':
$query->where('audit_status', '=', 2);
break;
default:
break;
}
}
/**
* 来源:system=系统代开(is_system_auto=1),manual=手工(0 或空)
*/
private function applySourceFilter($query): void
{
$sf = (string) ($this->params['source_filter'] ?? '');
if ($sf === '' || $sf === 'all') {
return;
}
if ($sf === 'system') {
$query->where('is_system_auto', 1);
return;
}
if ($sf === 'manual') {
$query->where('is_system_auto', 0);
}
}
private function applyVisibilityScope($query): void
{
$query->where(function ($query) {
// 超级管理员可查看全部
if (!empty($this->adminInfo['root']) && (int) $this->adminInfo['root'] === 1) {
return;
}
// 检查是否属于可查看全部处方的角色
$manageAllRoles = config('project.prescription_library_manage_all_roles', [0, 3]);
$roleIds = array_values(array_unique(array_map('intval', $this->adminInfo['role_id'] ?? [])));
$canSeeAll = false;
foreach ($roleIds as $rid) {
if (in_array($rid, $manageAllRoles, true)) {
$canSeeAll = true;
break;
}
}
// 如果属于可查看全部的角色,不添加任何限制
if ($canSeeAll) {
return;
}
// 其他用户只能查看:共享的、自己创建的、自己是医助的、或指定给自己角色的
$adminId = $this->adminId;
$query->where(function ($q) use ($adminId, $roleIds) {
$q->whereOr('is_shared', '=', 1);
$q->whereOr('creator_id', '=', $adminId);
$q->whereOr('assistant_id', '=', $adminId);
foreach ($roleIds as $rid) {
if ($rid > 0) {
$q->whereOrRaw('FIND_IN_SET(?, `visible_role_ids`)', [(string) $rid]);
}
}
});
});
}
/**
* @notes 获取列表
*/
public function lists(): array
{
$query = Prescription::where($this->searchWhere);
$this->applyAuditFilter($query);
$this->applySourceFilter($query);
$this->applyVisibilityScope($query);
$this->applyCreatorIdsFilter($query);
$this->applyDataScopeByOwnerColumns($query, ['creator_id', 'assistant_id']);
// 全局置顶:有业务订单且处方药材为空白/重复
$candidateIds = (clone $query)->whereNull('delete_time')->column('id');
$riskIds = $this->collectRiskPrescriptionIds(is_array($candidateIds) ? $candidateIds : []);
$listQuery = $query->whereNull('delete_time');
if ($riskIds !== []) {
$riskIdStr = implode(',', array_map('intval', $riskIds));
$listQuery->orderRaw("CASE WHEN id IN ({$riskIdStr}) THEN 0 ELSE 1 END ASC");
}
$lists = $listQuery
->order('id', 'desc')
->limit($this->limitOffset, $this->limitLength)
->select()
->toArray();
$rxIds = array_column($lists, 'id');
$rxIds = array_map('intval', $rxIds);
$rejectedRx = [];
$bizRejectRemark = [];
$hasBizOrderRx = [];
if ($rxIds !== []) {
$bizRejectRows = PrescriptionOrder::whereIn('prescription_id', $rxIds)
->where('prescription_audit_status', 2)
->whereNull('delete_time')
->where('fulfillment_status', '<>', 4)
->field(['prescription_id', 'prescription_audit_remark', 'id'])
->order('id', 'desc')
->select()
->toArray();
foreach ($bizRejectRows as $br) {
$pid = (int) ($br['prescription_id'] ?? 0);
if ($pid <= 0) {
continue;
}
$rejectedRx[$pid] = true;
if (!isset($bizRejectRemark[$pid])) {
$bizRejectRemark[$pid] = (string) ($br['prescription_audit_remark'] ?? '');
}
}
$orderRxIds = array_unique(array_map(
'intval',
PrescriptionOrder::whereIn('prescription_id', $rxIds)
->whereNull('delete_time')
->where('fulfillment_status', '<>', 4)
->column('prescription_id')
));
$hasBizOrderRx = array_fill_keys($orderRxIds, true);
}
// 获取医助姓名
$assistantIds = array_unique(array_filter(array_map('intval', array_column($lists, 'assistant_id'))));
$assistantNames = [];
if (!empty($assistantIds)) {
$assistantNames = \app\common\model\auth\Admin::whereIn('id', $assistantIds)
->column('name', 'id');
}
// 处理字段格式
foreach ($lists as &$item) {
// 设置默认值
$item['usage_time'] = $item['usage_time'] ?? '饭前';
$item['usage_way'] = $item['usage_way'] ?? '温水送服';
$item['usage_notes'] = $item['usage_notes'] ?? '';
$item['usage_days'] = $item['usage_days'] ?? 7;
$item['is_shared'] = $item['is_shared'] ?? 0;
$item['is_system_auto'] = (int) ($item['is_system_auto'] ?? 0);
$item['audit_status'] = (int) ($item['audit_status'] ?? 1);
$item['void_status'] = (int) ($item['void_status'] ?? 0);
$item['visible_role_ids'] = PrescriptionLogic::visibleRoleIdsToArray((string) ($item['visible_role_ids'] ?? ''));
$rid = (int) ($item['id'] ?? 0);
$item['business_prescription_audit_rejected'] = !empty($rejectedRx[$rid]) ? 1 : 0;
$item['business_prescription_audit_remark'] = (string) ($bizRejectRemark[$rid] ?? '');
$item['has_prescription_order'] = !empty($hasBizOrderRx[$rid]) ? 1 : 0;
// 添加医助姓名
$assistantId = (int) ($item['assistant_id'] ?? 0);
$item['assistant_name'] = $assistantId > 0 ? ($assistantNames[$assistantId] ?? '') : '';
// 将 dietary_taboo 从逗号分隔的字符串转换为数组(前端需要数组格式)
if (!empty($item['dietary_taboo']) && is_string($item['dietary_taboo'])) {
$item['dietary_taboo'] = array_filter(explode(',', $item['dietary_taboo']));
} else {
$item['dietary_taboo'] = [];
}
}
return $lists;
}
/**
* @notes 获取数量
*/
public function count(): int
{
$query = Prescription::where($this->searchWhere);
$this->applyAuditFilter($query);
$this->applySourceFilter($query);
$this->applyVisibilityScope($query);
$this->applyCreatorIdsFilter($query);
$this->applyDataScopeByOwnerColumns($query, ['creator_id', 'assistant_id']);
return $query->whereNull('delete_time')->count();
}
}
File diff suppressed because it is too large Load Diff