636 lines
22 KiB
PHP
636 lines
22 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace app\common\service\gancao;
|
||
|
||
use app\common\model\doctor\Medicine;
|
||
use think\facade\Cache;
|
||
use think\facade\Config;
|
||
use think\facade\Log;
|
||
|
||
/**
|
||
* 甘草 SCM 处方:MAKE_TOKEN、CTM_PREVIEW、CTM_SUBMIT_RECIPEL。
|
||
*/
|
||
final class GancaoScmRecipelService
|
||
{
|
||
private const CACHE_KEY = 'gancao_scm_api_token';
|
||
|
||
private static string $lastGetTokenError = '';
|
||
|
||
public static function getLastGetTokenError(): string
|
||
{
|
||
return self::$lastGetTokenError;
|
||
}
|
||
|
||
public static function isConfigured(): bool
|
||
{
|
||
return self::whyNotConfigured() === '';
|
||
}
|
||
|
||
/**
|
||
* 未就绪时返回中文原因(多条用分号分隔),就绪返回空串。
|
||
*/
|
||
public static function whyNotConfigured(): string
|
||
{
|
||
$c = Config::get('gancao_scm', []);
|
||
if (empty($c['enabled'])) {
|
||
return '未启用:请在 .env 顶层或 [GANCAO_SCM] 中设置 GANCAO_SCM_ENABLED=true(勿写在 [trtc] 等分区内,否则会变成 TRTC_GANCAO_SCM_* 读不到)';
|
||
}
|
||
$need = ['gateway_url', 'gateway_ak', 'gateway_sk', 'biz_ak', 'biz_sk', 'callback_url'];
|
||
$labels = [
|
||
'gateway_url' => 'GANCAO_SCM_GATEWAY_URL',
|
||
'gateway_ak' => 'GANCAO_SCM_GATEWAY_AK',
|
||
'gateway_sk' => 'GANCAO_SCM_GATEWAY_SK',
|
||
'biz_ak' => 'GANCAO_SCM_BIZ_AK(可留空则与网关 AK 相同)',
|
||
'biz_sk' => 'GANCAO_SCM_BIZ_SK(可留空则与网关 SK 相同)',
|
||
'callback_url' => 'GANCAO_SCM_CALLBACK_URL(须 https,甘草订单状态回调)',
|
||
];
|
||
$miss = [];
|
||
foreach ($need as $k) {
|
||
if (trim((string) ($c[$k] ?? '')) === '') {
|
||
$miss[] = $labels[$k] ?? $k;
|
||
}
|
||
}
|
||
|
||
return $miss === [] ? '' : '缺少或未配置:' . implode(';', $miss);
|
||
}
|
||
|
||
public static function apiStatusMessage(?array $body): string
|
||
{
|
||
|
||
if (!is_array($body)) {
|
||
return '响应异常';
|
||
}
|
||
|
||
$code = (string) ($body['status']['code'] ?? '');
|
||
$msg = $body['status']['msg'] ?? '';
|
||
|
||
// 确保 msg 是字符串
|
||
if (is_array($msg)) {
|
||
$msg = json_encode($msg, JSON_UNESCAPED_UNICODE);
|
||
} else {
|
||
$msg = (string) $msg;
|
||
}
|
||
|
||
return $code !== '' ? "[{$code}] {$msg}" : ($msg !== '' ? $msg : '未知错误');
|
||
}
|
||
|
||
public static function isApiSuccess(?array $body): bool
|
||
{
|
||
return is_array($body) && (string) ($body['status']['code'] ?? '') === '00000';
|
||
}
|
||
|
||
public static function getToken(bool $forceRefresh = false): ?string
|
||
{
|
||
self::$lastGetTokenError = '';
|
||
if (!self::isConfigured()) {
|
||
self::$lastGetTokenError = self::whyNotConfigured();
|
||
|
||
return null;
|
||
}
|
||
|
||
if (!$forceRefresh) {
|
||
$cached = Cache::get(self::CACHE_KEY);
|
||
if (is_string($cached) && strlen($cached) >= 10) {
|
||
return $cached;
|
||
}
|
||
}
|
||
|
||
$c = Config::get('gancao_scm', []);
|
||
$transport = new GancaoOpenApiTransport(
|
||
(string) $c['gateway_url'],
|
||
(string) $c['gateway_ak'],
|
||
(string) $c['gateway_sk']
|
||
);
|
||
$ts = time();
|
||
$bizAk = (string) $c['biz_ak'];
|
||
$bizSk = (string) $c['biz_sk'];
|
||
$gwAk = (string) $c['gateway_ak'];
|
||
$gwSk = (string) $c['gateway_sk'];
|
||
$pwd = md5($ts . $bizSk);
|
||
$ret = $transport->post([
|
||
'ak' => $bizAk,
|
||
'timestamp' => $ts,
|
||
'pwd' => $pwd,
|
||
'package' => 'igc_scm.ops.api.auth',
|
||
'class' => 'MAKE_TOKEN',
|
||
]);
|
||
|
||
if ((int) ($ret['state'] ?? 0) !== 1) {
|
||
$hint = (string) ($ret['msg'] ?? '');
|
||
$tail = isset($ret['response']) ? mb_substr((string) $ret['response'], 0, 200) : '';
|
||
self::$lastGetTokenError = '甘草网关通信失败:' . $hint . ($tail !== '' ? ';响应片段:' . $tail : '');
|
||
Log::warning('Gancao MAKE_TOKEN transport failed', ['msg' => $hint, 'ret' => $ret]);
|
||
|
||
return null;
|
||
}
|
||
|
||
$body = $ret['body'] ?? [];
|
||
|
||
if (!self::isApiSuccess($body)) {
|
||
$apiMsg = self::apiStatusMessage($body);
|
||
$code = (string) ($body['status']['code'] ?? '');
|
||
self::$lastGetTokenError = 'MAKE_TOKEN 失败:' . $apiMsg;
|
||
|
||
// 确保 $apiMsg 是字符串,避免 Array to string conversion 错误
|
||
$apiMsgStr = is_string($apiMsg) ? $apiMsg : json_encode($apiMsg, JSON_UNESCAPED_UNICODE);
|
||
|
||
if ($code === '10103' || str_contains($apiMsgStr, '10103')) {
|
||
self::$lastGetTokenError .= '。多为「业务 ak/sk」与 pwd=md5(时间戳+业务sk) 不匹配:请在 .env 配置与网关 OpenAPI 不同的 GANCAO_SCM_BIZ_AK、GANCAO_SCM_BIZ_SK(甘草控制台「业务账号」)。若业务与网关确为同一套,再检查 BIZ 是否与网关一致。';
|
||
}
|
||
if ($code === '10101' || str_contains($apiMsgStr, '10101')) {
|
||
self::$lastGetTokenError .= '。请核对 GANCAO_SCM_GATEWAY_AK 与甘草分配的 OpenAPI 网关账号一致。';
|
||
}
|
||
if ($bizAk === $gwAk && $bizSk === $gwSk) {
|
||
self::$lastGetTokenError .= ' 当前 BIZ 与网关相同;若仍失败,请向甘草索取独立的业务层 ak/sk 并填入 GANCAO_SCM_BIZ_AK / GANCAO_SCM_BIZ_SK。';
|
||
}
|
||
Log::warning('Gancao MAKE_TOKEN api error', ['body' => $body, 'apiMsg' => $apiMsg]);
|
||
|
||
return null;
|
||
}
|
||
|
||
$token = (string) ($body['result']['token'] ?? '');
|
||
|
||
if ($token === '') {
|
||
self::$lastGetTokenError = 'MAKE_TOKEN 返回无 token 字段';
|
||
|
||
return null;
|
||
}
|
||
Cache::set(self::CACHE_KEY, $token, 50 * 60);
|
||
|
||
return $token;
|
||
}
|
||
|
||
/**
|
||
* 从医师药品库 `doctor_medicine`(name + gid)解析甘草药材 id,仅 status=1 且未删除。
|
||
*
|
||
* @param array<int, array<string,mixed>> $herbs 处方 herbs
|
||
* @return array<string, int> 药材名 => 甘草 id
|
||
*/
|
||
/**
|
||
* 手机号脱敏处理
|
||
*
|
||
* @param string $phone 手机号
|
||
* @return string 脱敏后的手机号(如:138****0000)
|
||
*/
|
||
public static function maskPhone(string $phone): string
|
||
{
|
||
$phone = trim($phone);
|
||
if (strlen($phone) !== 11) {
|
||
return $phone;
|
||
}
|
||
return substr($phone, 0, 3) . '****' . substr($phone, -4);
|
||
}
|
||
|
||
/**
|
||
* 脱敏数据用于日志记录
|
||
*
|
||
* @param array<string,mixed> $data 原始数据
|
||
* @return array<string,mixed> 脱敏后的数据
|
||
*/
|
||
public static function maskSensitiveData(array $data): array
|
||
{
|
||
$masked = $data;
|
||
|
||
// 脱敏手机号字段
|
||
$phoneFields = ['phone', 'recipient_phone', 'patient_phone', 'doctor_phone'];
|
||
foreach ($phoneFields as $field) {
|
||
if (isset($masked[$field]) && is_string($masked[$field])) {
|
||
$masked[$field] = self::maskPhone($masked[$field]);
|
||
}
|
||
}
|
||
|
||
// 递归处理嵌套数组
|
||
foreach ($masked as $key => $value) {
|
||
if (is_array($value)) {
|
||
$masked[$key] = self::maskSensitiveData($value);
|
||
}
|
||
}
|
||
|
||
return $masked;
|
||
}
|
||
|
||
public static function doctorMedicineGidMapForHerbs(array $herbs): array
|
||
{
|
||
$names = [];
|
||
foreach ($herbs as $h) {
|
||
if (!is_array($h)) {
|
||
continue;
|
||
}
|
||
$n = trim((string) ($h['name'] ?? ''));
|
||
if ($n !== '') {
|
||
$names[] = $n;
|
||
}
|
||
}
|
||
$names = array_values(array_unique($names));
|
||
if ($names === []) {
|
||
return [];
|
||
}
|
||
$rows = Medicine::whereNull('delete_time')
|
||
->where('status', 1)
|
||
->whereIn('name', $names)
|
||
->column('gid', 'name');
|
||
if (!is_array($rows)) {
|
||
return [];
|
||
}
|
||
$map = [];
|
||
foreach ($rows as $nameKey => $gidRaw) {
|
||
$nameKey = trim((string) $nameKey);
|
||
$g = trim((string) $gidRaw);
|
||
if ($nameKey === '' || $g === '') {
|
||
continue;
|
||
}
|
||
if (!preg_match('/^\d+$/', $g)) {
|
||
continue;
|
||
}
|
||
$id = (int) $g;
|
||
if ($id > 0) {
|
||
$map[$nameKey] = $id;
|
||
}
|
||
}
|
||
|
||
return $map;
|
||
}
|
||
|
||
/**
|
||
* @param array<int, array<string,mixed>> $herbs
|
||
* @param array<string, int|string> $nameToIdMap 药材名 => 甘草 id(config herb_id_map)
|
||
* @param array<string, int> $doctorNameToGid 医师库 name => gid(甘草)
|
||
* @return array{0: list<array{id:int,name:string,quantity:float|string,brief:string}>, 1: list<string>} [m_list, missing_names]
|
||
*/
|
||
public static function buildMList(array $herbs, array $nameToIdMap, array $doctorNameToGid = []): array
|
||
{
|
||
$mList = [];
|
||
$missing = [];
|
||
foreach ($herbs as $h) {
|
||
if (!is_array($h)) {
|
||
continue;
|
||
}
|
||
$name = trim((string) ($h['name'] ?? ''));
|
||
if ($name === '') {
|
||
continue;
|
||
}
|
||
$id = (int) ($h['gc_id'] ?? $h['gancao_id'] ?? 0);
|
||
if ($id <= 0 && isset($nameToIdMap[$name])) {
|
||
$id = (int) $nameToIdMap[$name];
|
||
}
|
||
if ($id <= 0 && isset($doctorNameToGid[$name])) {
|
||
$id = (int) $doctorNameToGid[$name];
|
||
}
|
||
if ($id <= 0) {
|
||
$missing[] = $name;
|
||
continue;
|
||
}
|
||
$qty = (float) ($h['dosage'] ?? $h['quantity'] ?? 0);
|
||
if ($qty < 0.1) {
|
||
$qty = 0.1;
|
||
}
|
||
$qty = round($qty, 1);
|
||
$brief = trim((string) ($h['brief'] ?? $h['process'] ?? ''));
|
||
|
||
$mList[] = [
|
||
'id' => $id,
|
||
'name' => $name,
|
||
'quantity' => $qty,
|
||
'brief' => $brief,
|
||
];
|
||
}
|
||
|
||
return [$mList, $missing];
|
||
}
|
||
|
||
/**
|
||
* @return array{0:string,1:string,2:string} province, city, addr
|
||
*/
|
||
public static function splitCnAddress(string $full): array
|
||
{
|
||
$full = trim($full);
|
||
if ($full === '') {
|
||
return ['', '', ''];
|
||
}
|
||
$province = '';
|
||
$rest = $full;
|
||
if (preg_match('/^(.*?(?:省|自治区))(.*)$/u', $full, $m)) {
|
||
$province = $m[1];
|
||
$rest = trim($m[2]);
|
||
} elseif (preg_match('/^(北京市|天津市|上海市|重庆市)(.*)$/u', $full, $m2)) {
|
||
$province = $m2[1];
|
||
$rest = trim($m2[2]);
|
||
}
|
||
$city = '';
|
||
$addr = $rest;
|
||
if ($rest !== '') {
|
||
if (preg_match('/^(.*?(?:市|州|盟|地区))(.*)$/u', $rest, $m3)) {
|
||
$city = $m3[1];
|
||
$addr = trim($m3[2]);
|
||
}
|
||
}
|
||
if ($city === '' && $province !== '' && preg_match('/市$/u', $province)) {
|
||
$city = $province;
|
||
}
|
||
if ($addr === '') {
|
||
$addr = $full;
|
||
}
|
||
|
||
return [$province, $city, $addr];
|
||
}
|
||
|
||
/**
|
||
* @param array<string,mixed> $previewPayload token、df_id、amount、m_list、df101ext…+ package/class 由调用方组装
|
||
*/
|
||
public static function ctmPreview(array $previewPayload): array
|
||
{
|
||
$transport = self::transport();
|
||
|
||
return $transport->post($previewPayload);
|
||
}
|
||
|
||
public static function ctmSubmit(array $submitPayload): array
|
||
{
|
||
$transport = self::transport();
|
||
|
||
// Log the payload for debugging (with sensitive data masked)
|
||
try {
|
||
$maskedPayload = self::maskSensitiveData($submitPayload);
|
||
//Log::info('Gancao CTM_SUBMIT_RECIPEL payload', ['payload' => json_encode($maskedPayload, JSON_UNESCAPED_UNICODE)]);
|
||
} catch (\Throwable $e) {
|
||
|
||
// 忽略日志错误,不影响主流程
|
||
// Log::warning('Failed to log Gancao payload: ' . $e->getMessage());
|
||
}
|
||
|
||
|
||
return $transport->post($submitPayload);
|
||
}
|
||
|
||
private static function transport(): GancaoOpenApiTransport
|
||
{
|
||
$c = Config::get('gancao_scm', []);
|
||
|
||
return new GancaoOpenApiTransport(
|
||
(string) $c['gateway_url'],
|
||
(string) $c['gateway_ak'],
|
||
(string) $c['gateway_sk']
|
||
);
|
||
}
|
||
|
||
/**
|
||
* @param array<string,mixed> $rx 处方详情 toArray
|
||
* @param array<string,mixed> $order 业务订单 toArray
|
||
* @param string $token
|
||
* @return array<string,mixed>
|
||
*/
|
||
public static function buildPreviewPayload(array $rx, array $order, string $token): array
|
||
{
|
||
$c = Config::get('gancao_scm', []);
|
||
$type=0;
|
||
|
||
if(array_key_exists($rx['prescription_type'], $c['df_ids'])){
|
||
$type=$c['df_ids'][$rx['prescription_type']];
|
||
}
|
||
|
||
$dfId =$type? (int) $type:(int) $c['df_id'];
|
||
|
||
$herbs = is_array($rx['herbs'] ?? null) ? $rx['herbs'] : [];
|
||
$docMap = self::doctorMedicineGidMapForHerbs($herbs);
|
||
[$mList] = self::buildMList(
|
||
$herbs,
|
||
is_array($c['herb_id_map'] ?? null) ? $c['herb_id_map'] : [],
|
||
$docMap
|
||
);
|
||
$amount = (int) ($order['dose_count']?$order['dose_count'] :$rx['dose_count'] );
|
||
if ($amount < 1) {
|
||
$amount =3;
|
||
}
|
||
|
||
$base = [
|
||
'token' => $token,
|
||
'df_id' => $dfId,
|
||
'amount' => $amount,
|
||
'm_list' => $mList,
|
||
'package' => 'igc_scm.ops.api.order',
|
||
'class' => 'CTM_PREVIEW',
|
||
];
|
||
|
||
if ($dfId === 101) {
|
||
|
||
$base['df101ext'] = [
|
||
'times_per_day' =>$rx['times_per_day'],
|
||
'is_decoct' => $rx['need_decoction'],
|
||
'num_per_pack' => $rx['bags_per_dose'],
|
||
'is_special_writing' => $rx['bags_per_dose']==$rx['times_per_day']?0:1,
|
||
'dose' => $rx['dosage_amount'],
|
||
'usage_mode' => 'ORAL',
|
||
'ds_type' => 1
|
||
];
|
||
|
||
$base['doct_advice']=[
|
||
'taboo'=>$rx['dietary_taboo'],
|
||
'usage_time'=>$rx['usage_time'],
|
||
'usage_brief'=>$rx['usage_instruction'],
|
||
'others'=>$rx['usage_notes'],
|
||
'notes_doctor'=>$order['remark_extra']
|
||
];
|
||
$base['express_type']="general";
|
||
$base['cradle_store']="线上接诊";
|
||
$base['app_order_no']=$order['order_no'];
|
||
$base['express_to']=[
|
||
'name'=>$order['recipient_name'],
|
||
'phone'=>$order['recipient_phone'],
|
||
'province'=>$order['shipping_province'],
|
||
'city'=>$order['shipping_city'],
|
||
'addr'=>$order['shipping_province'].$order['shipping_city'].$order['shipping_city'].$order['shipping_address']
|
||
];
|
||
$base['callback_url']= $c['callback_url'];
|
||
$base['diagnosis']= $rx['clinical_diagnosis']||'无';
|
||
$base['disease']= $rx['clinical_diagnosis']||'无';
|
||
$base['doctor']=[
|
||
'name'=>$rx['doctor_name'],
|
||
'phone'=>''
|
||
];
|
||
|
||
$base['patient']=[
|
||
'name'=>$rx['patient_name'],
|
||
'age'=>$rx['age'],
|
||
'sex'=>$rx['gender_desc']=='男'?1:0
|
||
];
|
||
}
|
||
|
||
if ($dfId === 102) {
|
||
$base['df102ext'] = [
|
||
'times_per_day' =>$rx['times_per_day'],
|
||
'take_days'=>$order['medication_days']?$order['medication_days']:$rx['usage_days'],
|
||
"pill_type"=>$rx['prescription_type']?'WATER':'HONEY',
|
||
"dose"=>$rx['dosage_amount']
|
||
];
|
||
$base['doct_advice']=[
|
||
'taboo'=>$rx['dietary_taboo'],
|
||
'usage_time'=>$rx['usage_time'],
|
||
'usage_brief'=>$rx['usage_instruction'],
|
||
'others'=>$rx['usage_notes'],
|
||
'notes_doctor'=>$order['remark_extra']
|
||
];
|
||
|
||
$base['express_type']="general";
|
||
$base['cradle_store']="线上接诊";
|
||
$base['app_order_no']=$order['order_no'];
|
||
$base['express_to']=[
|
||
'name'=>$order['recipient_name'],
|
||
'phone'=>$order['recipient_phone'],
|
||
'province'=>$order['shipping_province'],
|
||
'city'=>$order['shipping_city'],
|
||
'addr'=>$order['shipping_province'].$order['shipping_city'].$order['shipping_city'].$order['shipping_address']
|
||
];
|
||
$base['callback_url']= $c['callback_url'];
|
||
$base['diagnosis']= $rx['clinical_diagnosis']||'无';
|
||
$base['disease']= $rx['clinical_diagnosis']||'无';
|
||
$base['doctor']=[
|
||
'name'=>$rx['doctor_name'],
|
||
'phone'=>''
|
||
];
|
||
|
||
$base['patient']=[
|
||
'name'=>$rx['patient_name'],
|
||
'age'=>$rx['age'],
|
||
'sex'=>$rx['gender_desc']=='男'?1:0
|
||
];
|
||
}
|
||
|
||
|
||
return $base;
|
||
}
|
||
|
||
/**
|
||
* @param array<string,mixed> $rx
|
||
* @param array<string,mixed> $order
|
||
* @param array{0:string,1:string,2:string} $addrParts province,city,addr
|
||
*/
|
||
public static function buildSubmitPayload(
|
||
array $rx,
|
||
array $order,
|
||
string $token,
|
||
array $addrParts,
|
||
string $appOrderNo
|
||
): array {
|
||
$c = Config::get('gancao_scm', []);
|
||
$preview = self::buildPreviewPayload($rx, $order, $token);
|
||
unset($preview['class']);
|
||
$preview['class'] = 'CTM_SUBMIT_RECIPEL';
|
||
|
||
$phone = preg_replace('/\D/', '', (string) ($order['recipient_phone'] ?? ''));
|
||
if (strlen($phone) !== 11) {
|
||
$phone = preg_replace('/\D/', '', (string) ($rx['phone'] ?? ''));
|
||
}
|
||
if (strlen($phone) !== 11) {
|
||
$phone = '';
|
||
}
|
||
|
||
[$p, $ct, $ad] = $addrParts;
|
||
if (mb_strlen($p) < 2) {
|
||
$p = '四川省';
|
||
}
|
||
if (mb_strlen($ct) < 2) {
|
||
$ct = '成都市';
|
||
}
|
||
if (mb_strlen($ad) < 4) {
|
||
$ad = (string) ($order['shipping_address'] ?? '');
|
||
}
|
||
|
||
$patientName = mb_substr(trim((string) ($rx['patient_name'] ?? $order['recipient_name'] ?? '患者')), 0, 30);
|
||
if ($patientName === '') {
|
||
$patientName = '患者';
|
||
}
|
||
$ageInt = (int) ($rx['age'] ?? 30);
|
||
if ($ageInt < 0) {
|
||
$ageInt = 0;
|
||
}
|
||
if ($ageInt > 120) {
|
||
$ageInt = 120;
|
||
}
|
||
$patientAge = (string) $ageInt;
|
||
$sex = (int) ($rx['gender'] ?? 0) === 1 ? 1 : 0;
|
||
$patientPhone = preg_replace('/\D/', '', (string) ($rx['phone'] ?? ''));
|
||
if (strlen($patientPhone) !== 11) {
|
||
$patientPhone = $phone;
|
||
}
|
||
|
||
$clinical = trim((string) ($rx['clinical_diagnosis'] ?? ''));
|
||
if ($clinical === '') {
|
||
$clinical = '中医辨证论治';
|
||
}
|
||
$clinical = mb_substr($clinical, 0, 128);
|
||
|
||
$doctorName = mb_substr(trim((string) ($rx['doctor_name'] ?? '医师')), 0, 10);
|
||
$doctorBlock = ['name' => $doctorName];
|
||
$docPhone = preg_replace('/\D/', '', (string) ($rx['doctor_phone'] ?? ''));
|
||
if (strlen($docPhone) === 11) {
|
||
$doctorBlock['phone'] = $docPhone;
|
||
}
|
||
|
||
$usageTime = trim((string) ($rx['usage_time'] ?? '饭后半小时服用'));
|
||
if ($usageTime === '') {
|
||
$usageTime = '饭后半小时服用';
|
||
}
|
||
$usageTime = mb_substr($usageTime, 0, 32);
|
||
|
||
$taboo = mb_substr(trim((string) ($rx['dietary_taboo'] ?? '')), 0, 128);
|
||
$usageBrief = trim((string) ($rx['usage_way'] ?? '') . ' ' . (string) ($rx['usage_instruction'] ?? ''));
|
||
$usageBrief = mb_substr(trim($usageBrief), 0, 128);
|
||
|
||
// 确保 express_type 是字符串
|
||
$expressType = isset($c['express_type']) ? (string) $c['express_type'] : 'sf';
|
||
if ($expressType === '' || is_array($c['express_type'] ?? null)) {
|
||
$expressType = 'sf'; // 默认顺丰
|
||
}
|
||
|
||
$preview['express_type'] = $expressType;
|
||
// $preview['express_to'] = [
|
||
// 'name' => mb_substr(trim((string) ($order['recipient_name'] ?? $patientName)), 0, 16),
|
||
// 'phone' => $phone,
|
||
// 'province' => mb_substr($p, 0, 16),
|
||
// 'city' => mb_substr($ct, 0, 16),
|
||
// 'addr' => mb_substr($ad, 0, 64),
|
||
// ];
|
||
$preview['app_order_no'] = mb_substr($appOrderNo, 0, 32);
|
||
|
||
$preview['cradle_store'] = mb_substr(trim((string) ($c['cradle_store'] ?? '')), 0, 32);
|
||
if ($preview['cradle_store'] === '') {
|
||
$preview['cradle_store'] = 'default';
|
||
}
|
||
|
||
// 确保 callback_url 是字符串
|
||
$callbackUrl = isset($c['callback_url']) ? (string) $c['callback_url'] : '';
|
||
if ($callbackUrl === '' || is_array($c['callback_url'] ?? null)) {
|
||
Log::error('Gancao callback_url is invalid', ['callback_url' => $c['callback_url'] ?? null]);
|
||
$callbackUrl = 'https://example.com/callback'; // 临时默认值,实际应该配置正确
|
||
}
|
||
$preview['callback_url'] = $callbackUrl;
|
||
$preview['disease'] = $clinical;
|
||
$preview['diagnosis'] = $clinical;
|
||
|
||
// Ensure all doct_advice fields are strings, not empty
|
||
$tabooStr = $taboo !== '' ? $taboo : '无';
|
||
$usageTimeStr = $usageTime;
|
||
$usageBriefStr = $usageBrief !== '' ? $usageBrief : '遵医嘱';
|
||
$othersStr = trim((string) ($rx['usage_notes'] ?? ''));
|
||
$notesDoctorStr = trim((string) ($order['remark_extra'] ?? ''));
|
||
|
||
$preview['doct_advice'] = [
|
||
'taboo' => $tabooStr,
|
||
'usage_time' => $usageTimeStr,
|
||
'usage_brief' => $usageBriefStr,
|
||
'others' => $othersStr !== '' ? $othersStr : '',
|
||
'notes_doctor' => $notesDoctorStr !== '' ? $notesDoctorStr : '',
|
||
];
|
||
$preview['doctor'] = $doctorBlock;
|
||
$preview['patient'] = [
|
||
'name' => $patientName,
|
||
'age' => $patientAge,
|
||
'sex' => $sex,
|
||
'phone' => $patientPhone,
|
||
];
|
||
return $preview;
|
||
}
|
||
}
|