更新
This commit is contained in:
@@ -457,6 +457,10 @@ class TcmController extends BaseApiController
|
||||
return ['ok' => false, 'error' => '诊单不存在'];
|
||||
}
|
||||
|
||||
if ((int) ($diagnosis['show_card'] ?? 1) !== 1) {
|
||||
return ['ok' => false, 'error' => '该就诊卡已在统计端隐藏'];
|
||||
}
|
||||
|
||||
// 通过 diagnosis_view_records 校验当前小程序用户与该诊单的归属关系
|
||||
// 该表是「就诊卡列表」的来源(参考 DiagnosisLogic::getCardList)
|
||||
$owned = \think\facade\Db::name('diagnosis_view_records')
|
||||
|
||||
@@ -499,8 +499,10 @@ class DailyDietAiLogic
|
||||
3. 严禁:白馒头、油条、粘豆包、糯米饭、西瓜、含糖饮料、白稀饭(杂面窝头、玉米碴粥除外)。
|
||||
4. 低 GI 优先;农村家常;禁止轻食/沙拉/藜麦等词。
|
||||
5. lunch 字段写法:蛋白菜放前面,淀粉(如有)放最后且注明「小半碗/一个」。
|
||||
6. 只输出 JSON:
|
||||
{"breakfast":"...","lunch":"...","dinner":"...","tips":"...","avoid":["...","..."]}
|
||||
6. 早餐必须包含「驼奶粉」(温水冲服,适量),可与粥、蛋、菜并列写。
|
||||
7. drinks 单独写全天喝什么:温开水为主;可写驼奶粉冲饮时间;严禁含糖饮料、果汁、酒。
|
||||
8. 只输出 JSON:
|
||||
{"breakfast":"...","drinks":"...","lunch":"...","dinner":"...","tips":"...","avoid":["...","..."]}
|
||||
SYS;
|
||||
|
||||
$user = sprintf(
|
||||
@@ -544,9 +546,11 @@ SYS;
|
||||
2. 淀粉(粥/饭/面/饼/窝头/薯/南瓜)每餐最多 1 种,早中晚互不相同。
|
||||
3. 严禁白馒头、油条、粘豆包、糯米饭、西瓜、含糖饮料。
|
||||
4. 农村家常饭,禁止轻食/沙拉/藜麦。
|
||||
5. 早餐必须含驼奶粉(温水冲服);喝的单独一行。
|
||||
|
||||
严格按下面 5 行格式输出,不要 JSON、不要 markdown、不要多余解释:
|
||||
早餐:(具体食物)
|
||||
严格按下面 6 行格式输出,不要 JSON、不要 markdown、不要多余解释:
|
||||
早餐:(具体食物,须含驼奶粉)
|
||||
喝的:(温开水、驼奶粉冲饮安排等,忌甜饮)
|
||||
午餐:(具体食物,蛋白放前)
|
||||
晚餐:(具体食物)
|
||||
提示:(一句土话提醒)
|
||||
@@ -593,6 +597,7 @@ SYS;
|
||||
|
||||
$map = [
|
||||
'breakfast' => null,
|
||||
'drinks' => null,
|
||||
'lunch' => null,
|
||||
'dinner' => null,
|
||||
'tips' => null,
|
||||
@@ -602,6 +607,9 @@ SYS;
|
||||
if (preg_match('/早餐[::]\s*(.+)$/mu', $text, $m)) {
|
||||
$map['breakfast'] = trim(preg_split('/\R/u', $m[1])[0] ?? $m[1]);
|
||||
}
|
||||
if (preg_match('/喝的[::]\s*(.+)$/mu', $text, $m)) {
|
||||
$map['drinks'] = trim(preg_split('/\R/u', $m[1])[0] ?? $m[1]);
|
||||
}
|
||||
if (preg_match('/午餐[::]\s*(.+)$/mu', $text, $m)) {
|
||||
$map['lunch'] = trim(preg_split('/\R/u', $m[1])[0] ?? $m[1]);
|
||||
}
|
||||
@@ -623,6 +631,7 @@ SYS;
|
||||
|
||||
$out = [
|
||||
'breakfast' => (string) $map['breakfast'],
|
||||
'drinks' => (string) ($map['drinks'] ?? ''),
|
||||
'lunch' => (string) ($map['lunch'] ?? ''),
|
||||
'dinner' => (string) ($map['dinner'] ?? ''),
|
||||
'tips' => (string) ($map['tips'] ?? ''),
|
||||
@@ -632,6 +641,29 @@ SYS;
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 产品要求:早餐含驼奶粉,并单独给出「喝的」
|
||||
*
|
||||
* @param array<string, mixed> $plan
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function applyDietProductDefaults(array $plan): array
|
||||
{
|
||||
$breakfast = trim((string) ($plan['breakfast'] ?? ''));
|
||||
if ($breakfast !== '' && mb_strpos($breakfast, '驼奶粉') === false && mb_strpos($breakfast, '驼奶') === false) {
|
||||
$plan['breakfast'] = '驼奶粉(温水冲服)、' . $breakfast;
|
||||
}
|
||||
|
||||
$drinks = trim((string) ($plan['drinks'] ?? ''));
|
||||
if ($drinks === '') {
|
||||
$plan['drinks'] = '白天多喝温开水;早餐按量冲驼奶粉,别喝甜饮料、果汁。';
|
||||
} elseif (mb_strpos($drinks, '驼奶') === false) {
|
||||
$plan['drinks'] = $drinks . ';早餐已安排驼奶粉';
|
||||
}
|
||||
|
||||
return $plan;
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化 AI 三餐方案;校验未过也保留 AI 文案(避免流式展示后被规则模板覆盖)
|
||||
*
|
||||
@@ -640,6 +672,7 @@ SYS;
|
||||
*/
|
||||
private static function finalizeAiRecommendPlan(array $parsed): array
|
||||
{
|
||||
$parsed = self::applyDietProductDefaults($parsed);
|
||||
$checked = DietMealValidator::validateAndNormalize($parsed);
|
||||
$data = $checked['plan'];
|
||||
$data['source'] = 'ai';
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace app\api\logic\tcm;
|
||||
|
||||
use app\common\model\tcm\DailyFamilyLike;
|
||||
use app\common\model\tcm\DailyShareInvite;
|
||||
use app\common\model\tcm\Diagnosis;
|
||||
|
||||
/**
|
||||
* 家人点赞(邀请观看页 → 患者日常页展示)
|
||||
@@ -204,6 +205,11 @@ class DailyFamilyLikeLogic
|
||||
return self::setError('无权查看该诊单') ? false : false;
|
||||
}
|
||||
|
||||
$diagnosis = Diagnosis::where('id', $diagnosisId)->where('delete_time', null)->field('show_card')->find();
|
||||
if (!$diagnosis || (int) ($diagnosis['show_card'] ?? 1) !== 1) {
|
||||
return self::setError('该就诊卡已在统计端隐藏') ? false : false;
|
||||
}
|
||||
|
||||
$likeDate = date('Y-m-d');
|
||||
$count = self::countToday($diagnosisId, $likeDate);
|
||||
|
||||
|
||||
@@ -49,6 +49,13 @@ class DailyGamifyLogic
|
||||
if (!$owned) {
|
||||
return self::setError('无权操作该诊单') ? false : false;
|
||||
}
|
||||
$diagnosis = \app\common\model\tcm\Diagnosis::where('id', $diagnosisId)
|
||||
->where('delete_time', null)
|
||||
->field('show_card')
|
||||
->find();
|
||||
if (!$diagnosis || (int) ($diagnosis['show_card'] ?? 1) !== 1) {
|
||||
return self::setError('该就诊卡已在统计端隐藏') ? false : false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
namespace app\api\logic\tcm;
|
||||
|
||||
use app\adminapi\logic\tcm\DiagnosisLogic;
|
||||
use app\common\model\DiagnosisViewRecord;
|
||||
use app\common\model\tcm\Diagnosis;
|
||||
use app\common\model\user\User;
|
||||
|
||||
/**
|
||||
@@ -74,19 +76,38 @@ class DailyPhoneLogic
|
||||
|
||||
if (!empty($cardList)) {
|
||||
$card = $cardList[0];
|
||||
return self::buildContextOk($card, $mobile, false, false);
|
||||
}
|
||||
|
||||
// 该手机号已在诊单库建档:关联查看记录,禁止重复创建轻量档案
|
||||
$existing = Diagnosis::where('phone', $mobile)
|
||||
->where('delete_time', null)
|
||||
->order('id', 'desc')
|
||||
->find();
|
||||
if ($existing && (int) ($existing['show_card'] ?? 1) !== 1) {
|
||||
return [
|
||||
'ok' => true,
|
||||
'ok' => false,
|
||||
'need_mobile' => false,
|
||||
'created' => false,
|
||||
'diagnosis_id' => (int) $card['id'],
|
||||
'patient_id' => (int) $card['patient_id'],
|
||||
'patient_name' => (string) ($card['patient_name'] ?? ''),
|
||||
'gender' => (int) ($card['gender'] ?? 0),
|
||||
'age' => (int) ($card['age'] ?? 0),
|
||||
'mobile' => $mobile,
|
||||
'create_source' => (string) ($card['create_source'] ?? ''),
|
||||
'error' => '该手机号对应就诊卡因隐私设置已隐藏,请联系医生',
|
||||
];
|
||||
}
|
||||
if ($existing) {
|
||||
if (!self::ensureUserViewRecord($userId, (int) $existing['id'], (int) $existing['patient_id'])) {
|
||||
return [
|
||||
'ok' => false,
|
||||
'need_mobile' => false,
|
||||
'error' => '该手机号已建档,暂无法关联到当前账号',
|
||||
];
|
||||
}
|
||||
return self::buildContextOk([
|
||||
'id' => (int) $existing['id'],
|
||||
'patient_id' => (int) $existing['patient_id'],
|
||||
'patient_name' => (string) ($existing['patient_name'] ?? ''),
|
||||
'gender' => (int) ($existing['gender'] ?? 0),
|
||||
'age' => (int) ($existing['age'] ?? 0),
|
||||
'create_source' => (string) ($existing['create_source'] ?? ''),
|
||||
], $mobile, false, true);
|
||||
}
|
||||
|
||||
$displayName = trim((string) (($user['real_name'] ?? '') ?: ($user['nickname'] ?? '')));
|
||||
if ($displayName === '') {
|
||||
@@ -114,17 +135,66 @@ class DailyPhoneLogic
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'need_mobile' => false,
|
||||
'created' => true,
|
||||
'diagnosis_id' => (int) $result['id'],
|
||||
return self::buildContextOk([
|
||||
'id' => (int) $result['id'],
|
||||
'patient_id' => (int) $result['patient_id'],
|
||||
'patient_name' => $displayName,
|
||||
'gender' => $gender,
|
||||
'age' => max(0, (int) ($user['age'] ?? 0)),
|
||||
'mobile' => $mobile,
|
||||
'create_source' => self::CREATE_SOURCE_MNP_DAILY,
|
||||
], $mobile, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{id:int,patient_id:int,patient_name?:string,gender?:int,age?:int,create_source?:string} $card
|
||||
*/
|
||||
private static function buildContextOk(array $card, string $mobile, bool $created, bool $linkedExisting): array
|
||||
{
|
||||
return [
|
||||
'ok' => true,
|
||||
'need_mobile' => false,
|
||||
'created' => $created,
|
||||
'linked_existing' => $linkedExisting,
|
||||
'diagnosis_id' => (int) $card['id'],
|
||||
'patient_id' => (int) $card['patient_id'],
|
||||
'patient_name' => (string) ($card['patient_name'] ?? ''),
|
||||
'gender' => (int) ($card['gender'] ?? 0),
|
||||
'age' => (int) ($card['age'] ?? 0),
|
||||
'mobile' => $mobile,
|
||||
'create_source' => (string) ($card['create_source'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
/** 将已有诊单挂到当前小程序用户(diagnosis_view_records) */
|
||||
private static function ensureUserViewRecord(int $userId, int $diagnosisId, int $patientId): bool
|
||||
{
|
||||
if ($userId <= 0 || $diagnosisId <= 0) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$exists = DiagnosisViewRecord::where('user_id', $userId)
|
||||
->where('diagnosis_id', $diagnosisId)
|
||||
->where('delete_time', null)
|
||||
->find();
|
||||
if ($exists) {
|
||||
return true;
|
||||
}
|
||||
$now = time();
|
||||
DiagnosisViewRecord::create([
|
||||
'user_id' => $userId,
|
||||
'diagnosis_id' => $diagnosisId,
|
||||
'patient_id' => $patientId,
|
||||
'share_user_id' => 0,
|
||||
'view_count' => 1,
|
||||
'first_view_time' => $now,
|
||||
'last_view_time' => $now,
|
||||
'is_confirmed' => 0,
|
||||
'create_time' => $now,
|
||||
'update_time' => $now,
|
||||
]);
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,9 @@ class DailyShareLogic
|
||||
if (!$diagnosis) {
|
||||
return self::setError('诊单不存在') ? false : false;
|
||||
}
|
||||
if ((int) ($diagnosis['show_card'] ?? 1) !== 1) {
|
||||
return self::setError('该就诊卡已在统计端隐藏') ? false : false;
|
||||
}
|
||||
|
||||
$inviteDate = date('Y-m-d');
|
||||
$code = self::generateUniqueCode();
|
||||
|
||||
@@ -114,6 +114,7 @@ class DietMealValidator
|
||||
|
||||
$normalized = [
|
||||
'breakfast' => $breakfast,
|
||||
'drinks' => trim((string) ($plan['drinks'] ?? '')),
|
||||
'lunch' => $lunch,
|
||||
'dinner' => $dinner,
|
||||
'tips' => $tips,
|
||||
|
||||
@@ -18,7 +18,7 @@ class GiKnowledge
|
||||
'蔬菜类' => ['魔芋', '粟米', '大白菜', '黄瓜', '芹菜', '茄子', '青椒', '海带', '金针菇', '香菇', '菠菜', '番茄', '豆芽', '芦笋', '花椰菜', '洋葱', '生菜'],
|
||||
'豆类' => ['黄豆', '眉豆', '鸡心豆', '豆腐', '豆角', '绿豆', '扁豆', '四季豆'],
|
||||
'水果类' => ['苹果', '水梨', '橙子', '桃', '提子', '沙田柚', '雪梨', '车厘子', '柚子', '草莓', '樱桃', '金桔', '葡萄'],
|
||||
'奶类' => ['牛奶', '低脂奶', '脱脂奶', '低脂乳酪', '红茶', '优格', '无糖豆浆'],
|
||||
'奶类' => ['牛奶', '低脂奶', '脱脂奶', '驼奶粉', '驼奶', '低脂乳酪', '红茶', '优格', '无糖豆浆'],
|
||||
],
|
||||
self::LEVEL_MEDIUM => [
|
||||
'主食类' => ['红米饭', '糙米饭', '西米', '乌冬面', '面包', '麦片', '番薯', '芋头'],
|
||||
@@ -36,40 +36,46 @@ class GiKnowledge
|
||||
],
|
||||
];
|
||||
|
||||
/** @var list<array{breakfast:string,lunch:string,dinner:string,tips:string}> */
|
||||
/** @var list<array{breakfast:string,drinks:string,lunch:string,dinner:string,tips:string}> */
|
||||
private static array $mealTemplates = [
|
||||
[
|
||||
'breakfast' => '玉米碴子粥、煮鸡蛋、拌黄瓜',
|
||||
'breakfast' => '驼奶粉(温水冲服)、玉米碴子粥、煮鸡蛋、拌黄瓜',
|
||||
'drinks' => '白天多喝温开水;早餐冲驼奶粉,别喝甜饮料、果汁。',
|
||||
'lunch' => '清炖鲤鱼、豆腐炖白菜、清炒豆角、米饭小半碗',
|
||||
'dinner' => '玉米面饼一个、瘦肉丝、凉拌茄子',
|
||||
'tips' => '中午鱼豆肉吃足;早中晚淀粉别重复,一顿一种主食就够。',
|
||||
],
|
||||
[
|
||||
'breakfast' => '小米粥、煮鸡蛋、小碟咸菜(少盐)',
|
||||
'breakfast' => '驼奶粉(温水冲服)、小米粥、煮鸡蛋、小碟咸菜(少盐)',
|
||||
'drinks' => '温开水为主;驼奶粉跟早餐一起吃,下午也记得喝水。',
|
||||
'lunch' => '去皮炖鸡肉、芹菜炒豆干、木耳炒鸡蛋',
|
||||
'dinner' => '蒸红薯(小半块)、清炒菠菜、豆腐汤',
|
||||
'tips' => '午餐最讲究蛋白;晚上才吃薯,别跟中午再配大米饭。',
|
||||
],
|
||||
[
|
||||
'breakfast' => '豆腐脑(少卤)、鸡蛋、一个苹果',
|
||||
'breakfast' => '驼奶粉(温水冲服)、豆腐脑(少卤)、煮鸡蛋',
|
||||
'drinks' => '早餐饮驼奶粉;白天喝白开水,别碰含糖饮料。',
|
||||
'lunch' => '清炖鸡肉、炖芸豆、番茄炒蛋、贴饼子一个',
|
||||
'dinner' => '挂面一小碗、清蒸小鱼、拌海带丝',
|
||||
'tips' => '中午饼配肉豆,蛋白管够;面条放晚上,别三顿都是粥饭。',
|
||||
],
|
||||
[
|
||||
'breakfast' => '高粱米粥、茶叶蛋、拌萝卜丝',
|
||||
'breakfast' => '驼奶粉(温水冲服)、高粱米粥、茶叶蛋、拌萝卜丝',
|
||||
'drinks' => '温开水;早餐驼奶粉按说明冲,别加糖。',
|
||||
'lunch' => '炖牛肉(瘦)、豆腐炒韭菜、清炒油菜、杂豆饭小半碗',
|
||||
'dinner' => '蒸南瓜(小半碗)、鸡蛋羹、拍黄瓜',
|
||||
'tips' => '中午肉蛋豆要多吃;南瓜算晚饭主食,别再加馒头稀饭。',
|
||||
],
|
||||
[
|
||||
'breakfast' => '棒子面粥、水煮蛋、大拌菜(少油)',
|
||||
'breakfast' => '驼奶粉(温水冲服)、棒子面粥、水煮蛋、大拌菜(少油)',
|
||||
'drinks' => '多喝温开水;驼奶粉放早餐,忌可乐汽水果汁。',
|
||||
'lunch' => '清炖鲫鱼、番茄炒蛋、炖扁豆、糙米饭小半碗',
|
||||
'dinner' => '玉米面饼一个、清炒豆芽、豆腐脑',
|
||||
'tips' => '午饭鱼蛋豆齐上;玉米面放晚上,跟中午米饭分开。',
|
||||
],
|
||||
[
|
||||
'breakfast' => '小米粥、煮鸡蛋、拌生菜',
|
||||
'breakfast' => '驼奶粉(温水冲服)、小米粥、煮鸡蛋、拌生菜',
|
||||
'drinks' => '白天小口多次喝温水;早餐冲好驼奶粉再吃饭。',
|
||||
'lunch' => '白切鸡(去皮)、炖豆腐、清炒豆角、二米饭小半碗',
|
||||
'dinner' => '杂面馒头一个、瘦肉炒芹菜、凉拌黄瓜',
|
||||
'tips' => '中午蛋白打主力;三顿别都喝粥吃面,一顿一种淀粉。',
|
||||
@@ -92,7 +98,8 @@ class GiKnowledge
|
||||
private static array $aliases = [
|
||||
'棒子面' => '荞麦面', '玉米碴' => '粟米', '地瓜' => '番薯', '红薯' => '番薯',
|
||||
'洋芋' => '芋头', '土豆' => '芋头', '窝头' => '通心粉', '贴饼子' => '通心粉',
|
||||
'二米饭' => '红米饭', '稀饭' => '白饭', '白稀饭' => '白饭', '糖糕' => '麦芽糖',
|
||||
'二米饭' => '红米饭', '稀饭' => '白饭', '白稀饭' => '白饭', '糖糕' => '麦芽糖',
|
||||
'驼奶' => '驼奶粉',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -132,6 +139,8 @@ class GiKnowledge
|
||||
public static function ruralMealHints(): string
|
||||
{
|
||||
return implode("\n", [
|
||||
'【早餐】须安排驼奶粉温水冲服(低 GI 奶类,稳血糖),可与粥、蛋、菜同餐。',
|
||||
'【喝的】单独说明:温开水为主;早餐驼奶粉;严禁含糖饮料、果汁、酒。',
|
||||
'【午餐】蛋白质要最高:至少 2 样高蛋白(鱼/鸡鸭/瘦肉/蛋/豆腐/豆干),肉蛋豆是午饭主角。',
|
||||
'【淀粉不重复】粥/饭/面/饼/窝头/红薯/土豆/玉米/南瓜等算淀粉;早中晚各最多 1 种,且三顿不能同一种、不要顿顿大主食。',
|
||||
'推荐蛋白:鸡蛋、豆腐、豆干、自家鸡鸭蛋、瘦肉、鲫鱼鲤鱼;',
|
||||
@@ -177,6 +186,7 @@ class GiKnowledge
|
||||
|
||||
$plan = [
|
||||
'breakfast' => $meal['breakfast'],
|
||||
'drinks' => $meal['drinks'] ?? '白天多喝温开水;早餐冲驼奶粉,别喝甜饮。',
|
||||
'lunch' => $meal['lunch'],
|
||||
'dinner' => $meal['dinner'],
|
||||
'tips' => $meal['tips'],
|
||||
|
||||
Reference in New Issue
Block a user