更新功能

This commit is contained in:
Your Name
2026-05-26 18:05:48 +08:00
parent 5b233fb0ab
commit 0dd9cdcba9
8 changed files with 4703 additions and 10 deletions
+299 -1
View File
@@ -15,7 +15,10 @@
namespace app\api\controller;
use app\adminapi\logic\tcm\DiagnosisLogic;
use app\adminapi\logic\tcm\TrackingNoteLogic;
use app\adminapi\logic\ConfigLogic;
use app\common\model\tcm\Diagnosis;
use app\common\model\tcm\BloodRecord;
/**
* 中医诊断控制器(供小程序调用)
@@ -28,7 +31,7 @@ class TcmController extends BaseApiController
* @notes 不需要登录的方法
* @var array
*/
public array $notNeedLogin = ['getPatientSignature', 'diagnosisDetail', 'getDict', 'confirmDiagnosis', 'getCardList', 'getOrderByNo', 'patientHangupVideo'];
public array $notNeedLogin = ['getPatientSignature', 'diagnosisDetail', 'getDict', 'confirmDiagnosis', 'getCardList', 'getOrderByNo', 'patientHangupVideo', 'dailyTrackingWindow', 'dailyTrackingNotes'];
/**
* @notes 获取患者签名(供小程序调用)
@@ -332,6 +335,301 @@ class TcmController extends BaseApiController
}
}
/**
* @notes 日常记录窗口数据(血糖血压 / 饮食 / 运动)—— 患者端
*
* 路由:GET /api/tcm/dailyTrackingWindow?diagnosis_id=:id&patient_id=:pid&start_date=&end_date=
* 说明:供小程序「日常记录」页面拉取一段时间内的跟踪数据,复用后台 fetchTrackingWindow。
*
* @return \think\response\Json
*/
public function dailyTrackingWindow()
{
$diagnosisId = (int) $this->request->get('diagnosis_id', 0);
$patientId = (int) $this->request->get('patient_id', 0);
$startDate = (string) $this->request->get('start_date', '');
$endDate = (string) $this->request->get('end_date', '');
if ($diagnosisId <= 0) {
return $this->fail('诊单ID不能为空');
}
try {
// 患者端越权校验:诊单必须存在;如传入 patient_id 则须与诊单匹配
$diagnosis = Diagnosis::where('id', $diagnosisId)->find();
if (!$diagnosis) {
return $this->fail('诊单不存在');
}
if ($patientId > 0 && (int) $diagnosis['patient_id'] !== $patientId) {
return $this->fail('无权查看该诊单的日常记录');
}
$data = DiagnosisLogic::fetchTrackingWindow($diagnosisId, $startDate, $endDate);
// 把诊断的基础信息一并返回,便于小程序展示头部摘要
$data['diagnosis'] = [
'id' => (int) $diagnosis['id'],
'patient_id' => (int) $diagnosis['patient_id'],
'patient_name' => (string) ($diagnosis['patient_name'] ?? ''),
'age' => (int) ($diagnosis['age'] ?? 0),
'gender' => (int) ($diagnosis['gender'] ?? 0),
];
return $this->data($data);
} catch (\Exception $e) {
return $this->fail($e->getMessage());
}
}
/**
* @notes 日常记录-跟踪备注列表(患者端)
*
* 路由:GET /api/tcm/dailyTrackingNotes?diagnosis_id=:id&patient_id=:pid
*
* @return \think\response\Json
*/
public function dailyTrackingNotes()
{
$diagnosisId = (int) $this->request->get('diagnosis_id', 0);
$patientId = (int) $this->request->get('patient_id', 0);
if ($diagnosisId <= 0) {
return $this->fail('诊单ID不能为空');
}
try {
$diagnosis = Diagnosis::where('id', $diagnosisId)->find();
if (!$diagnosis) {
return $this->fail('诊单不存在');
}
if ($patientId > 0 && (int) $diagnosis['patient_id'] !== $patientId) {
return $this->fail('无权查看该诊单的跟踪备注');
}
$notes = TrackingNoteLogic::getByDiagnosis($diagnosisId);
return $this->data($notes);
} catch (\Exception $e) {
return $this->fail($e->getMessage());
}
}
/**
* @notes 校验当前登录患者是否拥有指定诊单(通过 diagnosis_view_records 关联)
* @param int $diagnosisId
* @return array{ok:bool,diagnosis?:\app\common\model\tcm\Diagnosis,error?:string}
*/
private function ensurePatientOwnsDiagnosis(int $diagnosisId): array
{
if (!$this->userId) {
return ['ok' => false, 'error' => '请先登录'];
}
if ($diagnosisId <= 0) {
return ['ok' => false, 'error' => '诊单ID不能为空'];
}
$diagnosis = Diagnosis::where('id', $diagnosisId)->find();
if (!$diagnosis) {
return ['ok' => false, 'error' => '诊单不存在'];
}
// 通过 diagnosis_view_records 校验当前小程序用户与该诊单的归属关系
// 该表是「就诊卡列表」的来源(参考 DiagnosisLogic::getCardList
$owned = \think\facade\Db::name('diagnosis_view_records')
->where('user_id', $this->userId)
->where('diagnosis_id', $diagnosisId)
->where('delete_time', null)
->find();
if (!$owned) {
return ['ok' => false, 'error' => '无权操作该诊单'];
}
return ['ok' => true, 'diagnosis' => $diagnosis];
}
/**
* @notes 日常记录-获取今日已录入的血糖血压记录(患者端)
*
* 路由:GET /api/tcm/dailyTodayBloodRecord?diagnosis_id=:id
*
* 用于「日常记录」页打开「录入今日」表单时预填数据;只返回 source=1 的当天记录。
*
* @return \think\response\Json
*/
public function dailyTodayBloodRecord()
{
$diagnosisId = (int) $this->request->get('diagnosis_id', 0);
$check = $this->ensurePatientOwnsDiagnosis($diagnosisId);
if (!$check['ok']) {
return $this->fail($check['error']);
}
try {
$todayStart = strtotime(date('Y-m-d 00:00:00'));
$todayEnd = strtotime(date('Y-m-d 23:59:59'));
$record = BloodRecord::where('diagnosis_id', $diagnosisId)
->where('source', 1)
->where('record_date', '>=', $todayStart)
->where('record_date', '<=', $todayEnd)
->order('id', 'desc')
->find();
if (!$record) {
return $this->data(['record' => null, 'today' => date('Y-m-d')]);
}
$data = $record->toArray();
$data['record_date'] = date('Y-m-d', (int) $data['record_date']);
return $this->data(['record' => $data, 'today' => date('Y-m-d')]);
} catch (\Exception $e) {
return $this->fail($e->getMessage());
}
}
/**
* @notes 日常记录-保存今日的血糖血压记录(患者端)
*
* 路由:POST /api/tcm/dailySaveBloodRecord
* 必填:diagnosis_id
* 可选数值:fasting_blood_sugar, postprandial_blood_sugar, other_blood_sugar,
* systolic_pressure, diastolic_pressure
* 可选字符串:remark
*
* 语义:同一天同一诊单只有一条 source=1 的记录;存在则 update,不存在则 create。
* 日期强制为「今天」;任何 record_date 入参被忽略,避免补录历史。
*
* @return \think\response\Json
*/
public function dailySaveBloodRecord()
{
$diagnosisId = (int) $this->request->post('diagnosis_id', 0);
$check = $this->ensurePatientOwnsDiagnosis($diagnosisId);
if (!$check['ok']) {
return $this->fail($check['error']);
}
$diagnosis = $check['diagnosis'];
// 解析并校验数值字段;空字符串当 null 处理
$parseDecimal = function ($v) {
if ($v === null || $v === '' || $v === '0' || $v === 0) return null;
if (!is_numeric($v)) return null;
$f = (float) $v;
return $f > 0 ? $f : null;
};
$parseInt = function ($v) {
if ($v === null || $v === '') return null;
if (!is_numeric($v)) return null;
$i = (int) $v;
return $i > 0 ? $i : null;
};
$fasting = $parseDecimal($this->request->post('fasting_blood_sugar'));
$postprandial = $parseDecimal($this->request->post('postprandial_blood_sugar'));
$other = $parseDecimal($this->request->post('other_blood_sugar'));
$systolic = $parseInt($this->request->post('systolic_pressure'));
$diastolic = $parseInt($this->request->post('diastolic_pressure'));
$remark = trim((string) $this->request->post('remark', ''));
// 至少要填一个有效字段
if ($fasting === null && $postprandial === null && $other === null &&
$systolic === null && $diastolic === null && $remark === '') {
return $this->fail('请至少填写一项血糖或血压数据');
}
// 边界校验,避免误录天文数字
if ($fasting !== null && ($fasting < 0.1 || $fasting > 50)) return $this->fail('空腹血糖数值异常');
if ($postprandial !== null && ($postprandial < 0.1 || $postprandial > 50)) return $this->fail('餐后血糖数值异常');
if ($other !== null && ($other < 0.1 || $other > 50)) return $this->fail('其他血糖数值异常');
if ($systolic !== null && ($systolic < 40 || $systolic > 300)) return $this->fail('收缩压数值异常');
if ($diastolic !== null && ($diastolic < 20 || $diastolic > 200)) return $this->fail('舒张压数值异常');
try {
$todayStart = strtotime(date('Y-m-d 00:00:00'));
$todayEnd = strtotime(date('Y-m-d 23:59:59'));
$existing = BloodRecord::where('diagnosis_id', $diagnosisId)
->where('source', 1)
->where('record_date', '>=', $todayStart)
->where('record_date', '<=', $todayEnd)
->order('id', 'desc')
->find();
$payload = [
'diagnosis_id' => $diagnosisId,
'patient_id' => (int) $diagnosis['patient_id'],
'record_date' => $todayStart,
'record_time' => date('H:i'),
'fasting_blood_sugar' => $fasting,
'postprandial_blood_sugar' => $postprandial,
'other_blood_sugar' => $other,
'systolic_pressure' => $systolic,
'diastolic_pressure' => $diastolic,
'remark' => $remark,
'source' => 1,
];
if ($existing) {
$payload['id'] = $existing['id'];
BloodRecord::update($payload);
return $this->success('已更新今日记录', ['id' => $existing['id']]);
}
$created = BloodRecord::create($payload);
return $this->success('录入成功', ['id' => $created->id]);
} catch (\Exception $e) {
return $this->fail($e->getMessage());
}
}
/**
* @notes 日常记录-删除今日的血糖血压记录(患者端)
*
* 路由:POST /api/tcm/dailyDeleteBloodRecord
* 必填:diagnosis_id, id
*
* 只允许删除当前用户拥有的诊单 + 当天 + source=1 的记录;其他情况一律拒绝。
*
* @return \think\response\Json
*/
public function dailyDeleteBloodRecord()
{
$diagnosisId = (int) $this->request->post('diagnosis_id', 0);
$recordId = (int) $this->request->post('id', 0);
$check = $this->ensurePatientOwnsDiagnosis($diagnosisId);
if (!$check['ok']) {
return $this->fail($check['error']);
}
if ($recordId <= 0) {
return $this->fail('记录ID不能为空');
}
try {
$todayStart = strtotime(date('Y-m-d 00:00:00'));
$todayEnd = strtotime(date('Y-m-d 23:59:59'));
$record = BloodRecord::where('id', $recordId)
->where('diagnosis_id', $diagnosisId)
->where('source', 1)
->find();
if (!$record) {
return $this->fail('记录不存在或无权删除');
}
$ts = (int) $record['record_date'];
if ($ts < $todayStart || $ts > $todayEnd) {
return $this->fail('只能删除当天的记录');
}
BloodRecord::destroy($recordId);
return $this->success('已删除');
} catch (\Exception $e) {
return $this->fail($e->getMessage());
}
}
/**
* @notes 根据订单号获取订单详情(供小程序支付页调用)
* @return \think\response\Json
@@ -0,0 +1,11 @@
-- 为「血糖血压记录表」增加来源字段
-- 0 = 医生 / 助理在管理后台录入(历史数据默认 0)
-- 1 = 患者在小程序「日常记录」页自录
-- 该字段用于:1) 后台 DailyMatrix 上展示「患者自录」徽章;2) 限制患者只能编辑/删除自己当天的记录
ALTER TABLE `zyt_tcm_blood_record`
ADD COLUMN `source` tinyint(1) NOT NULL DEFAULT 0 COMMENT '来源:0-医生录入 1-患者自录' AFTER `remark`;
-- 加索引方便按来源过滤
ALTER TABLE `zyt_tcm_blood_record`
ADD KEY `idx_source` (`source`);