diff --git a/Laravel/app/Http/Controllers/API/SmsController.php b/Laravel/app/Http/Controllers/API/SmsController.php
index c73042e..001537f 100644
--- a/Laravel/app/Http/Controllers/API/SmsController.php
+++ b/Laravel/app/Http/Controllers/API/SmsController.php
@@ -68,4 +68,40 @@ class SmsController
}
return $code;
}
+
+ public function CallBack(Request $request)
+ {
+ // 1. 解析 JSON 数据 (腾讯云回调是数组格式)
+ $dataList = json_decode($request->getContent(), true);
+
+ // 兼容:如果不是数组,强制转为数组
+ if (!is_array($dataList)) {
+ $dataList = $dataList ? [$dataList] : [];
+ }
+
+ foreach ($dataList as $item) {
+ $sid = $item['sid'] ?? null;
+
+ // 2. 只有当 sid 存在时才处理
+ if ($sid) {
+ // 3. 查找并更新:找到就存 JSON,找不到自动忽略 (update 影响行数为 0)
+ DB::table('sms_log')
+ ->where('sid', $sid)
+ ->update([
+ 'callback_content' => json_encode($item, JSON_UNESCAPED_UNICODE),
+ 'updated_at' => now()
+ ]);
+ }
+ }
+
+ // 4. 必须按文档返回特定格式,否则腾讯云会重试
+ return response()->json(['result' => 0, 'errmsg' => 'OK']);
+ }
+
+ public function TestSend()
+ {
+ $service = new TencentSmsApiService();
+ $ss= $service->send('19933509886',"2567513", ["小明","转入"]);
+ }
+
}
diff --git a/Laravel/app/Services/TencentSmsApiService.php b/Laravel/app/Services/TencentSmsApiService.php
index d252968..08cbbc2 100644
--- a/Laravel/app/Services/TencentSmsApiService.php
+++ b/Laravel/app/Services/TencentSmsApiService.php
@@ -2,6 +2,7 @@
namespace App\Services;
+use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
@@ -27,13 +28,14 @@ class TencentSmsApiService
$this->templateId = "2564483";
}
- public function send(string $phoneNumber,string $templateId, array $params = [])
+ public function send(string $phoneNumber, string $templateId, array $params = [])
{
+ // 1. 基础配置与时间戳
$action = 'SendSms';
$timestamp = time();
$date = gmdate('Y-m-d', $timestamp);
- // 构造请求体
+ // 2. 构造请求体 (Body)
$body = [
'PhoneNumberSet' => [$phoneNumber],
'SmsSdkAppId' => $this->sdkAppId,
@@ -45,46 +47,124 @@ class TencentSmsApiService
$body['TemplateParamSet'] = $params;
}
+ // 注意:JSON_UNESCAPED_UNICODE 和 JSON_UNESCAPED_SLASHES 是腾讯云签名必须的
$payload = json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
-// Canonical Request
+ // 3. 构造规范请求 (Canonical Request)
$canonicalUri = '/';
$canonicalQueryString = '';
$canonicalHeaders = "content-type:application/json; charset=utf-8\nhost:{$this->host}\n";
$signedHeaders = 'content-type;host';
$hashedPayload = hash('sha256', $payload);
+
$canonicalRequest = "POST\n{$canonicalUri}\n{$canonicalQueryString}\n{$canonicalHeaders}\n{$signedHeaders}\n{$hashedPayload}";
-// String to Sign
+ // 4. 构造待签名字符串 (String to Sign)
$algorithm = 'TC3-HMAC-SHA256';
- $date = gmdate('Y-m-d', $timestamp);
$credentialScope = "{$date}/{$this->service}/tc3_request";
$hashedCanonicalRequest = hash('sha256', $canonicalRequest);
$stringToSign = "{$algorithm}\n{$timestamp}\n{$credentialScope}\n{$hashedCanonicalRequest}";
-// Signature
+ // 5. 计算签名 (Signature)
$kSecret = 'TC3' . $this->secretKey;
$kDate = hash_hmac('sha256', $date, $kSecret, true);
$kService = hash_hmac('sha256', $this->service, $kDate, true);
$kSigning = hash_hmac('sha256', 'tc3_request', $kService, true);
$signature = hash_hmac('sha256', $stringToSign, $kSigning, true);
- $signature = bin2hex($signature); // 转为小写 hex 字符串!
+ $signature = bin2hex($signature);
-// Authorization
+ // 6. 构造 Authorization 头
$authorization = "{$algorithm} Credential={$this->secretId}/{$credentialScope}, SignedHeaders={$signedHeaders}, Signature={$signature}";
-// 发送请求:必须用 $payload 字符串,不能传数组!
- $response = Http::withHeaders([
- 'Authorization' => $authorization,
- 'Content-Type' => 'application/json; charset=utf-8',
- 'Host' => $this->host,
- 'X-TC-Action' => $action,
- 'X-TC-Timestamp' => $timestamp,
- 'X-TC-Version' => $this->version,
- 'X-TC-Region' => $this->region,
- ])->withBody($payload, 'application/json; charset=utf-8')
- ->post("https://{$this->endpoint}");
-
- return $response->json();
+ // 7. 准备数据库初始数据 (此时尚未插入,等待请求结果)
+ $logData = [
+ 'phone' => $phoneNumber,
+ 'content' => json_encode($params, JSON_UNESCAPED_UNICODE),
+ 'sid' => null,
+ 'status' => 'sending', // 初始状态
+ 'remark' => null
+ ];
+
+ $msgId = null;
+
+ try {
+ // 8. 发送 HTTP 请求
+ $response = Http::withHeaders([
+ 'Authorization' => $authorization,
+ 'Content-Type' => 'application/json; charset=utf-8',
+ 'Host' => $this->host,
+ 'X-TC-Action' => $action,
+ 'X-TC-Timestamp' => (string)$timestamp,
+ 'X-TC-Version' => $this->version,
+ 'X-TC-Region' => $this->region,
+ ])
+ ->withBody($payload, 'application/json; charset=utf-8')
+ ->post("https://{$this->endpoint}");
+
+ // 9. 解析响应数据
+ $responseData = $response->json();
+
+ // 初始化提取变量
+ $serialNo = null;
+ $code = 'Unknown';
+ $message = 'Unknown response format';
+ $isSuccess = false;
+
+ // 防御性检查:确保 Response 结构和 SendStatusSet 存在
+ if (isset($responseData['Response'])
+ && isset($responseData['Response']['SendStatusSet'])
+ && is_array($responseData['Response']['SendStatusSet'])
+ && count($responseData['Response']['SendStatusSet']) > 0) {
+
+ $statusInfo = $responseData['Response']['SendStatusSet'][0];
+
+ $serialNo = $statusInfo['SerialNo'] ?? null;
+ $code = $statusInfo['Code'] ?? 'UnknownCode';
+ $message = $statusInfo['Message'] ?? 'No message returned';
+
+ // 腾讯云成功标识通常为 "Ok"
+ if ($code === 'Ok') {
+ $isSuccess = true;
+ }
+ } else {
+ // 如果返回结构完全不对(例如鉴权失败直接返回 Error 对象)
+ if (isset($responseData['Response']['Error'])) {
+ $code = $responseData['Response']['Error']['Code'] ?? 'ApiError';
+ $message = $responseData['Response']['Error']['Message'] ?? 'API Error occurred';
+ } else {
+ $message = 'Invalid response structure from provider';
+ }
+ }
+
+ // 10. 更新日志数据
+ $logData['sid'] = $serialNo;
+ $logData['status'] = $code; // 存储业务状态码 (Ok 或 FailedOperation.xxx)
+ $logData['remark'] = $message; // 存储具体错误信息
+
+ // 11. 写入数据库 (一次性插入完整记录)
+ $msgId = DB::table('sms_log')->insertGetId($logData);
+
+ // 如果业务逻辑失败(有 sid 但 code 不是 Ok),可以选择记录警告日志
+ if (!$isSuccess) {
+ // 这里可以根据需求选择是否抛出异常,或者仅记录日志
+ // \Log::warning("SMS Send Failed: {$phoneNumber}, Code: {$code}, Msg: {$message}, Sid: {$serialNo}");
+ }
+
+ return $responseData;
+
+ } catch (\Exception $e) {
+ // 12. 异常处理:网络错误、超时、签名计算错误等
+
+ // 更新日志数据为失败状态
+ $logData['status'] = 'RequestFailed'; // 标记为请求级失败
+ $logData['remark'] = $e->getMessage();
+ // sid 保持 null
+
+ // 写入数据库,确保不留脏数据
+ $msgId = DB::table('sms_log')->insertGetId($logData);
+
+ // 重新抛出异常,让上层调用者知道发送失败了
+ throw $e;
+ }
}
}
diff --git a/Laravel/routes/api.php b/Laravel/routes/api.php
index 34d94ad..ef51213 100644
--- a/Laravel/routes/api.php
+++ b/Laravel/routes/api.php
@@ -93,5 +93,6 @@ Route::post('mp/InsertInfo','App\Http\Controllers\API\LiuYanController@Mp_Insert
-
+Route::any('/SmsCallBack','App\Http\Controllers\API\SmsController@CallBack' )->middleware('log');
+Route::any('/TestSendSms','App\Http\Controllers\API\SmsController@TestSend' )->middleware('log');//测试发送短信
diff --git a/Laravel/routes/web.php b/Laravel/routes/web.php
index 08abe1d..eaecb15 100644
--- a/Laravel/routes/web.php
+++ b/Laravel/routes/web.php
@@ -25,4 +25,7 @@ Route::get('/wxLogin/{env}', function ($env) {
});
//微信登录授权获取openid
Route::get('/wxGetCode','App\Http\Controllers\API\mH5\LoginController@wxGetCode' );
+
+
+
Route::get('/test','App\Http\Controllers\TestController@DBtest' );
diff --git a/admin/src/router/index.js b/admin/src/router/index.js
index 7eb63f2..a920dad 100644
--- a/admin/src/router/index.js
+++ b/admin/src/router/index.js
@@ -136,6 +136,14 @@ const router = createRouter({
title: '日切'
}
},
+ {
+ path: '/systemMngr/smsLog',
+ name: 'SystemMngrSmsLog',
+ component: () => import('../views/SystemMngr/SmsLog.vue'),
+ meta: {
+ title: '短信日志'
+ }
+ },
{
path: '/yewu/weixinUserList',
name: 'YeWuWeixinUserList',
diff --git a/admin/src/views/SystemMngr/SmsLog.vue b/admin/src/views/SystemMngr/SmsLog.vue
new file mode 100644
index 0000000..02409e3
--- /dev/null
+++ b/admin/src/views/SystemMngr/SmsLog.vue
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file