|
|
@@ -4,6 +4,7 @@ namespace bizHd\birthday\classes;
|
|
|
|
|
|
use biz\sj\classes\MerchantAssetClass;
|
|
|
use biz\sj\classes\SjClass;
|
|
|
+use bizHd\birthday\models\BirthdayGift;
|
|
|
use bizHd\custom\classes\CustomClass;
|
|
|
use bizHd\custom\classes\HdClass;
|
|
|
use bizHd\member\classes\MemberLevelClass;
|
|
|
@@ -671,8 +672,8 @@ class BirthdayGiftClass extends BaseClass
|
|
|
if (empty($merchant)) {
|
|
|
return [false, '商家信息不存在'];
|
|
|
}
|
|
|
- $mobile = $custom->mobile ?? '';
|
|
|
- if (empty($mobile)) {
|
|
|
+ $mobile = preg_replace('/\D/', '', (string)($custom->mobile ?? ''));
|
|
|
+ if (!preg_match('/^1\d{10}$/', $mobile)) {
|
|
|
return [false, '客户无手机号'];
|
|
|
}
|
|
|
// 短信余额查询及可发数量削减 TODO
|
|
|
@@ -692,22 +693,30 @@ class BirthdayGiftClass extends BaseClass
|
|
|
$msg = "{$shopName}:亲爱的会员您好,明天就是您生日啦!为您准备了专属生日礼品:{$giftDesc}。请您回复领取时间,如明天10点左右,24小时内回复有效。不领取请忽略短信,祝您生活愉快!拒收请回复R";
|
|
|
$sms = new ChuanglanSmsApi();
|
|
|
$sms->useAccount('marketing');
|
|
|
- $re = $sms->sendSMS($mobile, $msg);
|
|
|
+ if (empty($gift->claimToken)) {
|
|
|
+ return [false, '短信回执标识不存在'];
|
|
|
+ }
|
|
|
+ $giftId = intval($gift->id);
|
|
|
+ $callbackSign = hash_hmac('sha256', $giftId . '|' . $mobile, (string)$gift->claimToken);
|
|
|
+ $callbackUid = $giftId . '.' . $callbackSign;
|
|
|
+ $callbackUrl = Yii::$app->params['hdHost'] . '/birthday-gift/notify-state';
|
|
|
+ $re = $sms->sendSMS($mobile, $msg, true, $callbackUrl, $callbackUid);
|
|
|
Yii::info('生日礼品短信返回:' . json_encode($re, JSON_UNESCAPED_UNICODE));
|
|
|
$re = json_decode($re, true);
|
|
|
- if ($re['code'] != '000000') {
|
|
|
- Yii::error('生日礼品短信发送失败:' . $re['errorMsg']);
|
|
|
+ if (!is_array($re) || ($re['code'] ?? '') != '000000') {
|
|
|
+ Yii::error('生日礼品短信发送失败:' . ($re['errorMsg'] ?? '响应格式错误'));
|
|
|
return [false, '短信发送失败'];
|
|
|
}
|
|
|
|
|
|
// TODO 记录到 xhSms
|
|
|
$sms = new SmsClass();
|
|
|
$sms->add([
|
|
|
- 'merchantId' => $sjId,
|
|
|
+ 'mainId' => $shop->mainId,
|
|
|
+ 'shopId' => $shop->id,
|
|
|
'userId' => $custom->userId,
|
|
|
+ 'mobile' => $mobile,
|
|
|
'content' => $msg,
|
|
|
- 'type' => '2', // 1.通知短信,2.营销短信
|
|
|
- 'addTime' => time()
|
|
|
+ 'type' => 1, // 0通知类型, 1营销类型
|
|
|
]);
|
|
|
|
|
|
return [true, ''];
|
|
|
@@ -749,18 +758,90 @@ class BirthdayGiftClass extends BaseClass
|
|
|
if (!$ok) {
|
|
|
return [false, $err];
|
|
|
}
|
|
|
+ return [true, ''];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理创蓝短信状态回执。只有 DELIVRD 才推进生日赠礼业务。
|
|
|
+ */
|
|
|
+ public static function handleNotifyState($params)
|
|
|
+ {
|
|
|
+ Yii::info('生日礼品短信状态回调:' . json_encode($params, JSON_UNESCAPED_UNICODE), __METHOD__);
|
|
|
+
|
|
|
+ $uid = trim((string)($params['uid'] ?? ''));
|
|
|
+ $uidParts = explode('.', $uid, 2);
|
|
|
+ $giftId = intval($uidParts[0] ?? 0);
|
|
|
+ $sign = strtolower(trim((string)($uidParts[1] ?? '')));
|
|
|
+ $msgId = trim((string)($params['msgid'] ?? ''));
|
|
|
+ $mobile = preg_replace('/\D/', '', (string)($params['mobile'] ?? ''));
|
|
|
+ $status = strtoupper(trim((string)($params['status'] ?? '')));
|
|
|
+ if ($giftId <= 0 || !preg_match('/^[a-f0-9]{64}$/', $sign) || !preg_match('/^\d{32}$/', $msgId) || !preg_match('/^1\d{10}$/', $mobile) || $status === '') {
|
|
|
+ Yii::warning('生日礼品短信状态回调参数不完整', __METHOD__);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ $gift = self::getById($giftId, true);
|
|
|
+ if (empty($gift) || empty($gift->claimToken)) {
|
|
|
+ Yii::warning('生日礼品短信状态回调标识无效:giftId=' . $giftId . ' msgId=' . $msgId, __METHOD__);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ $expectedSign = hash_hmac('sha256', $giftId . '|' . $mobile, (string)$gift->claimToken);
|
|
|
+ if (!hash_equals($expectedSign, $sign)) {
|
|
|
+ Yii::warning('生日礼品短信状态回调手机号不匹配:giftId=' . $giftId . ' msgId=' . $msgId, __METHOD__);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($status !== 'DELIVRD') {
|
|
|
+ $statusDesc = urldecode((string)($params['statusDesc'] ?? ''));
|
|
|
+ Yii::warning('生日礼品短信未送达:giftId=' . $giftId . ' msgId=' . $msgId . ' status=' . $status . ' statusDesc=' . $statusDesc, __METHOD__);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return self::confirmNotifyDelivered($giftId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 确认短信送达后,原子更新赠礼状态并创建过期延迟消息。
|
|
|
+ */
|
|
|
+ public static function confirmNotifyDelivered($giftId)
|
|
|
+ {
|
|
|
+ $gift = self::getById($giftId, true);
|
|
|
+ if (empty($gift) || intval($gift->smsSent) === 1) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if (intval($gift->status) !== self::STATUS_PENDING_NOTIFY) {
|
|
|
+ Yii::warning('短信已送达,但生日赠礼状态不可推进:giftId=' . intval($giftId), __METHOD__);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
$now = date('Y-m-d H:i:s');
|
|
|
$expire = date('Y-m-d H:i:s', time() + self::EXPIRE_SECONDS);
|
|
|
- $gift->status = self::STATUS_PENDING_CLAIM;
|
|
|
- $gift->notifyTime = $now;
|
|
|
- $gift->expireTime = $expire;
|
|
|
- $gift->smsSent = 1;
|
|
|
- if (empty($gift->orderSn)) {
|
|
|
- $gift->orderSn = 'BG' . date('ymdHis') . mt_rand(1000, 9999);
|
|
|
+ $orderSn = empty($gift->orderSn) ? 'BG' . date('ymdHis') . mt_rand(1000, 9999) : $gift->orderSn;
|
|
|
+ $transaction = Yii::$app->db->beginTransaction();
|
|
|
+ try {
|
|
|
+ $affectedRows = BirthdayGift::updateAll([
|
|
|
+ 'status' => self::STATUS_PENDING_CLAIM,
|
|
|
+ 'notifyTime' => $now,
|
|
|
+ 'expireTime' => $expire,
|
|
|
+ 'smsSent' => 1,
|
|
|
+ 'orderSn' => $orderSn,
|
|
|
+ ], [
|
|
|
+ 'id' => intval($giftId),
|
|
|
+ 'status' => self::STATUS_PENDING_NOTIFY,
|
|
|
+ 'smsSent' => 0,
|
|
|
+ ]);
|
|
|
+ if ($affectedRows > 0) {
|
|
|
+ self::scheduleExpireDelay($giftId, strtotime($expire));
|
|
|
+ }
|
|
|
+ $transaction->commit();
|
|
|
+ } catch (\Throwable $throwable) {
|
|
|
+ if ($transaction->getIsActive()) {
|
|
|
+ $transaction->rollBack();
|
|
|
+ }
|
|
|
+ throw $throwable;
|
|
|
}
|
|
|
- $gift->save();
|
|
|
- self::scheduleExpireDelay($gift->id, strtotime($expire));
|
|
|
- return [true, ''];
|
|
|
+
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
public static function notifyOneTomorrow($shop, $giftId)
|