1013 lines
39 KiB
PHP
Executable File
1013 lines
39 KiB
PHP
Executable File
<?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
|
||
// +----------------------------------------------------------------------
|
||
|
||
namespace app\api\controller;
|
||
|
||
use app\adminapi\logic\tcm\DiagnosisLogic;
|
||
use app\adminapi\logic\tcm\TrackingNoteLogic;
|
||
use app\api\logic\tcm\DailyGamifyLogic;
|
||
use app\api\logic\tcm\DailyFamilyLikeLogic;
|
||
use app\api\logic\tcm\DailyShareLogic;
|
||
use app\adminapi\logic\ConfigLogic;
|
||
use app\common\model\tcm\Diagnosis;
|
||
use app\common\model\tcm\BloodRecord;
|
||
use app\common\model\tcm\DietRecord;
|
||
use app\common\model\tcm\ExerciseRecord;
|
||
|
||
/**
|
||
* 中医诊断控制器(供小程序调用)
|
||
* Class TcmController
|
||
* @package app\api\controller
|
||
*/
|
||
class TcmController extends BaseApiController
|
||
{
|
||
/**
|
||
* @notes 不需要登录的方法
|
||
* @var array
|
||
*/
|
||
public array $notNeedLogin = ['getPatientSignature', 'diagnosisDetail', 'getDict', 'confirmDiagnosis', 'getCardList', 'getOrderByNo', 'patientHangupVideo', 'dailySharePreview', 'dailyFamilyLike'];
|
||
|
||
/**
|
||
* @notes 获取患者签名(供小程序调用)
|
||
* @return \think\response\Json
|
||
*/
|
||
public function getPatientSignature()
|
||
{
|
||
$patientId = $this->request->get('patient_id');
|
||
|
||
if (!$patientId) {
|
||
return $this->fail('患者ID不能为空');
|
||
}
|
||
|
||
// 调用逻辑层
|
||
$result = DiagnosisLogic::getPatientSignature($patientId);
|
||
|
||
if ($result === false) {
|
||
return $this->fail(DiagnosisLogic::getError());
|
||
}
|
||
|
||
return $this->data($result);
|
||
}
|
||
|
||
/**
|
||
* @notes 患者挂断视频:后端 DeleteCloudRecording + 结束通话记录(与诊单 patient_id 校验)
|
||
* @return \think\response\Json
|
||
*/
|
||
public function patientHangupVideo()
|
||
{
|
||
$params = [
|
||
'diagnosis_id' => (int)$this->request->post('diagnosis_id', 0),
|
||
'patient_id' => (int)$this->request->post('patient_id', 0),
|
||
'room_id' => trim((string)$this->request->post('room_id', '')),
|
||
];
|
||
if ($params['patient_id'] <= 0) {
|
||
return $this->fail('患者ID不能为空');
|
||
}
|
||
if ($params['diagnosis_id'] <= 0 && $params['room_id'] === '') {
|
||
return $this->fail('诊单ID或房间号须至少传一项');
|
||
}
|
||
$result = DiagnosisLogic::patientHangupVideoCall($params);
|
||
if ($result === false) {
|
||
return $this->fail(DiagnosisLogic::getError());
|
||
}
|
||
|
||
return $this->success('已结束通话');
|
||
}
|
||
|
||
/**
|
||
* @notes 获取诊单详情(供小程序调用)
|
||
* @return \think\response\Json
|
||
*/
|
||
public function diagnosisDetail()
|
||
{
|
||
$params = [
|
||
'id' => $this->request->get('id'),
|
||
'user_id' => $this->userId,
|
||
'share_user_id' => $this->request->get('share_user_id', 0)
|
||
];
|
||
|
||
if (!$params['id']) {
|
||
return $this->fail('诊单ID不能为空');
|
||
}
|
||
|
||
try {
|
||
$result = DiagnosisLogic::diagnosisDetailWithRecord($params);
|
||
|
||
if ($result === false) {
|
||
return $this->fail(DiagnosisLogic::getError());
|
||
}
|
||
|
||
return $this->data($result);
|
||
} catch (\Exception $e) {
|
||
return $this->fail($e->getMessage());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notes 确认诊单(供小程序调用)
|
||
* @return \think\response\Json
|
||
*/
|
||
public function confirmDiagnosis()
|
||
{
|
||
$params = [
|
||
'id' => $this->request->post('id'),
|
||
'user_id' => $this->userId,
|
||
'share_user_id' => $this->request->post('share_user', 0)
|
||
];
|
||
|
||
if (!$params['id']) {
|
||
return $this->fail('诊单ID不能为空');
|
||
}
|
||
|
||
try {
|
||
$result = DiagnosisLogic::confirmDiagnosisRecord($params);
|
||
|
||
if ($result === false) {
|
||
return $this->fail(DiagnosisLogic::getError());
|
||
}
|
||
|
||
return $this->success('确认成功');
|
||
} catch (\Exception $e) {
|
||
return $this->fail($e->getMessage());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notes 获取字典数据(供小程序调用)
|
||
* @return \think\response\Json
|
||
*/
|
||
public function getDict()
|
||
{
|
||
$type = $this->request->get('type', '');
|
||
|
||
if (!$type) {
|
||
return $this->fail('字典类型不能为空');
|
||
}
|
||
|
||
try {
|
||
$data = ConfigLogic::getDictByType($type);
|
||
return $this->data($data);
|
||
} catch (\Exception $e) {
|
||
return $this->fail($e->getMessage());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notes 获取就诊卡列表(供小程序调用)
|
||
* @return \think\response\Json
|
||
*/
|
||
public function getCardList()
|
||
{
|
||
$user_id =$this->userId;
|
||
|
||
if (!$user_id) {
|
||
return $this->fail('患者ID不能为空');
|
||
}
|
||
|
||
try {
|
||
$result = DiagnosisLogic::getCardList($user_id);
|
||
|
||
if ($result === false) {
|
||
return $this->fail(DiagnosisLogic::getError());
|
||
}
|
||
|
||
return $this->data($result);
|
||
} catch (\Exception $e) {
|
||
return $this->fail($e->getMessage());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notes 新建就诊卡(供小程序调用)
|
||
* @return \think\response\Json
|
||
*/
|
||
public function addCard()
|
||
{
|
||
$params = [
|
||
'user_id' => $this->userId,
|
||
'patient_id' => $this->request->post('patient_id'),
|
||
'patient_name' => $this->request->post('patient_name'),
|
||
'gender' => $this->request->post('gender'),
|
||
'age' => $this->request->post('age'),
|
||
'id_card' => $this->request->post('id_card'),
|
||
'height' => $this->request->post('height'),
|
||
'weight' => $this->request->post('weight'),
|
||
'systolic_pressure' => $this->request->post('systolic_pressure'),
|
||
'diastolic_pressure' => $this->request->post('diastolic_pressure'),
|
||
'fasting_blood_sugar' => $this->request->post('fasting_blood_sugar'),
|
||
'diabetes_discovery_year' => $this->request->post('diabetes_discovery_year'),
|
||
'local_hospital_diagnosis' => $this->request->post('local_hospital_diagnosis'),
|
||
'local_hospital_name' => $this->request->post('local_hospital_name'),
|
||
'local_hospital_visit_date' => $this->request->post('local_hospital_visit_date'),
|
||
'diagnosis_date' => $this->request->post('diagnosis_date'),
|
||
'diagnosis_type' => $this->request->post('diagnosis_type'),
|
||
'syndrome_type' => $this->request->post('syndrome_type'),
|
||
'diabetes_type' => $this->request->post('diabetes_type'),
|
||
'appetite' => $this->request->post('appetite'),
|
||
'water_intake' => $this->request->post('water_intake'),
|
||
'diet_condition' => $this->request->post('diet_condition'),
|
||
'weight_change' => $this->request->post('weight_change'),
|
||
'body_feeling' => $this->request->post('body_feeling'),
|
||
'sleep_condition' => $this->request->post('sleep_condition'),
|
||
'eye_condition' => $this->request->post('eye_condition'),
|
||
'head_feeling' => $this->request->post('head_feeling'),
|
||
'sweat_condition' => $this->request->post('sweat_condition'),
|
||
'skin_condition' => $this->request->post('skin_condition'),
|
||
'urine_condition' => $this->request->post('urine_condition'),
|
||
'stool_condition' => $this->request->post('stool_condition'),
|
||
'kidney_condition' => $this->request->post('kidney_condition'),
|
||
'fatty_liver_degree' => $this->request->post('fatty_liver_degree'),
|
||
'past_history' => $this->request->post('past_history'),
|
||
'trauma_history' => $this->request->post('trauma_history'),
|
||
'surgery_history' => $this->request->post('surgery_history'),
|
||
'allergy_history' => $this->request->post('allergy_history'),
|
||
'family_history' => $this->request->post('family_history'),
|
||
'pregnancy_history' => $this->request->post('pregnancy_history'),
|
||
'symptoms' => $this->request->post('symptoms'),
|
||
'tongue_coating' => $this->request->post('tongue_coating'),
|
||
'tongue_images' => $this->request->post('tongue_images'),
|
||
'report_files' => $this->request->post('report_files'),
|
||
'pulse' => $this->request->post('pulse'),
|
||
'treatment_principle' => $this->request->post('treatment_principle'),
|
||
'prescription' => $this->request->post('prescription'),
|
||
'doctor_advice' => $this->request->post('doctor_advice'),
|
||
'remark' => $this->request->post('remark')
|
||
];
|
||
// patient_id 可选:不传则创建时自动生成(新患者首张就诊卡)
|
||
try {
|
||
$result = DiagnosisLogic::addCard($params);
|
||
if ($result === false) {
|
||
return $this->fail(DiagnosisLogic::getError());
|
||
}
|
||
return $this->success('创建成功', $result);
|
||
} catch (\Exception $e) {
|
||
return $this->fail($e->getMessage());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notes 更新就诊卡(供小程序调用)
|
||
* @return \think\response\Json
|
||
*/
|
||
public function updateCard()
|
||
{
|
||
$params = [
|
||
'id' => $this->request->post('id'),
|
||
'patient_id' => $this->request->post('patient_id'),
|
||
'patient_name' => $this->request->post('patient_name'),
|
||
'gender' => $this->request->post('gender'),
|
||
'age' => $this->request->post('age'),
|
||
'id_card' => $this->request->post('id_card'),
|
||
// 生命体征
|
||
'height' => $this->request->post('height'),
|
||
'weight' => $this->request->post('weight'),
|
||
'systolic_pressure' => $this->request->post('systolic_pressure'),
|
||
'diastolic_pressure' => $this->request->post('diastolic_pressure'),
|
||
'fasting_blood_sugar' => $this->request->post('fasting_blood_sugar'),
|
||
// 主诉
|
||
'diabetes_discovery_year' => $this->request->post('diabetes_discovery_year'),
|
||
'local_hospital_diagnosis' => $this->request->post('local_hospital_diagnosis'),
|
||
'local_hospital_name' => $this->request->post('local_hospital_name'),
|
||
'local_hospital_visit_date' => $this->request->post('local_hospital_visit_date'),
|
||
// 诊断信息
|
||
'diagnosis_date' => $this->request->post('diagnosis_date'),
|
||
'diagnosis_type' => $this->request->post('diagnosis_type'),
|
||
'syndrome_type' => $this->request->post('syndrome_type'),
|
||
'diabetes_type' => $this->request->post('diabetes_type'),
|
||
// 现病史字段
|
||
'appetite' => $this->request->post('appetite'),
|
||
'water_intake' => $this->request->post('water_intake'),
|
||
'diet_condition' => $this->request->post('diet_condition'),
|
||
'weight_change' => $this->request->post('weight_change'),
|
||
'body_feeling' => $this->request->post('body_feeling'),
|
||
'sleep_condition' => $this->request->post('sleep_condition'),
|
||
'eye_condition' => $this->request->post('eye_condition'),
|
||
'head_feeling' => $this->request->post('head_feeling'),
|
||
'sweat_condition' => $this->request->post('sweat_condition'),
|
||
'skin_condition' => $this->request->post('skin_condition'),
|
||
'urine_condition' => $this->request->post('urine_condition'),
|
||
'stool_condition' => $this->request->post('stool_condition'),
|
||
'kidney_condition' => $this->request->post('kidney_condition'),
|
||
'fatty_liver_degree' => $this->request->post('fatty_liver_degree'),
|
||
// 既往史字段
|
||
'past_history' => $this->request->post('past_history'),
|
||
'trauma_history' => $this->request->post('trauma_history'),
|
||
'surgery_history' => $this->request->post('surgery_history'),
|
||
'allergy_history' => $this->request->post('allergy_history'),
|
||
'family_history' => $this->request->post('family_history'),
|
||
'pregnancy_history' => $this->request->post('pregnancy_history'),
|
||
// 临床信息
|
||
'symptoms' => $this->request->post('symptoms'),
|
||
'tongue_coating' => $this->request->post('tongue_coating'),
|
||
'tongue_images' => $this->request->post('tongue_images'),
|
||
'report_files' => $this->request->post('report_files'),
|
||
'pulse' => $this->request->post('pulse'),
|
||
'treatment_principle' => $this->request->post('treatment_principle'),
|
||
'prescription' => $this->request->post('prescription'),
|
||
'doctor_advice' => $this->request->post('doctor_advice'),
|
||
'remark' => $this->request->post('remark')
|
||
];
|
||
|
||
if (!$params['id']) {
|
||
return $this->fail('诊单ID不能为空');
|
||
}
|
||
|
||
if (!$params['patient_id']) {
|
||
return $this->fail('患者ID不能为空');
|
||
}
|
||
|
||
try {
|
||
$result = DiagnosisLogic::updateCard($params);
|
||
|
||
if ($result === false) {
|
||
return $this->fail(DiagnosisLogic::getError());
|
||
}
|
||
|
||
return $this->success('更新成功');
|
||
} catch (\Exception $e) {
|
||
return $this->fail($e->getMessage());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @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', '');
|
||
|
||
$check = $this->ensurePatientOwnsDiagnosis($diagnosisId);
|
||
if (!$check['ok']) {
|
||
return $this->fail($check['error']);
|
||
}
|
||
$diagnosis = $check['diagnosis'];
|
||
|
||
try {
|
||
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);
|
||
|
||
$check = $this->ensurePatientOwnsDiagnosis($diagnosisId);
|
||
if (!$check['ok']) {
|
||
return $this->fail($check['error']);
|
||
}
|
||
$diagnosis = $check['diagnosis'];
|
||
|
||
try {
|
||
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());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 解析小程序 form-urlencoded 提交的 JSON 字段
|
||
*
|
||
* @param mixed $raw
|
||
* @param string $arrayKey ThinkPHP 数组字段名,如 badges/a
|
||
* @return array
|
||
*/
|
||
private function decodeJsonPostField($raw, string $arrayKey): array
|
||
{
|
||
if (is_string($raw) && $raw !== '') {
|
||
$decoded = json_decode($raw, true);
|
||
if (is_array($decoded)) {
|
||
return $decoded;
|
||
}
|
||
}
|
||
if (is_array($raw)) {
|
||
return $raw;
|
||
}
|
||
$arr = $this->request->post($arrayKey, []);
|
||
return is_array($arr) ? $arr : [];
|
||
}
|
||
|
||
/**
|
||
* @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 获取稳糖乐园状态(积分、今日任务、可领取积分)
|
||
*
|
||
* 路由:GET /api/tcm/dailyGetGamify?diagnosis_id=:id
|
||
*
|
||
* @return \think\response\Json
|
||
*/
|
||
public function dailyGetGamify()
|
||
{
|
||
$diagnosisId = (int) $this->request->get('diagnosis_id', 0);
|
||
$result = DailyGamifyLogic::getState((int) $this->userId, $diagnosisId);
|
||
if ($result === false) {
|
||
return $this->fail(DailyGamifyLogic::getError());
|
||
}
|
||
return $this->data($result);
|
||
}
|
||
|
||
/**
|
||
* @notes 给小树浇水(领取今日已完成任务的积分,服务端校验)
|
||
*
|
||
* 路由:POST /api/tcm/dailyWaterTree
|
||
* 必填:diagnosis_id
|
||
*
|
||
* @return \think\response\Json
|
||
*/
|
||
public function dailyWaterTree()
|
||
{
|
||
$diagnosisId = (int) $this->request->post('diagnosis_id', 0);
|
||
$result = DailyGamifyLogic::waterTree((int) $this->userId, $diagnosisId);
|
||
if ($result === false) {
|
||
return $this->fail(DailyGamifyLogic::getError());
|
||
}
|
||
// 统一走 success,保证 msg 与 data 同时返回,前端可提示且刷新状态
|
||
return $this->success($result['message'] ?? '操作成功', $result, 1, 1);
|
||
}
|
||
|
||
/**
|
||
* @notes 日常记录-获取今日饮食记录
|
||
*
|
||
* 路由:GET /api/tcm/dailyTodayDietRecord?diagnosis_id=:id
|
||
*/
|
||
public function dailyTodayDietRecord()
|
||
{
|
||
$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 = DietRecord::where('diagnosis_id', $diagnosisId)
|
||
->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/dailySaveDietRecord
|
||
*/
|
||
public function dailySaveDietRecord()
|
||
{
|
||
$diagnosisId = (int) $this->request->post('diagnosis_id', 0);
|
||
$check = $this->ensurePatientOwnsDiagnosis($diagnosisId);
|
||
if (!$check['ok']) {
|
||
return $this->fail($check['error']);
|
||
}
|
||
$diagnosis = $check['diagnosis'];
|
||
|
||
$breakfast = trim((string) $this->request->post('breakfast_foods', ''));
|
||
$lunch = trim((string) $this->request->post('lunch_foods', ''));
|
||
$dinner = trim((string) $this->request->post('dinner_foods', ''));
|
||
$note = trim((string) $this->request->post('note', ''));
|
||
|
||
if ($breakfast === '' && $lunch === '' && $dinner === '') {
|
||
return $this->fail('请至少填写一餐饮食');
|
||
}
|
||
|
||
try {
|
||
$todayStart = strtotime(date('Y-m-d 00:00:00'));
|
||
$todayEnd = strtotime(date('Y-m-d 23:59:59'));
|
||
$existing = DietRecord::where('diagnosis_id', $diagnosisId)
|
||
->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,
|
||
'breakfast_foods' => $breakfast,
|
||
'lunch_foods' => $lunch,
|
||
'dinner_foods' => $dinner,
|
||
'note' => $note,
|
||
];
|
||
|
||
if ($existing) {
|
||
$payload['id'] = $existing['id'];
|
||
DietRecord::update($payload);
|
||
return $this->success('已更新今日饮食', ['id' => $existing['id']]);
|
||
}
|
||
|
||
$created = DietRecord::create($payload);
|
||
return $this->success('饮食记录已保存', ['id' => $created->id]);
|
||
} catch (\Exception $e) {
|
||
return $this->fail($e->getMessage());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notes 日常记录-获取今日运动记录
|
||
*
|
||
* 路由:GET /api/tcm/dailyTodayExerciseRecord?diagnosis_id=:id
|
||
*/
|
||
public function dailyTodayExerciseRecord()
|
||
{
|
||
$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 = ExerciseRecord::where('diagnosis_id', $diagnosisId)
|
||
->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/dailySaveExerciseRecord
|
||
*/
|
||
public function dailySaveExerciseRecord()
|
||
{
|
||
$diagnosisId = (int) $this->request->post('diagnosis_id', 0);
|
||
$check = $this->ensurePatientOwnsDiagnosis($diagnosisId);
|
||
if (!$check['ok']) {
|
||
return $this->fail($check['error']);
|
||
}
|
||
$diagnosis = $check['diagnosis'];
|
||
|
||
$exerciseType = trim((string) $this->request->post('exercise_type', ''));
|
||
$durationRaw = $this->request->post('duration', '');
|
||
$intensity = (int) $this->request->post('intensity', 2);
|
||
$note = trim((string) $this->request->post('note', ''));
|
||
|
||
$duration = null;
|
||
if ($durationRaw !== '' && $durationRaw !== null && is_numeric($durationRaw)) {
|
||
$d = (int) $durationRaw;
|
||
if ($d > 0) {
|
||
$duration = $d;
|
||
}
|
||
}
|
||
|
||
if ($exerciseType === '' && $duration === null) {
|
||
return $this->fail('请填写运动类型或时长');
|
||
}
|
||
|
||
if ($intensity < 1 || $intensity > 3) {
|
||
$intensity = 2;
|
||
}
|
||
|
||
try {
|
||
$todayStart = strtotime(date('Y-m-d 00:00:00'));
|
||
$todayEnd = strtotime(date('Y-m-d 23:59:59'));
|
||
$existing = ExerciseRecord::where('diagnosis_id', $diagnosisId)
|
||
->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,
|
||
'exercise_type' => $exerciseType,
|
||
'duration' => $duration,
|
||
'intensity' => $intensity,
|
||
'note' => $note,
|
||
];
|
||
|
||
if ($existing) {
|
||
$payload['id'] = $existing['id'];
|
||
ExerciseRecord::update($payload);
|
||
return $this->success('已更新今日运动', ['id' => $existing['id']]);
|
||
}
|
||
|
||
$created = ExerciseRecord::create($payload);
|
||
return $this->success('运动记录已保存', ['id' => $created->id]);
|
||
} catch (\Exception $e) {
|
||
return $this->fail($e->getMessage());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notes 保存稳糖分 / 勋章 / 任务领奖状态
|
||
*
|
||
* 路由:POST /api/tcm/dailySaveGamify
|
||
* 参数:diagnosis_id, points, badges(数组), task_awards(对象)
|
||
*
|
||
* @return \think\response\Json
|
||
*/
|
||
public function dailySaveGamify()
|
||
{
|
||
$diagnosisId = (int) $this->request->post('diagnosis_id', 0);
|
||
$points = (int) $this->request->post('points', 0);
|
||
$badges = $this->decodeJsonPostField($this->request->post('badges', ''), 'badges/a');
|
||
$taskAwards = $this->decodeJsonPostField($this->request->post('task_awards', ''), 'task_awards/a');
|
||
|
||
$result = DailyGamifyLogic::saveState((int) $this->userId, $diagnosisId, $points, $badges, $taskAwards);
|
||
if ($result === false) {
|
||
return $this->fail(DailyGamifyLogic::getError());
|
||
}
|
||
return $this->data($result);
|
||
}
|
||
|
||
/**
|
||
* @notes 生成日常记录分享邀请码(仅当天有效)
|
||
*
|
||
* 路由:POST /api/tcm/dailyCreateShareInvite
|
||
* 必填:diagnosis_id
|
||
*
|
||
* @return \think\response\Json
|
||
*/
|
||
public function dailyCreateShareInvite()
|
||
{
|
||
$diagnosisId = (int) $this->request->post('diagnosis_id', 0);
|
||
$result = DailyShareLogic::createInvite((int) $this->userId, $diagnosisId);
|
||
if ($result === false) {
|
||
return $this->fail(DailyShareLogic::getError());
|
||
}
|
||
return $this->data($result);
|
||
}
|
||
|
||
/**
|
||
* @notes 凭邀请码查看分享战报(无需登录,含近7日血糖/血压记录)
|
||
*
|
||
* 路由:GET /api/tcm/dailySharePreview?invite_code=XXXXXXXX
|
||
*
|
||
* @return \think\response\Json
|
||
*/
|
||
public function dailySharePreview()
|
||
{
|
||
$inviteCode = (string) $this->request->get('invite_code', '');
|
||
$viewerKey = (string) $this->request->get('viewer_key', '');
|
||
$result = DailyShareLogic::previewByInviteCode($inviteCode, $viewerKey);
|
||
if ($result === false) {
|
||
return $this->fail(DailyShareLogic::getError());
|
||
}
|
||
return $this->data($result);
|
||
}
|
||
|
||
/**
|
||
* @notes 家人点赞(邀请观看页,无需登录)
|
||
*
|
||
* 路由:POST /api/tcm/dailyFamilyLike
|
||
* 必填:invite_code, viewer_key
|
||
* 可选:nickname(家人/亲友/老伴 等)
|
||
*
|
||
* @return \think\response\Json
|
||
*/
|
||
public function dailyFamilyLike()
|
||
{
|
||
$inviteCode = (string) $this->request->post('invite_code', '');
|
||
$viewerKey = (string) $this->request->post('viewer_key', '');
|
||
$nickname = (string) $this->request->post('nickname', '');
|
||
$result = DailyFamilyLikeLogic::addLike($inviteCode, $viewerKey, $nickname);
|
||
if ($result === false) {
|
||
return $this->fail(DailyFamilyLikeLogic::getError());
|
||
}
|
||
return $this->data($result);
|
||
}
|
||
|
||
/**
|
||
* @notes 患者查看今日家人点赞汇总
|
||
*
|
||
* 路由:GET /api/tcm/dailyFamilyLikeSummary?diagnosis_id=
|
||
*
|
||
* @return \think\response\Json
|
||
*/
|
||
public function dailyFamilyLikeSummary()
|
||
{
|
||
$diagnosisId = (int) $this->request->get('diagnosis_id', 0);
|
||
$result = DailyFamilyLikeLogic::summaryForPatient((int) $this->userId, $diagnosisId);
|
||
if ($result === false) {
|
||
return $this->fail(DailyFamilyLikeLogic::getError());
|
||
}
|
||
return $this->data($result);
|
||
}
|
||
|
||
/**
|
||
* @notes 根据订单号获取订单详情(供小程序支付页调用)
|
||
* @return \think\response\Json
|
||
*/
|
||
public function getOrderByNo()
|
||
{
|
||
$orderNo = $this->request->get('order_no', '');
|
||
if (empty($orderNo)) {
|
||
return $this->fail('订单号不能为空');
|
||
}
|
||
|
||
try {
|
||
$order = \app\common\model\Order::where('order_no', $orderNo)->find();
|
||
if (!$order) {
|
||
return $this->fail('订单不存在');
|
||
}
|
||
|
||
$typeMap = [1 => '挂号费', 2 => '问诊费', 3 => '药品费用'];
|
||
$statusMap = [1 => '待支付', 2 => '已支付', 3 => '已取消', 4 => '已退款'];
|
||
|
||
return $this->data([
|
||
'id' => $order['id'],
|
||
'order_no' => $order['order_no'],
|
||
'order_type' => $order['order_type'],
|
||
'order_type_desc' => $typeMap[$order['order_type']] ?? '未知',
|
||
'amount' => $order['amount'],
|
||
'status' => $order['status'],
|
||
'status_desc' => $statusMap[$order['status']] ?? '未知',
|
||
'remark' => $order['remark'] ?? '',
|
||
'create_time' => $order['create_time'],
|
||
]);
|
||
} catch (\Exception $e) {
|
||
return $this->fail($e->getMessage());
|
||
}
|
||
}
|
||
}
|