更新
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\common\enum;
|
||||
|
||||
/** 挂号的问诊方式,与诊单的初诊/复诊类型无关。 */
|
||||
class AppointmentTypeEnum
|
||||
{
|
||||
public const VIDEO = 'video';
|
||||
public const TEXT = 'text';
|
||||
|
||||
public static function isWritable($value): bool
|
||||
{
|
||||
return is_string($value) && in_array($value, [self::VIDEO, self::TEXT], true);
|
||||
}
|
||||
|
||||
/** 只给未传字段的新客户端请求补默认值;显式传入空值仍需校验。 */
|
||||
public static function withDefault(array $params): array
|
||||
{
|
||||
if (!array_key_exists('appointment_type', $params)) {
|
||||
$params['appointment_type'] = self::VIDEO;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/** 旧记录未填写类型时按视频展示,已记录的历史类型保持原值。 */
|
||||
public static function normalizeStored($value): string
|
||||
{
|
||||
return $value === null || (is_string($value) && trim($value) === '')
|
||||
? self::VIDEO
|
||||
: (string) $value;
|
||||
}
|
||||
|
||||
public static function description($value): string
|
||||
{
|
||||
return [
|
||||
self::VIDEO => '视频问诊',
|
||||
self::TEXT => '图文问诊',
|
||||
'phone' => '电话问诊', // 仅兼容已存在的历史记录,不允许新写入。
|
||||
][self::normalizeStored($value)] ?? '未知';
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace app\common\model\doctor;
|
||||
|
||||
use app\common\model\BaseModel;
|
||||
use app\common\enum\AppointmentTypeEnum;
|
||||
|
||||
/**
|
||||
* 医生预约模型
|
||||
@@ -58,12 +59,12 @@ class Appointment extends BaseModel
|
||||
*/
|
||||
public function getAppointmentTypeDescAttr($value, $data)
|
||||
{
|
||||
$typeMap = [
|
||||
'video' => '视频问诊',
|
||||
'text' => '图文问诊',
|
||||
'phone' => '电话问诊',
|
||||
];
|
||||
return $typeMap[$data['appointment_type']] ?? '未知';
|
||||
return AppointmentTypeEnum::description($data['appointment_type'] ?? null);
|
||||
}
|
||||
|
||||
public function getAppointmentTypeAttr($value)
|
||||
{
|
||||
return AppointmentTypeEnum::normalizeStored($value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,9 @@ class PrescriptionOrder extends BaseModel
|
||||
{
|
||||
use SoftDelete;
|
||||
|
||||
/** 已取消 / 已退款的历史订单不再占用处方;部分退款仍沿用原履约状态。 */
|
||||
public const RELEASED_PRESCRIPTION_STATUSES = [4, 10];
|
||||
|
||||
protected $name = 'tcm_prescription_order';
|
||||
|
||||
protected $deleteTime = 'delete_time';
|
||||
|
||||
@@ -261,8 +261,13 @@ class QywxPromotionMemberSchedulerService
|
||||
return ['pool_id' => $poolId, 'next_member_id' => 0, 'queued' => false, 'blocked' => true];
|
||||
}
|
||||
$appliedUserIds = self::linkRangeUserIds($linkId);
|
||||
$changed = !QywxPromotionMemberRange::same($range['userids'], $appliedUserIds);
|
||||
$alreadyPending = in_array((int) ($sync['status'] ?? 0), [1, 3], true);
|
||||
$departmentJson = (string) (Db::name('qywx_promotion_link')->where('id', $linkId)->value('range_department_json') ?? '[]');
|
||||
$departmentIds = json_decode($departmentJson, true);
|
||||
$changed = !QywxPromotionMemberRange::same($range['userids'], $appliedUserIds)
|
||||
|| !empty($departmentIds);
|
||||
// 活跃或超时的租约、尚未应用的版本都不能仅凭旧快照被重算成“已同步”。
|
||||
$alreadyPending = in_array((int) ($sync['status'] ?? 0), [1, 2, 3], true)
|
||||
|| (int) ($sync['desired_version'] ?? 0) > (int) ($sync['applied_version'] ?? 0);
|
||||
$needsSync = $changed || $alreadyPending;
|
||||
self::upsertSync($poolId, $linkId, $needsSync, $sync, $now);
|
||||
|
||||
|
||||
@@ -99,33 +99,42 @@ class QywxPromotionRangeSyncService
|
||||
$response = $this->api->getLink($remoteLinkId);
|
||||
$remote = QywxCustomerAcquisitionLinkService::normaliseRemoteResponse($response, $remoteLinkId);
|
||||
$actualUserIds = $remote['range_userids'];
|
||||
if (!QywxPromotionMemberRange::same($actualUserIds, $desiredUserIds)) {
|
||||
if (!QywxPromotionMemberRange::same($actualUserIds, $desiredUserIds)
|
||||
|| $remote['range_department_ids'] !== []) {
|
||||
throw new RuntimeException('企业微信返回的多人路由成员范围与方案可用医助不一致');
|
||||
}
|
||||
$url = $remote['url'];
|
||||
$snapshot = json_encode($remote['snapshot'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
Db::name('qywx_promotion_link')->where('id', (int) $link['id'])->update([
|
||||
'wecom_url' => $url,
|
||||
'remote_status' => 1,
|
||||
'range_user_json' => json_encode($actualUserIds, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
'range_department_json' => json_encode($remote['range_department_ids'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
'remote_snapshot' => $snapshot === false ? null : $snapshot,
|
||||
'last_sync_time' => time(),
|
||||
'sync_error' => '',
|
||||
'update_time' => time(),
|
||||
]);
|
||||
|
||||
$freshVersion = (int) (Db::name('qywx_promotion_range_sync')
|
||||
->where('pool_id', $poolId)->value('desired_version') ?? 0);
|
||||
Db::name('qywx_promotion_range_sync')
|
||||
->where('pool_id', $poolId)
|
||||
->where('lock_token', $token)
|
||||
->update([
|
||||
'status' => $freshVersion === $desiredVersion ? 0 : 1,
|
||||
$confirmed = Db::transaction(function () use (
|
||||
$poolId, $token, $desiredVersion, $link, $url, $actualUserIds, $remote, $snapshot
|
||||
): bool {
|
||||
$fresh = Db::name('qywx_promotion_range_sync')->where('pool_id', $poolId)->lock(true)->find();
|
||||
if (!$fresh || (int) ($fresh['status'] ?? 0) === 5) {
|
||||
return false;
|
||||
}
|
||||
if ((string) ($fresh['lock_token'] ?? '') !== $token
|
||||
|| (int) ($fresh['lock_until'] ?? 0) <= time()) {
|
||||
// 过期工作不能覆盖新工作的确认结果。它可能较晚触达企微,需再推一次最新范围。
|
||||
QywxPromotionMemberSchedulerService::requestPoolSync($poolId, (int) $fresh['promotion_link_id']);
|
||||
return false;
|
||||
}
|
||||
Db::name('qywx_promotion_link')->where('id', (int) $link['id'])->update([
|
||||
'wecom_url' => $url,
|
||||
'remote_status' => 1,
|
||||
'range_user_json' => json_encode($actualUserIds, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
'range_department_json' => json_encode($remote['range_department_ids'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
'remote_snapshot' => $snapshot === false ? null : $snapshot,
|
||||
'last_sync_time' => time(),
|
||||
'sync_error' => '',
|
||||
'update_time' => time(),
|
||||
]);
|
||||
$isLatest = (int) ($fresh['desired_version'] ?? 0) === $desiredVersion;
|
||||
Db::name('qywx_promotion_range_sync')->where('pool_id', $poolId)->update([
|
||||
'status' => $isLatest ? 0 : 1,
|
||||
'desired_member_id' => 0,
|
||||
'applied_member_id' => 0,
|
||||
'applied_version' => $desiredVersion,
|
||||
'next_retry' => $freshVersion === $desiredVersion ? 0 : time(),
|
||||
'next_retry' => $isLatest ? 0 : time(),
|
||||
'attempts' => 0,
|
||||
'lock_token' => '',
|
||||
'lock_until' => 0,
|
||||
@@ -133,13 +142,19 @@ class QywxPromotionRangeSyncService
|
||||
'update_time' => time(),
|
||||
]);
|
||||
|
||||
return ['status' => 'synced', 'pool_id' => $poolId, 'member_id' => 0];
|
||||
return $isLatest;
|
||||
});
|
||||
|
||||
return ['status' => $confirmed ? 'synced' : 'pending', 'pool_id' => $poolId, 'member_id' => 0];
|
||||
} catch (\Throwable $e) {
|
||||
$attempts = max(1, (int) ($claim['attempts'] ?? 0) + 1);
|
||||
Db::name('qywx_promotion_range_sync')
|
||||
->where('pool_id', $poolId)
|
||||
->where('lock_token', $token)
|
||||
->update([
|
||||
Db::transaction(function () use ($poolId, $token, $attempts, $claim, $e): void {
|
||||
$fresh = Db::name('qywx_promotion_range_sync')->where('pool_id', $poolId)->lock(true)->find();
|
||||
if (!$fresh || (string) ($fresh['lock_token'] ?? '') !== $token
|
||||
|| (int) ($fresh['status'] ?? 0) === 5) {
|
||||
return;
|
||||
}
|
||||
Db::name('qywx_promotion_range_sync')->where('pool_id', $poolId)->update([
|
||||
'status' => 3,
|
||||
'next_retry' => time() + min(300, 15 * $attempts),
|
||||
'lock_token' => '',
|
||||
@@ -147,9 +162,10 @@ class QywxPromotionRangeSyncService
|
||||
'last_error' => mb_substr($e->getMessage(), 0, 500),
|
||||
'update_time' => time(),
|
||||
]);
|
||||
Db::name('qywx_promotion_link')
|
||||
->where('id', (int) ($claim['promotion_link_id'] ?? 0))
|
||||
->update(['sync_error' => mb_substr($e->getMessage(), 0, 500), 'update_time' => time()]);
|
||||
Db::name('qywx_promotion_link')
|
||||
->where('id', (int) ($claim['promotion_link_id'] ?? 0))
|
||||
->update(['sync_error' => mb_substr($e->getMessage(), 0, 500), 'update_time' => time()]);
|
||||
});
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user