parent
ecf9758d64
commit
bd580f4093
@ -0,0 +1,80 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Console\Commands; |
||||||
|
|
||||||
|
use App\Dictionary\SendMessage\Type; |
||||||
|
use App\Dictionary\WeChat\MiniProgram\SubscribeId; |
||||||
|
use App\Jobs\SendWeChatMessageJob; |
||||||
|
use App\Models\RegistrationRecord; |
||||||
|
use App\Models\SendMessageJob; |
||||||
|
use App\Utils\Traits\SendSubscribeMessage; |
||||||
|
use Exception; |
||||||
|
use Illuminate\Console\Command; |
||||||
|
use Illuminate\Support\Facades\Log; |
||||||
|
use \Symfony\Component\Console\Command\Command as BaseCommand; |
||||||
|
use Throwable; |
||||||
|
|
||||||
|
/** |
||||||
|
* Appointment Visit Reminders 发送预约就诊提示 |
||||||
|
* @example |
||||||
|
*/ |
||||||
|
class SendAppointmentReminders extends Command |
||||||
|
{ |
||||||
|
use SendSubscribeMessage; |
||||||
|
|
||||||
|
/** |
||||||
|
* The name and signature of the console command. |
||||||
|
* |
||||||
|
* @var string |
||||||
|
*/ |
||||||
|
protected $signature = 'app:send-appointment-reminders'; |
||||||
|
|
||||||
|
/** |
||||||
|
* The console command description. |
||||||
|
* |
||||||
|
* @var string |
||||||
|
*/ |
||||||
|
protected $description = 'Send appointment reminders to users via WeChat subscription messages'; |
||||||
|
|
||||||
|
/** |
||||||
|
* Execute the console command. |
||||||
|
*/ |
||||||
|
public function handle(): int |
||||||
|
{ |
||||||
|
$this->info('Starting to send appointment reminders...'); |
||||||
|
|
||||||
|
// 查询即将到期的预约记录(8小时后的预约) |
||||||
|
$appointments = RegistrationRecord::where('visit_date', now()->toDate()) |
||||||
|
->where('begin_date', now()->subHours(8)) |
||||||
|
->where('reminder_sent', 0) |
||||||
|
->get(); |
||||||
|
|
||||||
|
if ($appointments->isEmpty()) { |
||||||
|
$this->info('No appointments to send reminders for.'); |
||||||
|
return BaseCommand::SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
$message = new SendMessageJob(); |
||||||
|
$success_records = []; |
||||||
|
foreach ($appointments as $appointment) { |
||||||
|
try { |
||||||
|
$this->sendAppointmentReminderMessage($appointment); |
||||||
|
|
||||||
|
// 标记提醒已发送 |
||||||
|
$appointment->update(['reminder_sent' => 1]); |
||||||
|
$success_records[] = $appointment->id; |
||||||
|
|
||||||
|
$this->info("Reminder sent for appointment ID: {$appointment->id}"); |
||||||
|
Log::channel('send_wechat_message')->info('Reminder sent for appointment ID: '. $appointment->id); |
||||||
|
} catch (Exception|Throwable $e) { |
||||||
|
// 记录错误日志 |
||||||
|
$err_msg = "{$e->getMessage()} ON {$e->getFile()}:{$e->getLine()}"; |
||||||
|
$this->error("Failed to send reminder for appointment ID: {$appointment->id}, Err msg: {$err_msg}"); |
||||||
|
Log::channel('send_wechat_message')->error('Failed to send reminder for appointment ID: '. $appointment->id, ['error' => $err_msg]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
$this->info( '['. date('Y-m-d H:i:s').'] Appointment reminders sent successfully. send Record:'. implode(',', $success_records)); |
||||||
|
return BaseCommand::SUCCESS; |
||||||
|
} |
||||||
|
} |
@ -1,51 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
declare(strict_types=1); |
|
||||||
|
|
||||||
namespace App\Dictionary\PushMessage; |
|
||||||
|
|
||||||
use App\Dictionary\WeChat\Official\OpenApi; |
|
||||||
|
|
||||||
/** |
|
||||||
* 推送微信消息类型 |
|
||||||
*/ |
|
||||||
enum Type: int |
|
||||||
{ |
|
||||||
case OFFICIAL_TEMPLATE = 1; |
|
||||||
|
|
||||||
case OFFICIAL_SINGLE_SUBSCRIBE = 2; |
|
||||||
|
|
||||||
case OFFICIAL_SUBSCRIBE = 3; |
|
||||||
|
|
||||||
case OFFICIAL_CUSTOM = 4; |
|
||||||
|
|
||||||
/** |
|
||||||
* Label string. |
|
||||||
* |
|
||||||
* @return string |
|
||||||
*/ |
|
||||||
public function label(): string |
|
||||||
{ |
|
||||||
return match ($this) { |
|
||||||
self::OFFICIAL_TEMPLATE => '公众号模板消息', |
|
||||||
self::OFFICIAL_SINGLE_SUBSCRIBE => '公众号一次性订阅消息', |
|
||||||
self::OFFICIAL_SUBSCRIBE => '公众号订阅消息', |
|
||||||
self::OFFICIAL_CUSTOM => '公众号客服消息', |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get Open Api. |
|
||||||
* |
|
||||||
* @return OpenApi |
|
||||||
*/ |
|
||||||
public function api(): OpenApi |
|
||||||
{ |
|
||||||
return match ($this) { |
|
||||||
self::OFFICIAL_TEMPLATE => OpenApi::SEND_TEMPLATE_MESSAGE, |
|
||||||
self::OFFICIAL_SINGLE_SUBSCRIBE => OpenApi::SEND_SINGLE_SUBSCRIBE_MESSAGE, |
|
||||||
self::OFFICIAL_SUBSCRIBE => OpenApi::SEND_SUBSCRIBE_MESSAGE, |
|
||||||
self::OFFICIAL_CUSTOM => OpenApi::SEND_CUSTOM_MESSAGE, |
|
||||||
}; |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,52 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Dictionary\SendMessage; |
||||||
|
|
||||||
|
use App\Dictionary\WeChat\MiniProgram\OpenApi as MiniOpenApi; |
||||||
|
use App\Dictionary\WeChat\Official\OpenApi as OfficialOpenApi; |
||||||
|
|
||||||
|
/** |
||||||
|
* 推送微信消息类型 |
||||||
|
*/ |
||||||
|
enum Type: int |
||||||
|
{ |
||||||
|
case TEMPLATE = 1; |
||||||
|
|
||||||
|
case SINGLE_SUBSCRIBE = 2; |
||||||
|
|
||||||
|
case SUBSCRIBE = 3; |
||||||
|
|
||||||
|
case CUSTOM = 4; |
||||||
|
|
||||||
|
/** |
||||||
|
* Label string. |
||||||
|
* |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public function label(): string |
||||||
|
{ |
||||||
|
return match ($this) { |
||||||
|
self::TEMPLATE => '模板消息', |
||||||
|
self::SINGLE_SUBSCRIBE => '一次性订阅消息', |
||||||
|
self::SUBSCRIBE => '长期订阅消息', |
||||||
|
self::CUSTOM => '客服消息', |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get Open Api. |
||||||
|
* |
||||||
|
* @return OfficialOpenApi|MiniOpenApi |
||||||
|
*/ |
||||||
|
public function api(): OfficialOpenApi|MiniOpenApi |
||||||
|
{ |
||||||
|
return match ($this) { |
||||||
|
self::TEMPLATE => OfficialOpenApi::SEND_TEMPLATE_MESSAGE, |
||||||
|
self::SINGLE_SUBSCRIBE, |
||||||
|
self::SUBSCRIBE => MiniOpenApi::SEND_SUBSCRIBE_MESSAGE, |
||||||
|
self::CUSTOM => OfficialOpenApi::SEND_CUSTOM_MESSAGE, |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Dictionary\WeChat\MiniProgram; |
||||||
|
|
||||||
|
/** |
||||||
|
* 推送模板消息ID |
||||||
|
*/ |
||||||
|
enum SubscribeId: string |
||||||
|
{ |
||||||
|
|
||||||
|
case PATIENT_BIND_SUCCESS = 'C1JKomPq-Bes7fOs4vv2Jcy5SFUVvnr_qcl9oNaz7eQ'; |
||||||
|
|
||||||
|
case PATIENT_UNBIND_SUCCESS = 'oDOqp-Smyve0Se8ued4592HC82fBxRLodD1CCb2FWBU'; |
||||||
|
|
||||||
|
case REGISTRATION_SUCCESS = '8TEAjsZsuK8atvLeLqzEH067Mnn5qGmyFnshEy6YsiU'; |
||||||
|
|
||||||
|
case REGISTRATION_FAILURE = '4I_LqdovtP1gwkQjH78Y7gwuPde_rY0qgPqNl4aPBWk'; |
||||||
|
|
||||||
|
case REGISTRATION_CANCEL = 'fVaoPnY6MOpKXbpnrGcPqcLl8dv-aRBNfCGeOlgL8bA'; |
||||||
|
|
||||||
|
case OUTPATIENT_PAYMENT_SUCCESS = 'IlYVy2NNv7v6sg-s_Y6OYS4bRlulosztG0fn7LW4qcQ'; |
||||||
|
|
||||||
|
case OUTPATIENT_PAYMENT_FAILURE = 'ayUD7DXCxZAlhWZ7YiWnHvUXf3AqS_6xkWlbCMC40ek'; |
||||||
|
|
||||||
|
case VISIT_REMIND = 'PidheHDjaf--SIu4-wsDT2uFA_0gjDhq_BU0fABa_w0'; |
||||||
|
|
||||||
|
/** |
||||||
|
* Label string |
||||||
|
* |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public function label(): string |
||||||
|
{ |
||||||
|
return match ($this) { |
||||||
|
self::PATIENT_BIND_SUCCESS => '就诊人解绑成功提醒', |
||||||
|
self::PATIENT_UNBIND_SUCCESS => '就诊人绑定成功提醒', |
||||||
|
self::REGISTRATION_SUCCESS => '挂号成功通知', |
||||||
|
self::REGISTRATION_FAILURE => '挂号失败通知', |
||||||
|
self::REGISTRATION_CANCEL => '取消预约挂号通知', |
||||||
|
self::OUTPATIENT_PAYMENT_SUCCESS => '门诊缴费成功通知', |
||||||
|
self::OUTPATIENT_PAYMENT_FAILURE => '门诊缴费失败通知', |
||||||
|
self::VISIT_REMIND => '就诊提醒', |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -1,58 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
declare(strict_types=1); |
|
||||||
|
|
||||||
namespace App\Dictionary\WeChat\Official; |
|
||||||
|
|
||||||
/** |
|
||||||
* 推送模板消息ID |
|
||||||
*/ |
|
||||||
enum SubscribeId: string |
|
||||||
{ |
|
||||||
case REGISTRATION_SUCCESS = 'ASUyXyVFONT6RNwbAs49_rHKQUkc_7iXs31kt1AyACU'; |
|
||||||
|
|
||||||
case REGISTRATION_FAILURE = '36fosQYJU-dN8AsNZERrXY9bfEhP1N9zoqVkORnVr_o'; |
|
||||||
|
|
||||||
case REGISTRATION_CANCEL = '8sWThvcSxg0uS6AFy0LWOeGjbRTUI_n2QHDDMYffjGo'; |
|
||||||
|
|
||||||
case OUTPATIENT_PENDING = 'qieeDt7t2XnT0xB8Fc5ec_peuQDN4TQFPl3ivsILKF0'; |
|
||||||
|
|
||||||
case OUTPATIENT_PAYMENT = 'Aeww94Ahcr0q8bGaAQTSW3s6CB2RXdN9qxZV65bYhhc'; |
|
||||||
|
|
||||||
case INPATIENT_RECHARGE_SUCCESS = 'V30A9ZZ6dqJe_DrYhB5YTUmDbj_F99oJ5k3XP4LK7qk'; |
|
||||||
|
|
||||||
case INPATIENT_RECHARGE_FAILURE = '02HnYY5HFP9LKrWDX_UsQNq2vw3ttfGBvZ4H7SfPZyM'; |
|
||||||
|
|
||||||
case REFUND = '3C9Zw6gBgESwciGF3y2ZeoelYXg1fiZeHAnOUXRf2nM'; |
|
||||||
|
|
||||||
case VISIT = 'IrpIcVU20c5GzBPNUqkp_lvuO-t8gxjiiGx7S88GvIA'; |
|
||||||
|
|
||||||
case SUSPEND_VISIT = 'fTUc-MhhqpBA9WzqQQtfceAmgRr8EFuJNDSdvRDG5K8'; |
|
||||||
|
|
||||||
case TAKE_MEDICINE = '-NX91rCEWPejiKVgbkkGf1QF4zNyH7tadHnkTDqyK_c'; |
|
||||||
|
|
||||||
case TODO_LIST = 'pLdQ5HfXP12C8bHM8EiUP2dqOVFqv-2m0YGCpqC-54Y'; |
|
||||||
|
|
||||||
/** |
|
||||||
* Label string |
|
||||||
* |
|
||||||
* @return string |
|
||||||
*/ |
|
||||||
public function label(): string |
|
||||||
{ |
|
||||||
return match ($this) { |
|
||||||
self::REGISTRATION_SUCCESS => '挂号成功通知', |
|
||||||
self::REGISTRATION_FAILURE => '挂号失败通知', |
|
||||||
self::REGISTRATION_CANCEL => '挂号取消通知', |
|
||||||
self::OUTPATIENT_PENDING => '门诊待缴费通知', |
|
||||||
self::OUTPATIENT_PAYMENT => '门诊缴费通知', |
|
||||||
self::INPATIENT_RECHARGE_SUCCESS => '住院预交金支付成功通知', |
|
||||||
self::INPATIENT_RECHARGE_FAILURE => '住院预交失败提醒', |
|
||||||
self::REFUND => '退费通知', |
|
||||||
self::VISIT => '就诊提醒', |
|
||||||
self::SUSPEND_VISIT => '医生停诊通知', |
|
||||||
self::TAKE_MEDICINE => '取药通知', |
|
||||||
self::TODO_LIST => '待办事项通知' |
|
||||||
}; |
|
||||||
} |
|
||||||
} |
|
@ -1,58 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
declare(strict_types=1); |
|
||||||
|
|
||||||
namespace App\Dictionary\WeChat\Official; |
|
||||||
|
|
||||||
/** |
|
||||||
* 推送模板消息ID |
|
||||||
*/ |
|
||||||
enum TemplateId: string |
|
||||||
{ |
|
||||||
case REGISTRATION_SUCCESS = 'ASUyXyVFONT6RNwbAs49_rHKQUkc_7iXs31kt1AyACU'; |
|
||||||
|
|
||||||
case REGISTRATION_FAILURE = '36fosQYJU-dN8AsNZERrXY9bfEhP1N9zoqVkORnVr_o'; |
|
||||||
|
|
||||||
case REGISTRATION_CANCEL = '8sWThvcSxg0uS6AFy0LWOeGjbRTUI_n2QHDDMYffjGo'; |
|
||||||
|
|
||||||
case OUTPATIENT_PENDING = 'qieeDt7t2XnT0xB8Fc5ec_peuQDN4TQFPl3ivsILKF0'; |
|
||||||
|
|
||||||
case OUTPATIENT_PAYMENT = 'Aeww94Ahcr0q8bGaAQTSW3s6CB2RXdN9qxZV65bYhhc'; |
|
||||||
|
|
||||||
case INPATIENT_RECHARGE_SUCCESS = 'V30A9ZZ6dqJe_DrYhB5YTUmDbj_F99oJ5k3XP4LK7qk'; |
|
||||||
|
|
||||||
case INPATIENT_RECHARGE_FAILURE = '02HnYY5HFP9LKrWDX_UsQNq2vw3ttfGBvZ4H7SfPZyM'; |
|
||||||
|
|
||||||
case REFUND = '3C9Zw6gBgESwciGF3y2ZeoelYXg1fiZeHAnOUXRf2nM'; |
|
||||||
|
|
||||||
case VISIT = 'IrpIcVU20c5GzBPNUqkp_lvuO-t8gxjiiGx7S88GvIA'; |
|
||||||
|
|
||||||
case SUSPEND_VISIT = 'fTUc-MhhqpBA9WzqQQtfceAmgRr8EFuJNDSdvRDG5K8'; |
|
||||||
|
|
||||||
case TAKE_MEDICINE = '-NX91rCEWPejiKVgbkkGf1QF4zNyH7tadHnkTDqyK_c'; |
|
||||||
|
|
||||||
case TODO_LIST = 'pLdQ5HfXP12C8bHM8EiUP2dqOVFqv-2m0YGCpqC-54Y'; |
|
||||||
|
|
||||||
/** |
|
||||||
* Label string |
|
||||||
* |
|
||||||
* @return string |
|
||||||
*/ |
|
||||||
public function label(): string |
|
||||||
{ |
|
||||||
return match ($this) { |
|
||||||
self::REGISTRATION_SUCCESS => '挂号成功通知', |
|
||||||
self::REGISTRATION_FAILURE => '挂号失败通知', |
|
||||||
self::REGISTRATION_CANCEL => '挂号取消通知', |
|
||||||
self::OUTPATIENT_PENDING => '门诊待缴费通知', |
|
||||||
self::OUTPATIENT_PAYMENT => '门诊缴费通知', |
|
||||||
self::INPATIENT_RECHARGE_SUCCESS => '住院预交金支付成功通知', |
|
||||||
self::INPATIENT_RECHARGE_FAILURE => '住院预交失败提醒', |
|
||||||
self::REFUND => '退费通知', |
|
||||||
self::VISIT => '就诊提醒', |
|
||||||
self::SUSPEND_VISIT => '医生停诊通知', |
|
||||||
self::TAKE_MEDICINE => '取药通知', |
|
||||||
self::TODO_LIST => '待办事项通知' |
|
||||||
}; |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,48 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Jobs\Message; |
||||||
|
|
||||||
|
use App\Dictionary\SendMessage\Type as MessageType; |
||||||
|
use EasyWeChat\Kernel\HttpClient\Response; |
||||||
|
use EasyWeChat\OfficialAccount\Application as OfficialApplication; |
||||||
|
use EasyWeChat\MiniApp\Application as MiniApplication; |
||||||
|
use EasyWeChat\Work\Application as WorkApplication; |
||||||
|
use Illuminate\Support\Facades\Log; |
||||||
|
use ReflectionException; |
||||||
|
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; |
||||||
|
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; |
||||||
|
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; |
||||||
|
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; |
||||||
|
|
||||||
|
abstract class Message |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @return OfficialApplication|MiniApplication|WorkApplication |
||||||
|
*/ |
||||||
|
abstract public function getApp(): OfficialApplication|MiniApplication|WorkApplication; |
||||||
|
|
||||||
|
/** |
||||||
|
* Send Message. |
||||||
|
* |
||||||
|
* @param MessageType $type 消息类型 |
||||||
|
* @param array $message 消息数据 json 包 |
||||||
|
* |
||||||
|
* @return Response |
||||||
|
* |
||||||
|
* @throws TransportExceptionInterface |
||||||
|
* @throws ClientExceptionInterface |
||||||
|
* @throws RedirectionExceptionInterface |
||||||
|
* @throws ServerExceptionInterface |
||||||
|
*/ |
||||||
|
public function send(MessageType $type, array $message): Response |
||||||
|
{ |
||||||
|
/** @var Response $response */ |
||||||
|
$response = $this->getApp()->getClient()->postJson($type->api()->value, $message); |
||||||
|
|
||||||
|
Log::channel('SendWeChatMessage')->info('Push WeChat Message', [$type->label(), $message, $response->getContent(false)]); |
||||||
|
|
||||||
|
return $response; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
<?php |
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Jobs\Message; |
||||||
|
|
||||||
|
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException; |
||||||
|
use EasyWeChat\MiniApp\Application as MiniApplication; |
||||||
|
|
||||||
|
class SubscriptionMessage extends Message |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @return MiniApplication |
||||||
|
* @throws InvalidArgumentException |
||||||
|
*/ |
||||||
|
public function getApp(): MiniApplication |
||||||
|
{ |
||||||
|
/** @var MiniApplication */ |
||||||
|
return getWeChatMiniProgramApp(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,159 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Jobs; |
||||||
|
|
||||||
|
use App\Dictionary\SendMessage\Type as MessageType; |
||||||
|
use App\Dictionary\SendMessage\Status as MessageStatus; |
||||||
|
use App\Jobs\Message\SubscriptionMessage; |
||||||
|
use App\Models\SendMessageJob; |
||||||
|
use Exception; |
||||||
|
use Illuminate\Bus\Queueable; |
||||||
|
use Illuminate\Contracts\Cache\Repository; |
||||||
|
use Illuminate\Contracts\Queue\ShouldBeUnique; |
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue; |
||||||
|
use Illuminate\Foundation\Bus\Dispatchable; |
||||||
|
use Illuminate\Queue\InteractsWithQueue; |
||||||
|
use Illuminate\Queue\SerializesModels; |
||||||
|
use Illuminate\Support\Facades\Cache; |
||||||
|
use Illuminate\Support\Facades\Log; |
||||||
|
use Throwable; |
||||||
|
|
||||||
|
class SendWeChatMessageJob implements ShouldQueue, ShouldBeUnique |
||||||
|
{ |
||||||
|
use Dispatchable; |
||||||
|
use InteractsWithQueue; |
||||||
|
use Queueable; |
||||||
|
use SerializesModels; |
||||||
|
|
||||||
|
/** |
||||||
|
* PushWechatMessage Model. |
||||||
|
* |
||||||
|
* @var SendMessageJob |
||||||
|
*/ |
||||||
|
protected SendMessageJob $message; |
||||||
|
|
||||||
|
/** |
||||||
|
* Message Type. |
||||||
|
* |
||||||
|
* @var MessageType |
||||||
|
*/ |
||||||
|
protected MessageType $message_type; |
||||||
|
|
||||||
|
/** |
||||||
|
* 任务可尝试次数. |
||||||
|
* |
||||||
|
* @var int |
||||||
|
*/ |
||||||
|
public int $tries = 3; |
||||||
|
|
||||||
|
/** |
||||||
|
* 任务的唯一 ID。 |
||||||
|
* |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public function uniqueId(): string |
||||||
|
{ |
||||||
|
return 'Queue:SendWechatMessage:'. $this->message->id; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取唯一任务锁的缓存驱动程序。 |
||||||
|
* |
||||||
|
* @return Repository |
||||||
|
*/ |
||||||
|
public function uniqueVia(): Repository |
||||||
|
{ |
||||||
|
return Cache::driver('redis'); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Send WeChat Message Job Construct. |
||||||
|
* |
||||||
|
* @param SendMessageJob $message |
||||||
|
*/ |
||||||
|
public function __construct(SendMessageJob $message) |
||||||
|
{ |
||||||
|
$this->message = $message->withoutRelations(); |
||||||
|
$this->message_type = MessageType::from($this->message->type); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Execute the job. |
||||||
|
*/ |
||||||
|
public function handle(): void |
||||||
|
{ |
||||||
|
$content = json_decode($this->message->content, true); |
||||||
|
|
||||||
|
try { |
||||||
|
$response = match ($this->message_type) { |
||||||
|
MessageType::TEMPLATE, |
||||||
|
MessageType::SINGLE_SUBSCRIBE, |
||||||
|
MessageType::SUBSCRIBE, |
||||||
|
MessageType::CUSTOM => (new SubscriptionMessage())->send($this->message_type, $content), |
||||||
|
}; |
||||||
|
|
||||||
|
$data = $response->toArray(false); |
||||||
|
|
||||||
|
if ($response->isSuccessful()) { |
||||||
|
$this->successful((string)$data['errcode']); |
||||||
|
} else { |
||||||
|
$this->retry((string)$data['errmsg']); |
||||||
|
} |
||||||
|
|
||||||
|
} catch (Exception|Throwable $e) { |
||||||
|
$message = $e->getMessage().' in '.$e->getFile().':'.$e->getLine(); |
||||||
|
Log::channel('SendWeChatMessage')->info('Push WeChat Message Error', [$this->message->id, $message]); |
||||||
|
|
||||||
|
$this->retry($e->getMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Handle a job successful. |
||||||
|
* |
||||||
|
* @param string $msg_id |
||||||
|
* |
||||||
|
* @return void |
||||||
|
*/ |
||||||
|
public function successful(string $msg_id): void |
||||||
|
{ |
||||||
|
$this->message->status = MessageStatus::SUCCESS->value; |
||||||
|
$this->message->msg_id = $msg_id; |
||||||
|
$this->message->sent_at = date('Y-m-d H:i:s'); |
||||||
|
$this->message->save(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Handle a job retry. |
||||||
|
* |
||||||
|
* @param string $fail_reason |
||||||
|
* @return void |
||||||
|
*/ |
||||||
|
public function retry(string $fail_reason): void |
||||||
|
{ |
||||||
|
if ($this->message->number < 3) { |
||||||
|
++$this->message->number; |
||||||
|
$this->message->save(); |
||||||
|
|
||||||
|
$this->release(10); |
||||||
|
} else { |
||||||
|
$this->failed($fail_reason); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Handle a job failure. |
||||||
|
* |
||||||
|
* @param string $fail_reason |
||||||
|
* |
||||||
|
* @return void |
||||||
|
*/ |
||||||
|
public function failed(string $fail_reason): void |
||||||
|
{ |
||||||
|
$this->message->status = MessageStatus::FAILURE->value; |
||||||
|
$this->message->fail_reason = $fail_reason; |
||||||
|
$this->message->save(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,260 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Utils\Traits; |
||||||
|
use App\Dictionary\SendMessage\Type; |
||||||
|
use App\Dictionary\WeChat\MiniProgram\SubscribeId; |
||||||
|
use App\Jobs\SendWeChatMessageJob; |
||||||
|
use App\Models\Order as OrderModel; |
||||||
|
use App\Models\RegistrationRecord; |
||||||
|
use App\Models\SendMessageJob; |
||||||
|
|
||||||
|
trait SendSubscribeMessage |
||||||
|
{ |
||||||
|
|
||||||
|
protected static SendMessageJob $message_model; |
||||||
|
|
||||||
|
/** |
||||||
|
* 单例获取 SendMessageJob |
||||||
|
* @return SendMessageJob |
||||||
|
*/ |
||||||
|
public static function getSendMessageJob(): SendMessageJob |
||||||
|
{ |
||||||
|
if (!self::$message_model) { |
||||||
|
self::$message_model = new SendMessageJob(); |
||||||
|
} |
||||||
|
return self::$message_model; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 发送绑定患者订阅消息 |
||||||
|
* @param string $open_id |
||||||
|
* @param int $relate_patient_id |
||||||
|
* @param string $patient_name |
||||||
|
* @return void |
||||||
|
*/ |
||||||
|
public function sendBindPatientSubscribeMessage(string $open_id, int $relate_patient_id, string $patient_name): void |
||||||
|
{ |
||||||
|
$subscribe_id = SubscribeId::PATIENT_BIND_SUCCESS; |
||||||
|
$data = [ |
||||||
|
'touser' => $open_id, |
||||||
|
'template_id' => $subscribe_id->value, |
||||||
|
'page' => 'pagesA/card/index', |
||||||
|
'data' => [ |
||||||
|
'thing1' => ['value' => $patient_name], |
||||||
|
'time2' => ['value' => date('Y-m-d H:i')], |
||||||
|
'thing3' => ['value' => '您已绑定成功,可以进行线上线下就医服务。'], |
||||||
|
], |
||||||
|
'miniprogram_state' => env('custom.mini_program_message_state') |
||||||
|
]; |
||||||
|
|
||||||
|
$message = self::getSendMessageJob()->insertMessageJobs(0, $relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, ''); |
||||||
|
|
||||||
|
SendWeChatMessageJob::dispatch($message); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 发送绑定患者订阅消息 |
||||||
|
* @param string $open_id |
||||||
|
* @param int $relate_patient_id |
||||||
|
* @param string $patient_name |
||||||
|
* @param string $inpatient_id 住院号 |
||||||
|
* @param string $patient_id 门诊号 |
||||||
|
* @return void |
||||||
|
*/ |
||||||
|
public function sendUnbindPatientSubscribeMessage(string $open_id, int $relate_patient_id, string $patient_name, string $inpatient_id, string $patient_id): void |
||||||
|
{ |
||||||
|
$subscribe_id = SubscribeId::PATIENT_UNBIND_SUCCESS; |
||||||
|
$data = [ |
||||||
|
'touser' => $open_id, |
||||||
|
'template_id' => $subscribe_id->value, |
||||||
|
'page' => '', |
||||||
|
'data' => [ |
||||||
|
'thing1' => ['value' => $patient_name], |
||||||
|
'time2' => ['value' => date('Y-m-d H:i')], |
||||||
|
'thing3' => ['value' => '您已解除绑定关系,无法使用线上线下就医服务。'], |
||||||
|
'character_string5' => ['value' => $inpatient_id], |
||||||
|
'character_string6' => ['value' => $patient_id], |
||||||
|
], |
||||||
|
'miniprogram_state' => env('custom.mini_program_message_state') |
||||||
|
]; |
||||||
|
|
||||||
|
$message = self::getSendMessageJob()->insertMessageJobs(0, $relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data); |
||||||
|
|
||||||
|
SendWeChatMessageJob::dispatch($message); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 发送就诊提醒消息 |
||||||
|
* @param RegistrationRecord $record |
||||||
|
* @return void |
||||||
|
*/ |
||||||
|
public function sendAppointmentReminderMessage(RegistrationRecord $record): void |
||||||
|
{ |
||||||
|
$order = &$record->order; |
||||||
|
$subscribe_id = SubscribeId::VISIT_REMIND; |
||||||
|
$visit_time = $record->visit_date . ' '. $record->begin_time . '~'. $record->end_time; |
||||||
|
$data = [ |
||||||
|
'touser' => $order->open_id, |
||||||
|
'template_id' => $subscribe_id->value, |
||||||
|
'page' => 'pagesA/register/regRecord', |
||||||
|
'data' => [ |
||||||
|
'name1' => ['value' => $order->patient_name], |
||||||
|
'time7' => ['value' => $visit_time], |
||||||
|
'thing10' => ['value' => $record->dept_name. '('. $record->doctor_name. ')'], |
||||||
|
'thing6' => ['value' => $record->dept_location], |
||||||
|
'thing5' => ['value' => '请准时前往医院就诊。'], |
||||||
|
], |
||||||
|
'miniprogram_state' => env('custom.mini_program_message_state') |
||||||
|
]; |
||||||
|
|
||||||
|
$message = self::getSendMessageJob()->insertMessageJobs(0, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, ''); |
||||||
|
|
||||||
|
SendWeChatMessageJob::dispatch($message); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 发送挂号成功订阅消息 |
||||||
|
* @param OrderModel $order |
||||||
|
* @return void |
||||||
|
*/ |
||||||
|
public function sendRegistrationSuccessMessage(OrderModel $order): void |
||||||
|
{ |
||||||
|
$record = &$order->registrationRecord; |
||||||
|
$subscribe_id = SubscribeId::REGISTRATION_SUCCESS; |
||||||
|
$visit_time = $record->visit_date . ' '. $record->begin_time . '~'. $record->end_time; |
||||||
|
$data = [ |
||||||
|
'touser' => $order->open_id, |
||||||
|
'template_id' => $subscribe_id->value, |
||||||
|
'page' => 'pagesA/register/regRecord', |
||||||
|
'data' => [ |
||||||
|
'thing5' => ['value' => $record->dept_name], |
||||||
|
'thing17' => ['value' => $record->dept_location], |
||||||
|
'character_string15' => ['value' => $visit_time], |
||||||
|
'thing19' => ['value' => $record->doctor_name], |
||||||
|
'amount13' => ['value' => $order->fee / 100], |
||||||
|
], |
||||||
|
'miniprogram_state' => env('custom.mini_program_message_state') |
||||||
|
]; |
||||||
|
|
||||||
|
$message = self::getSendMessageJob()->insertMessageJobs(0, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, ''); |
||||||
|
|
||||||
|
SendWeChatMessageJob::dispatch($message); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 发送挂号失败订阅消息 |
||||||
|
* @param OrderModel $order |
||||||
|
* @return void |
||||||
|
*/ |
||||||
|
public function sendRegistrationFailureMessage(OrderModel $order): void |
||||||
|
{ |
||||||
|
$record = &$order->registrationRecord; |
||||||
|
$subscribe_id = SubscribeId::REGISTRATION_FAILURE; |
||||||
|
$visit_time = $record->visit_date . ' '. $record->begin_time . '~'. $record->end_time; |
||||||
|
$data = [ |
||||||
|
'touser' => $order->open_id, |
||||||
|
'template_id' => $subscribe_id->value, |
||||||
|
'page' => 'pagesA/register/regRecord', |
||||||
|
'data' => [ |
||||||
|
'thing2' => ['value' => $record->dept_name], |
||||||
|
'thing3' => ['value' => $record->doctor_name], |
||||||
|
'name1' => ['value' => $order->patient_name], |
||||||
|
'time4' => ['value' => $visit_time], |
||||||
|
'amount13' => ['value' => $order->fee / 100], |
||||||
|
], |
||||||
|
'miniprogram_state' => env('custom.mini_program_message_state') |
||||||
|
]; |
||||||
|
|
||||||
|
$message = self::getSendMessageJob()->insertMessageJobs(0, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, ''); |
||||||
|
|
||||||
|
SendWeChatMessageJob::dispatch($message); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 发送取消预约挂号消息 |
||||||
|
* @param OrderModel $order |
||||||
|
* @return void |
||||||
|
*/ |
||||||
|
public function sendRegistrationCancelMessage(OrderModel $order): void |
||||||
|
{ |
||||||
|
$record = &$order->registrationRecord; |
||||||
|
$subscribe_id = SubscribeId::REGISTRATION_CANCEL; |
||||||
|
$visit_time = $record->visit_date . ' '. $record->begin_time . '~'. $record->end_time; |
||||||
|
$data = [ |
||||||
|
'touser' => $order->open_id, |
||||||
|
'template_id' => $subscribe_id->value, |
||||||
|
'page' => 'pagesA/register/regRecord', |
||||||
|
'data' => [ |
||||||
|
'thing1' => ['value' => $order->patient_name], |
||||||
|
'thing2' => ['value' => $record->dept_name], |
||||||
|
'thing3' => ['value' => $record->doctor_name], |
||||||
|
'time4' => ['value' => $visit_time], |
||||||
|
'thing5' => ['value' => '已成功取消预约,如有需要请重新预约。'], |
||||||
|
], |
||||||
|
'miniprogram_state' => env('custom.mini_program_message_state') |
||||||
|
]; |
||||||
|
|
||||||
|
$message = self::getSendMessageJob()->insertMessageJobs(0, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, ''); |
||||||
|
|
||||||
|
SendWeChatMessageJob::dispatch($message); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 发送门诊缴费成功消息 |
||||||
|
* @param OrderModel $order |
||||||
|
* @return void |
||||||
|
*/ |
||||||
|
public function sendOutpatientPaymentSuccessMessage(OrderModel $order): void |
||||||
|
{ |
||||||
|
$record = &$order->outpatientPaymentRecord; |
||||||
|
$subscribe_id = SubscribeId::OUTPATIENT_PAYMENT_SUCCESS; |
||||||
|
$data = [ |
||||||
|
'touser' => $order->open_id, |
||||||
|
'template_id' => $subscribe_id->value, |
||||||
|
'page' => 'pagesA/outpatient/outPayList', |
||||||
|
'data' => [ |
||||||
|
'date5' => ['value' => date('Y-m-d')], |
||||||
|
'amount6' => ['value' => $order->fee / 100], |
||||||
|
'character_string7' => ['value' => $order->order_id], |
||||||
|
'character_string14' => ['value' => $order->patient_id], |
||||||
|
'thing9' => ['value' => '门诊缴费'], |
||||||
|
], |
||||||
|
'miniprogram_state' => env('custom.mini_program_message_state') |
||||||
|
]; |
||||||
|
|
||||||
|
$message = self::getSendMessageJob()->insertMessageJobs(0, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, ''); |
||||||
|
|
||||||
|
SendWeChatMessageJob::dispatch($message); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 发送门诊缴费失败消息 |
||||||
|
* @param OrderModel $order |
||||||
|
* @return void |
||||||
|
*/ |
||||||
|
public function sendOutpatientPaymentFailureMessage(OrderModel $order): void |
||||||
|
{ |
||||||
|
$record = &$order->outpatientPaymentRecord; |
||||||
|
$subscribe_id = SubscribeId::OUTPATIENT_PAYMENT_FAILURE; |
||||||
|
$data = [ |
||||||
|
'touser' => $order->open_id, |
||||||
|
'template_id' => $subscribe_id->value, |
||||||
|
'page' => '', |
||||||
|
'data' => [ |
||||||
|
|
||||||
|
'character_string1' => ['value' => $order->order_id], |
||||||
|
'thing2' => ['value' => '门诊缴费'], |
||||||
|
'name3' => ['value' => $order->patient_name], |
||||||
|
'amount4' => ['value' => $order->fee / 100], |
||||||
|
'date6' => ['value' => date('Y-m-d')], |
||||||
|
], |
||||||
|
'miniprogram_state' => env('custom.mini_program_message_state') |
||||||
|
]; |
||||||
|
|
||||||
|
$message = self::getSendMessageJob()->insertMessageJobs(0, $order->relate_patient_id, Type::SINGLE_SUBSCRIBE, $subscribe_id, $data, ''); |
||||||
|
|
||||||
|
SendWeChatMessageJob::dispatch($message); |
||||||
|
} |
||||||
|
} |
@ -1,33 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration; |
|
||||||
use Illuminate\Database\Schema\Blueprint; |
|
||||||
use Illuminate\Support\Facades\Schema; |
|
||||||
|
|
||||||
return new class extends Migration |
|
||||||
{ |
|
||||||
/** |
|
||||||
* Run the migrations. |
|
||||||
*/ |
|
||||||
public function up(): void |
|
||||||
{ |
|
||||||
Schema::create('personal_access_tokens', function (Blueprint $table) { |
|
||||||
$table->id(); |
|
||||||
$table->morphs('tokenable'); |
|
||||||
$table->string('name'); |
|
||||||
$table->string('token', 64)->unique(); |
|
||||||
$table->text('abilities')->nullable(); |
|
||||||
$table->timestamp('last_used_at')->nullable(); |
|
||||||
$table->timestamp('expires_at')->nullable(); |
|
||||||
$table->timestamps(); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reverse the migrations. |
|
||||||
*/ |
|
||||||
public function down(): void |
|
||||||
{ |
|
||||||
Schema::dropIfExists('personal_access_tokens'); |
|
||||||
} |
|
||||||
}; |
|
Loading…
Reference in new issue