This commit is contained in:
Your Name
2026-05-27 15:50:54 +08:00
parent 8b0fcd7050
commit 19af50c344
16 changed files with 5372 additions and 684 deletions
+358 -14
View File
@@ -16,9 +16,14 @@ 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;
/**
* 中医诊断控制器(供小程序调用)
@@ -31,7 +36,7 @@ class TcmController extends BaseApiController
* @notes 不需要登录的方法
* @var array
*/
public array $notNeedLogin = ['getPatientSignature', 'diagnosisDetail', 'getDict', 'confirmDiagnosis', 'getCardList', 'getOrderByNo', 'patientHangupVideo', 'dailyTrackingWindow', 'dailyTrackingNotes'];
public array $notNeedLogin = ['getPatientSignature', 'diagnosisDetail', 'getDict', 'confirmDiagnosis', 'getCardList', 'getOrderByNo', 'patientHangupVideo', 'dailySharePreview', 'dailyFamilyLike'];
/**
* @notes 获取患者签名(供小程序调用)
@@ -350,16 +355,13 @@ class TcmController extends BaseApiController
$startDate = (string) $this->request->get('start_date', '');
$endDate = (string) $this->request->get('end_date', '');
if ($diagnosisId <= 0) {
return $this->fail('诊单ID不能为空');
$check = $this->ensurePatientOwnsDiagnosis($diagnosisId);
if (!$check['ok']) {
return $this->fail($check['error']);
}
$diagnosis = $check['diagnosis'];
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('无权查看该诊单的日常记录');
}
@@ -393,15 +395,13 @@ class TcmController extends BaseApiController
$diagnosisId = (int) $this->request->get('diagnosis_id', 0);
$patientId = (int) $this->request->get('patient_id', 0);
if ($diagnosisId <= 0) {
return $this->fail('诊单ID不能为空');
$check = $this->ensurePatientOwnsDiagnosis($diagnosisId);
if (!$check['ok']) {
return $this->fail($check['error']);
}
$diagnosis = $check['diagnosis'];
try {
$diagnosis = Diagnosis::where('id', $diagnosisId)->find();
if (!$diagnosis) {
return $this->fail('诊单不存在');
}
if ($patientId > 0 && (int) $diagnosis['patient_id'] !== $patientId) {
return $this->fail('无权查看该诊单的跟踪备注');
}
@@ -413,6 +413,28 @@ class TcmController extends BaseApiController
}
}
/**
* 解析小程序 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
@@ -630,6 +652,328 @@ class TcmController extends BaseApiController
}
}
/**
* @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