Merge branch 'master' into yeji-5-11

This commit is contained in:
Your Name
2026-05-12 10:43:04 +08:00
306 changed files with 738 additions and 302 deletions
@@ -557,6 +557,24 @@ class OrderController extends BaseAdminController
}
}
public function setExempt()
{
$id = (int) $this->request->post('id', 0);
$isExempt = (int) $this->request->post('is_exempt', 0);
if ($id <= 0) {
return $this->fail('参数错误');
}
if (!in_array($isExempt, [0, 1], true)) {
return $this->fail('is_exempt 值无效');
}
$result = OrderLogic::setExempt($id, $isExempt);
if (!$result) {
return $this->fail(OrderLogic::getError());
}
$this->logOrderAction($id, 'set_exempt', $isExempt === 1 ? '设置豁免权' : '取消豁免权');
return $this->success($isExempt === 1 ? '已设置豁免权' : '已取消豁免权');
}
private function logOrderAction(int $orderId, string $action, string $summary = ''): void
{
OrderActionLogLogic::record($orderId, (int) $this->adminId, $this->adminInfo, $action, $summary);
@@ -117,6 +117,7 @@ class DiagnosisController extends BaseAdminController
$params = (new DiagnosisValidate())->goCheck('id');
$result = DiagnosisLogic::detail($params);
DiagnosisLogic::markAssignRead((int) ($params['id'] ?? 0), $this->adminId);
return $this->data($result);
}
@@ -135,7 +136,7 @@ class DiagnosisController extends BaseAdminController
if (empty($result)) {
return $this->fail(DiagnosisLogic::getError() ?: '诊单不存在或无权访问');
}
DiagnosisLogic::markAssignRead((int) ($params['id'] ?? 0), $this->adminId);
return $this->data($result);
}
@@ -177,14 +177,14 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
$isCompletedTab = !$pendingWideSearch && isset($this->params['completed_appointment']) && (string) $this->params['completed_appointment'] === '1';
if ($isCompletedTab) {
$maxCompletedAptExpr = '(SELECT MAX(CONCAT(apt.appointment_date, \' \', IFNULL(NULLIF(TRIM(apt.appointment_time), \'\'), \'00:00:00\'))) FROM ' . $aptTbl . ' apt WHERE apt.patient_id = ' . $diagTbl . '.id AND apt.status = 3' . $minAptDateCond . ')';
$orderRaw = 'IFNULL(' . $maxCompletedAptExpr . ", '1970-01-01 00:00:00') DESC, {$diagTbl}.id DESC";
$orderRaw = $diagTbl . '.assign_read_at IS NULL DESC, IFNULL(' . $maxCompletedAptExpr . ", '1970-01-01 00:00:00') DESC, {$diagTbl}.id DESC";
} else {
$orderRaw = 'CASE IFNULL(' . $minAptStatusExpr . ', 999) WHEN 4 THEN 1 WHEN 1 THEN 2 WHEN 3 THEN 3 ELSE 4 END ASC, IFNULL(' . $minAptExpr . ", '9999-12-31 23:59:59') ASC, {$diagTbl}.id DESC";
$orderRaw = $diagTbl . '.assign_read_at IS NULL DESC, CASE IFNULL(' . $minAptStatusExpr . ', 999) WHEN 4 THEN 1 WHEN 1 THEN 2 WHEN 3 THEN 3 ELSE 4 END ASC, IFNULL(' . $minAptExpr . ", '9999-12-31 23:59:59') ASC, {$diagTbl}.id DESC";
}
$lists = $query
->with(['DiagnosisViewRecord'])
->field(['id', 'patient_id', 'patient_name', 'id_card', 'phone', 'gender', 'age', 'diagnosis_date', 'diagnosis_type', 'syndrome_type', 'assistant_id', 'status', 'create_time', 'update_time'])
->field(['id', 'patient_id', 'patient_name', 'id_card', 'phone', 'gender', 'age', 'diagnosis_date', 'diagnosis_type', 'syndrome_type', 'assistant_id', 'assign_read_at', 'status', 'create_time', 'update_time'])
->append(['gender_desc', 'status_desc', 'diagnosis_date_text'])
->orderRaw($orderRaw)
->limit($this->limitOffset, $this->limitLength)
@@ -906,6 +906,24 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
return;
}
$yejiTableRowDeptIds = [];
if (isset($this->params['yeji_table_row_dept_ids']) && trim((string) $this->params['yeji_table_row_dept_ids']) !== '') {
foreach (explode(',', (string) $this->params['yeji_table_row_dept_ids']) as $p) {
$v = (int) trim((string) $p);
if ($v > 0) {
$yejiTableRowDeptIds[] = $v;
}
}
}
if ((int) ($this->params['yeji_order_drawer'] ?? 0) === 1 && $yejiTableRowDeptIds !== []) {
YejiStatsLogic::applyPrescriptionOrderListAlignedWinnerDeptFilter(
$query,
(int) $this->params['assistant_dept_id'],
$yejiTableRowDeptIds
);
return;
}
$rootDeptId = (int) $this->params['assistant_dept_id'];
$deptIds = DeptLogic::getSelfAndDescendantIds($rootDeptId);
$deptIds = array_values(array_filter(array_map('intval', $deptIds), static function (int $id): bool {
@@ -918,6 +936,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
}
$inList = implode(',', $deptIds);
$adTbl = (new AdminDept())->getTable();
<<<<<<< HEAD
if ($yejiPanel) {
/** 业绩看板部门侧栏:仅订单创建人人事部门 ∈ 子树(与「合计业绩」/「接诊诊单」/「复诊」聚合同口径) */
$query->whereExists(
@@ -936,6 +955,12 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
);
});
}
=======
$poRef = '`' . str_replace('`', '', $poTbl) . '`';
$query->whereRaw(
YejiStatsLogic::sqlPrescriptionOrderListDeptSubtreeMatchCreatorFirst($poRef, $diagTbl, $adTbl, $inList)
);
>>>>>>> master
}
}
+13 -1
View File
@@ -1213,7 +1213,7 @@ class OrderLogic
}
$rows = $q
->field(['id', 'order_no', 'order_type', 'amount', 'status', 'create_time', 'creator_id', 'remark'])
->field(['id', 'order_no', 'order_type', 'amount', 'status', 'create_time', 'creator_id', 'remark', 'is_exempt'])
->order('id', 'desc')
->select()
->toArray();
@@ -1400,4 +1400,16 @@ class OrderLogic
'errors' => $errors,
];
}
public static function setExempt(int $id, int $isExempt): bool
{
$order = Order::find($id);
if (!$order) {
self::setError('订单不存在');
return false;
}
$order->is_exempt = $isExempt;
$order->save();
return true;
}
}
@@ -5,6 +5,8 @@ declare(strict_types=1);
namespace app\adminapi\logic\stats;
use app\adminapi\logic\dept\DeptLogic;
use app\common\model\auth\AdminDept;
use app\common\model\tcm\Diagnosis;
use app\common\model\tcm\PrescriptionOrder;
use app\common\service\DataScope\DataScopeService;
use app\common\service\qywx\MediaChannelService;
@@ -20,10 +22,17 @@ use think\facade\Db;
* · **二中心及其组织下级在选定渠道下计 0**(与进线一致)
* - 复诊统计 = 仅**名称含「二中心」的部门及其组织架构下级**:区间内业务单按患者 create_time 排序,第 1 笔不计,第 2 笔起分列「复诊2」「复诊3」…;口径同业绩单条件;按**订单创建人**所在部门在组织树内 rollup(含下级),与排行榜复诊一致
* - 已完成挂号 = status=3 挂号条数(appointment_date),列「已完成挂号」;部门归属:有效医助→接诊医生→展示行映射。
<<<<<<< HEAD
* - 接诊诊单 = 列名对应**业务订单条数**(非取消、create_time 区间),按**订单创建人**人事部门落在该行子树即计入;多行命中时取**最深的展示部门行**。与处方列表按 assistant_dept_id 筛选不同(列表保留旧口径:诊单医助 ∪ 创建人)。
* - {渠道}成交单 = 选定渠道时与 {渠道}业绩同源:区间内 **非取消业务订单条数**(仅剔除 fulfillment_status=4NULL 与其它状态计入,按 create_time 归日);dict(channels) 与 tag 口径均按订单计(按订单创建人归属),非挂号条数。
* - 合计业绩 = 区间内业务订单 amount 求和(排除已取消(4) 与软删),**部门列**与「接诊诊单」一致:**订单创建人**人事部门落在该展示行子树则计入,多行命中取最深展示行;与排行榜诊金同口径。
* 无法归入任一展示部门的金额单列「未归属中心」行;**底栏「合计业绩」「接诊诊单」= 当前表内各行业绩列相加**(含未归属行),与弹窗摊分口径一致。
=======
* - 接诊诊单 = 列名对应**业务订单条数**(非取消、create_time 区间),与处方列表按 **assistant_dept_id** 筛选时一致:优先按订单 **creator_id** 人事部门;无创建人时回退诊单 **assistant_id**;多行命中时取**最深的展示部门行**,与「业绩归属医助」分组可能不同。
* - {渠道}成交单 = 选定渠道时与 {渠道}业绩同源:区间内 **非取消业务订单条数**(仅剔除 fulfillment_status=4NULL 与其它状态计入,按 create_time 归日);dict(channels) 与 tag 口径均按订单计(tag 时按诊单医助收窄),非挂号条数。
* - 合计业绩 = 区间内业务订单 amount 求和(排除已取消(4) 与软删),**部门列**与「接诊诊单」一致;与处方列表按 assistant_dept_id 筛选后的金额合计一致。
* 无法归入任一展示部门的金额单列「未归属中心」行;**全表合计行**仍为同时间/业绩条件的全表 SUM(不限部门)。
>>>>>>> master
* · 时间窗与 stats_order_amount_performance 一致;列表若另有患者筛选或账号数据域收窄,金额可能仍不同
* · 选定渠道时「{渠道}业绩」列仍按比例展示;合计列全量与渠道列含未归属补差;**二中心及其组织下级展示部门不参与该渠道进线/业绩/成交单/ROI 分子**(与同模块 DeptLogic::getErCenterSubtreeDeptIdSet 一致)
* - {渠道}业绩 = 与「处方订单列表」业绩同口径(≠4):按 **o.create_time** 落入区间,按**订单创建人**归部门;
@@ -550,12 +559,20 @@ class YejiStatsLogic
}
$scopedTableNote = ($explicitDeptFilter
<<<<<<< HEAD
? ' 已筛选展示部门时,表格仅展示所选树中的部门行;底栏「合计业绩」「接诊诊单」与当前表内各行业绩列相加一致(含「未归属中心」行若展示)。'
=======
? ' 已筛选展示部门时,表格「合计业绩」「接诊诊单」各行为同一套列表部门口径(**创建人人事部门优先**,无创建人则诊单医助;∪子树、最深展示行);底栏合计业绩/成交单与处方订单列表 extend(所选部门树+数据域)一致,可能与各行相加不同。'
>>>>>>> master
: '')
. ($dataScopeRestricted
? ' 当前账号受数据范围限制时,底栏合计业绩与列表同域。'
: '');
<<<<<<< HEAD
$baseNoteNoChannel = '业绩口径:除履约已取消(4)外全部计入。部门行「合计业绩」「接诊诊单」「复诊」均按**订单创建人人事部门 ∈ 该展示行子树**归属(多行命中取最深展示行),与医助排行榜诊金一致;与处方订单列表 assistant_dept_id 默认筛选(诊单医助 ∪ 创建人)不同——侧栏点击会自动收窄为创建人口径。多表「本月」区间为当月1日至服务器当日。**投放成本**按进线占比分摊,**二中心子树展示部门进线在分摊中计 0**(与部门 ErCenter 子树一致)。';
=======
$baseNoteNoChannel = '业绩口径:除履约已取消(4)外全部计入。部门行「合计业绩」与「接诊诊单」均为列表 assistant_dept_id 口径(**创建人优先**,否则诊单医助;人事部门∪子树,多行命中取最深展示行)。多表「本月」区间为当月1日至服务器当日。**投放成本**按进线占比分摊,**二中心子树展示部门进线在分摊中计 0**(与部门 ErCenter 子树一致)。';
>>>>>>> master
return [
'start_date' => $startDate,
@@ -565,9 +582,15 @@ class YejiStatsLogic
'channel_name' => $channelInfo ? (string) $channelInfo['channel_name'] : '',
'channel_group' => $channelInfo ? (string) $channelInfo['source_group_name'] : '',
'channel_filter_note' => $channelCode !== ''
<<<<<<< HEAD
? ('{渠道}业绩:按订单 create_time 归日、仅剔除履约已取消(4)(NULL 与其它状态均计入);渠道判定用"该患者是否有挂号 channels 命中 dict + status=3"(不限挂号时点)。部门归属按**订单创建人**人事部门 ∈ 子树。'
. ' {渠道}成交单数:与{渠道}业绩同源,仅剔除已取消(4)的订单条数(非挂号条数),按订单创建人归部门。'
. ' 「已完成挂号」列同 admin 挂号已完成列表;「接诊诊单」列为业务订单条数(非取消),同样按订单创建人归部门。**二中心及其组织下级**(与部门模块 ErCenter 子树一致)**不计入**该渠道进线、业绩与成交单。**投放成本**按各部门进线条数占展示部门进线合计的比例分摊,**二中心子树进线在分摊中计 0**(未选渠道时仍显示进线数,仅不参与均摊)。ROI = 渠道业绩(不含二中心子树)÷ 总投放成本。进线按所选标签客户精确过滤。'
=======
? ('{渠道}业绩:与「处方订单列表」业绩同口径,按订单 create_time 归日、仅剔除履约已取消(4)(NULL 与其它状态均计入);渠道判定用"该患者是否有挂号 channels 命中 dict + status=3"(不限挂号时点)。'
. ' {渠道}成交单数:与{渠道}业绩同源,仅剔除已取消(4)的订单条数(非挂号条数)。'
. ' 「已完成挂号」列同 admin 挂号已完成列表;「接诊诊单」列为业务订单条数(非取消),与列表按 assistant_dept_id 筛选时一致(**创建人优先**,否则诊单医助;∪子树)。**二中心及其组织下级**(与部门模块 ErCenter 子树一致)**不计入**该渠道进线、业绩与成交单。**投放成本**按各部门进线条数占展示部门进线合计的比例分摊,**二中心子树进线在分摊中计 0**(未选渠道时仍显示进线数,仅不参与均摊)。ROI = 渠道业绩(不含二中心子树)÷ 总投放成本。进线按所选标签客户精确过滤。'
>>>>>>> master
. ($tagFallback
? ' ⚠ 当前业务尚未回填 tcm_diagnosis.external_userid 与 user_auth.unionid,已自动退化为「该标签客户的跟进医助 → 区间内接诊/业绩」口径,仅用于趋势观察。'
: '')
@@ -3399,6 +3422,101 @@ class YejiStatsLogic
}
/**
<<<<<<< HEAD
=======
* 与处方订单列表 buildBaseQueryForStats(仅人事筛选维度)对齐的订单查询 WHERE。
*
* @param int[]|null $expandedDeptIds
* @param int[]|null $visibleAdminIds
*/
private static function applyOrderListStatsVisibilityToPrescriptionQuery(
$query,
?array $expandedDeptIds,
?array $visibleAdminIds
): void {
$hasDept = $expandedDeptIds !== null && $expandedDeptIds !== [];
$hasVis = $visibleAdminIds !== null && $visibleAdminIds !== [];
if (!$hasDept && !$hasVis) {
return;
}
$diagTable = self::tableWithPrefix('tcm_diagnosis');
if ($hasDept) {
$ids = array_values(array_unique(array_filter(array_map('intval', $expandedDeptIds), static function (int $x): bool {
return $x > 0;
})));
if ($ids === []) {
$query->whereRaw('0 = 1');
return;
}
$inList = implode(',', $ids);
$diagTb = (new Diagnosis())->getTable();
$adTb = (new AdminDept())->getTable();
$query->whereRaw(self::sqlPrescriptionOrderListDeptSubtreeMatchCreatorFirst('`o`', $diagTb, $adTb, $inList));
}
if ($hasVis) {
$ids = array_values(array_unique(array_filter(array_map('intval', $visibleAdminIds), static function (int $x): bool {
return $x > 0;
})));
if ($ids === []) {
$query->whereRaw('0 = 1');
return;
}
$inList = implode(',', $ids);
$query->where(function ($q) use ($diagTable, $inList, $ids) {
$q->whereIn('o.creator_id', $ids);
$q->whereOrRaw(
"EXISTS (SELECT 1 FROM {$diagTable} dg WHERE dg.id = o.diagnosis_id "
. "AND dg.delete_time IS NULL AND dg.assistant_id IN ({$inList}))"
);
});
}
}
/**
* 收窄合计时与订单列表 extend「业绩金额」一致(create_time、NOT(fulfillment<=>4)、人事筛选)。
*
* @param int[]|null $expandedDeptIds
* @param int[]|null $visibleAdminIds
*/
private static function sumOrderListAlignedPerformanceAmount(
int $startTs,
int $endTs,
?array $expandedDeptIds,
?array $visibleAdminIds
): float {
$query = Db::name('tcm_prescription_order')
->alias('o')
->whereNull('o.delete_time')
->where('o.create_time', 'between', [$startTs, $endTs]);
self::applyPrescriptionOrderNotCancelledForPerformance($query);
self::applyOrderListStatsVisibilityToPrescriptionQuery($query, $expandedDeptIds, $visibleAdminIds);
return round((float) $query->sum('o.amount'), 1);
}
/**
* 与 sumOrderListAlignedPerformanceAmount 同源,计订单条数。
*/
private static function countOrderListAlignedPerformanceOrders(
int $startTs,
int $endTs,
?array $expandedDeptIds,
?array $visibleAdminIds
): int {
$query = Db::name('tcm_prescription_order')
->alias('o')
->whereNull('o.delete_time')
->where('o.create_time', 'between', [$startTs, $endTs]);
self::applyPrescriptionOrderNotCancelledForPerformance($query);
self::applyOrderListStatsVisibilityToPrescriptionQuery($query, $expandedDeptIds, $visibleAdminIds);
return (int) $query->count();
}
/**
>>>>>>> master
* 标签/医助兜底口径下的渠道向业绩全量 SUM(与 sumPerformance 的 scoped 分支同一组 WHERE,无部门归属过滤)。
*/
private static function sumGlobalPerformanceTagScoped(
@@ -3824,8 +3942,13 @@ class YejiStatsLogic
}
/**
<<<<<<< HEAD
* 与业绩看板部门列一致:仅按订单创建人候选,将其人事部门落在展示行子树即命中;命中多行时取组织深度最大的展示行。
* (处方订单列表默认 assistant_dept_id 仍为「诊单医助 ∪ 创建人」并集,与看板有别。)
=======
* 与 {@see PrescriptionOrderLists::applyDoctorAssistantFilters} 中 assistant_dept_id(未开抽屉对齐开关)一致:
* 有创建人则仅用创建人人事部门落在该行子树即命中;无创建人则诊单医助;命中多行时取组织深度最大的展示行。
>>>>>>> master
*
* @param array<int, true> $candidates admin_id => true
* @param array<int, int[]> $adminDeptIdList
@@ -3899,8 +4022,12 @@ class YejiStatsLogic
}
/**
<<<<<<< HEAD
* 看板「合计业绩 / 接诊诊单」列用的部门摊行:仅按订单创建人 o.creator_id 的人事部门映射到展示行子树(命中多行取最深),
* 与侧栏 yeji_order_drawer=1、{@see sqlPerformanceAttributionAdminExpr} 一致。不再把诊单医助纳入归属候选。
=======
* 列表部门口径(有创建人则仅用创建人、否则诊单医助 ⇄ 展示行子树,最深行):聚单笔数与金额,供「接诊诊单」与「合计业绩」列共用。
>>>>>>> master
*
* @return array{count: array<int, int>, amount: array<int, float>}
*/
@@ -3970,6 +4097,8 @@ class YejiStatsLogic
$c = (int) ($r['creator_id'] ?? 0);
if ($c > 0) {
$adminIds[] = $c;
} elseif ($a > 0) {
$adminIds[] = $a;
}
}
$adminIds = array_values(array_unique(array_filter($adminIds, static fn (int $x): bool => $x > 0)));
@@ -3990,8 +4119,16 @@ class YejiStatsLogic
$byAmt = [];
foreach ($orderRows as $r) {
$cr = (int) ($r['creator_id'] ?? 0);
<<<<<<< HEAD
if ($cr <= 0) {
continue;
=======
$cand = [];
if ($cr > 0) {
$cand[$cr] = true;
} elseif ($asst > 0) {
$cand[$asst] = true;
>>>>>>> master
}
$cand = [$cr => true];
$deptKey = self::resolveDeepestYejiTableRowForListDeptOr(
@@ -4014,9 +4151,15 @@ class YejiStatsLogic
}
/**
<<<<<<< HEAD
* 成交单数按部门:有展示行时与 {@see aggregateListAlignedOrderMetricsByDept} 同口径(仅订单创建人归属)。
* 无展示行时按 sqlPerformanceAttributionAdminExpr 分组再 fold(同样为创建人)。
* 选定医助 tag 时收窄为 o.creator_id ∈ 集。
=======
* 成交单数按部门:**处方订单列表**按 assistant_dept_id 筛选时的业务单条数(创建人优先、无创建人则诊单医助 ∪ 部门子树),
* 与 {@see PrescriptionOrderLists::applyDoctorAssistantFilters} 在 <b>未</b> 传 yeji_drawer_match_table_performance 时一致。
* 选定医助 tag 时收窄为(业绩归属医助 ∈ 集 **或** 诊单医助 ∈ 集 **或** 创建人 ∈ 集)。
>>>>>>> master
*
* @return array<int, int>
*/
@@ -4804,6 +4947,7 @@ class YejiStatsLogic
}
/**
<<<<<<< HEAD
* 提成结算统计:渠道收窄条件应用到业务订单查询(与 sumPerformance「scoped」同源)。
*/
private static function applyCommissionSettlementChannelFilter(Query $query, array $c): void
@@ -4837,11 +4981,49 @@ class YejiStatsLogic
return;
}
if ($tagAssistantIds !== null && $tagAssistantIds === [] && $tagFallback) {
=======
* 业务订单列表 WHERE 片段:有创建人则仅按创建人人事部门落在子树;无创建人则按诊单医助(与
* {@see aggregateListAlignedOrderMetricsByDept}
* {@see PrescriptionOrderLists::applyDoctorAssistantFilters} assistant_dept_id 一致)。
*
* @param string $orderRefBackticked 主查询里业务订单表标识,须已加反引号:列表/Model 无别名时用 `` `zyt_tcm_prescription_order` ``Db::name()->alias('o') 时用 `` `o` ``
* @param string $inListComma 已净化的 id 列表(仅数字与逗号)
*/
public static function sqlPrescriptionOrderListDeptSubtreeMatchCreatorFirst(
string $orderRefBackticked,
string $diagTbl,
string $adTbl,
string $inListComma
): string {
$creatorInSubtree = "EXISTS (SELECT 1 FROM `{$adTbl}` adc WHERE adc.`admin_id` = {$orderRefBackticked}.`creator_id` AND adc.`dept_id` IN ({$inListComma}))";
$assistantInSubtree = "EXISTS (SELECT 1 FROM `{$adTbl}` ad INNER JOIN `{$diagTbl}` dg ON ad.admin_id = dg.assistant_id AND dg.delete_time IS NULL "
. "WHERE dg.`id` = {$orderRefBackticked}.`diagnosis_id` AND ad.dept_id IN ({$inListComma}))";
return '('
. '((IFNULL(' . $orderRefBackticked . '.`creator_id`,0) > 0) AND (' . $creatorInSubtree . '))'
. ' OR '
. '((IFNULL(' . $orderRefBackticked . '.`creator_id`,0) <= 0) AND (' . $assistantInSubtree . '))'
. ')';
}
/**
* 业绩看板侧栏:与「接诊诊单」列 {@see aggregateListAlignedOrderMetricsByDept} 同源——在**同一表格**
* 展示行集合内,创建人优先归属;无创建人则医助;多行命中时取组织深度最大的展示行(同深取较小 dept id)。
* 传入当前表全部展示行 dept id。
*
* @param int[] $tableRowDeptIds 当前业绩表 body 行 dept_id(含点击行及兄弟行)
*/
public static function applyPrescriptionOrderListAlignedWinnerDeptFilter(Query $query, int $targetRowDeptId, array $tableRowDeptIds): void
{
$targetRowDeptId = (int) $targetRowDeptId;
if ($targetRowDeptId <= 0) {
>>>>>>> master
$query->whereRaw('0 = 1');
return;
}
<<<<<<< HEAD
$att = self::sqlPerformanceAttributionAdminExpr();
$narrowed = false;
if ($tagDiagIds !== null) {
@@ -5954,5 +6136,63 @@ class YejiStatsLogic
'next_settlement_month' => $next,
'deferred_carry_count' => count($deferredIds),
];
=======
$uniq = [];
foreach ($tableRowDeptIds as $id) {
$id = (int) $id;
if ($id > 0) {
$uniq[$id] = true;
}
}
$tableRowDeptIds = array_keys($uniq);
if ($tableRowDeptIds === []) {
$query->whereRaw('0 = 1');
return;
}
$deptRows = Db::name('dept')->whereNull('delete_time')->field(['id', 'pid'])->select()->toArray();
$deptById = [];
foreach ($deptRows as $row) {
$deptById[(int) $row['id']] = $row;
}
$dT = self::deptDepthFromRoot($targetRowDeptId, $deptById);
$beaters = [];
foreach ($tableRowDeptIds as $r) {
$r = (int) $r;
if ($r <= 0 || $r === $targetRowDeptId) {
continue;
}
$dR = self::deptDepthFromRoot($r, $deptById);
if ($dR > $dT || ($dR === $dT && $r < $targetRowDeptId)) {
$beaters[] = $r;
}
}
$poRef = '`' . str_replace('`', '', (new PrescriptionOrder())->getTable()) . '`';
$diagTbl = (new Diagnosis())->getTable();
$adTbl = (new AdminDept())->getTable();
$tSubtree = DeptLogic::getSelfAndDescendantIds($targetRowDeptId);
$tSubtree = array_values(array_filter(array_map('intval', $tSubtree), static fn (int $id): bool => $id > 0));
if ($tSubtree === []) {
$query->whereRaw('0 = 1');
return;
}
$tIn = implode(',', $tSubtree);
$clauses = [self::sqlPrescriptionOrderListDeptSubtreeMatchCreatorFirst($poRef, $diagTbl, $adTbl, $tIn)];
foreach ($beaters as $b) {
$bSubtree = DeptLogic::getSelfAndDescendantIds((int) $b);
$bSubtree = array_values(array_filter(array_map('intval', $bSubtree), static fn (int $id): bool => $id > 0));
if ($bSubtree === []) {
continue;
}
$bIn = implode(',', $bSubtree);
$clauses[] = 'NOT (' . self::sqlPrescriptionOrderListDeptSubtreeMatchCreatorFirst($poRef, $diagTbl, $adTbl, $bIn) . ')';
}
$query->whereRaw('(' . implode(' AND ', $clauses) . ')');
>>>>>>> master
}
}
@@ -535,6 +535,19 @@ class DiagnosisLogic extends BaseLogic
return 0;
}
public static function markAssignRead(int $diagnosisId, int $adminId): void
{
if ($diagnosisId <= 0 || $adminId <= 0) {
return;
}
Db::name('tcm_diagnosis')
->where('id', $diagnosisId)
->where('assistant_id', $adminId)
->whereNull('assign_read_at')
->whereNull('delete_time')
->update(['assign_read_at' => time()]);
}
/**
* @notes 指派医助(每次成功更新写入一条操作记录,批量指派为多次接口调用各记一条)。
* **原医助**:优先诊单 `assistant_id`;若为 0 且本次为指派到某人,则从**最近一条**指派日志推断(覆盖发货释放后库为 0、业务上仍有「前任」医助的情况)。
@@ -588,7 +601,10 @@ class DiagnosisLogic extends BaseLogic
Db::name('tcm_diagnosis')
->where('id', $id)
->whereNull('delete_time')
->update(['assistant_id' => $toAssistantId]);
->update([
'assistant_id' => $toAssistantId,
'assign_read_at' => $toAssistantId > 0 ? null : 0,
]);
$req = request();
$admin = $req->adminInfo ?? [];
@@ -324,7 +324,16 @@ class PrescriptionOrderLogic
if ($payOrderIds === []) {
return '未关联支付单,关联支付单总金额须大于等于定金门槛 ¥' . $min;
}
// 豁免权:关联的支付单中有豁免单则跳过金额校验
$hasExempt = Order::whereIn('id', $payOrderIds)
->whereNull('delete_time')
->where('is_exempt', 1)
->count();
if ($hasExempt > 0) {
return null;
}
// 检查关联支付单的总金额
$rows = Order::whereIn('id', $payOrderIds)->whereNull('delete_time')->column('amount', 'id');
$totalAmount = 0.0;
@@ -504,7 +513,7 @@ class PrescriptionOrderLogic
return [];
}
$rows = Order::whereIn('id', $ids)->whereNull('delete_time')
->field(['id', 'order_no', 'order_type', 'amount', 'status', 'create_time', 'creator_id', 'remark'])
->field(['id', 'order_no', 'order_type', 'amount', 'status', 'create_time', 'creator_id', 'remark', 'is_exempt'])
->order('id', 'asc')
->select()
->toArray();