677 lines
24 KiB
PHP
677 lines
24 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace app\admin\controller\System;
|
||
|
||
use app\admin\BaseController;
|
||
use app\model\System\SystemSiteSettings;
|
||
use think\facade\Db;
|
||
use think\facade\Log;
|
||
use think\db\exception\DbException;
|
||
use think\exception\ValidateException;
|
||
use think\facade\Request;
|
||
|
||
class SmsController extends BaseController
|
||
{
|
||
private function getSiteSettingValue(string $label): string
|
||
{
|
||
$row = SystemSiteSettings::where('label', $label)->order('id', 'asc')->find();
|
||
if (!$row) {
|
||
return '';
|
||
}
|
||
return (string)$row['value'];
|
||
}
|
||
|
||
private function upsertSiteSetting(string $label, string $value): void
|
||
{
|
||
$row = SystemSiteSettings::where('label', $label)->order('id', 'asc')->find();
|
||
if ($row) {
|
||
SystemSiteSettings::update(['value' => $value], ['id' => $row['id']]);
|
||
return;
|
||
}
|
||
|
||
$model = new SystemSiteSettings();
|
||
$model->label = $label;
|
||
$model->value = $value;
|
||
$model->sort = 0;
|
||
$model->save();
|
||
}
|
||
|
||
/**
|
||
* 获取短信网关配置
|
||
*/
|
||
public function getSmsInfo()
|
||
{
|
||
try {
|
||
$backendUrl = $this->getSiteSettingValue('backendUrl');
|
||
$apiKey = $this->getSiteSettingValue('apiKey');
|
||
|
||
// 前端约定返回 data[0],字段名兼容 backend_url/api_key 与 backendUrl/apiKey
|
||
$data = [[
|
||
'backend_url' => $backendUrl,
|
||
'api_key' => $apiKey,
|
||
'backendUrl' => $backendUrl,
|
||
'apiKey' => $apiKey,
|
||
]];
|
||
|
||
$this->logSuccess('短信管理', '获取短信网关配置', ['data' => $data]);
|
||
|
||
return json([
|
||
'code' => 200,
|
||
'msg' => '获取成功',
|
||
'data' => $data,
|
||
]);
|
||
} catch (DbException $e) {
|
||
$this->logFail('短信管理', '获取短信网关配置', $e->getMessage());
|
||
return json([
|
||
'code' => 500,
|
||
'msg' => '获取失败:' . $e->getMessage(),
|
||
'data' => [],
|
||
]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 编辑短信网关配置(本表设计为单条配置)
|
||
*/
|
||
public function editSmsInfo()
|
||
{
|
||
try {
|
||
$raw = $this->request->post();
|
||
$data = [
|
||
'backend_url' => $raw['backendUrl'] ?? $raw['backend_url'] ?? '',
|
||
'api_key' => $raw['apiKey'] ?? $raw['api_key'] ?? '',
|
||
];
|
||
|
||
$this->validate($data, [
|
||
'backend_url|短信网关地址' => 'require|max:255',
|
||
'api_key|短信网关 API KEY' => 'require|max:255',
|
||
]);
|
||
|
||
$this->upsertSiteSetting('backendUrl', (string)$data['backend_url']);
|
||
$this->upsertSiteSetting('apiKey', (string)$data['api_key']);
|
||
|
||
$updated = [[
|
||
'backend_url' => $this->getSiteSettingValue('backendUrl'),
|
||
'api_key' => $this->getSiteSettingValue('apiKey'),
|
||
]];
|
||
|
||
$this->logSuccess('短信管理', '更新短信网关配置', ['data' => $updated]);
|
||
|
||
return json([
|
||
'code' => 200,
|
||
'msg' => '更新成功',
|
||
'data' => $updated,
|
||
]);
|
||
} catch (ValidateException $e) {
|
||
$this->logFail('短信管理', '更新短信网关配置', $e->getMessage());
|
||
return json([
|
||
'code' => 400,
|
||
'msg' => $e->getError(),
|
||
]);
|
||
} catch (DbException $e) {
|
||
$this->logFail('短信管理', '更新短信网关配置', $e->getMessage());
|
||
return json([
|
||
'code' => 500,
|
||
'msg' => '更新失败:' . $e->getMessage(),
|
||
]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 后台发起“短信测试”:入队一条任务,等待 Android 网关轮询取走并发送
|
||
*/
|
||
public function sendTestSms()
|
||
{
|
||
try {
|
||
$raw = $this->request->post();
|
||
$phone = trim((string)($raw['phone'] ?? $raw['testPhone'] ?? ''));
|
||
$content = trim((string)($raw['content'] ?? ''));
|
||
|
||
if (empty($phone)) {
|
||
return json(['code' => 400, 'msg' => '缺少测试手机号']);
|
||
}
|
||
|
||
// Android 网关文档:国际格式 +8613712345678
|
||
if (!preg_match('/^\+\d{6,15}$/', $phone)) {
|
||
return json(['code' => 400, 'msg' => '请使用国际格式手机号(以 + 开头,后为数字)']);
|
||
}
|
||
|
||
// 获取当前网关配置(来自 mete_system_site_settings label/value)
|
||
$apiKey = $this->getSiteSettingValue('apiKey');
|
||
$backendUrl = $this->getSiteSettingValue('backendUrl');
|
||
if (empty($apiKey)) {
|
||
return json(['code' => 400, 'msg' => '请先配置短信网关 API KEY']);
|
||
}
|
||
if (empty($backendUrl)) {
|
||
return json(['code' => 400, 'msg' => '请先配置短信网关地址 backendUrl']);
|
||
}
|
||
|
||
$code = (string)random_int(100000, 999999);
|
||
if ($content === '') {
|
||
$content = '短信测试验证码:' . $code;
|
||
}
|
||
|
||
// 兼容:调用 Uniapp/sms 后端入队发送任务
|
||
$enqueueUrl = rtrim($backendUrl, '/') . '/api/v1/business/outbound-tasks';
|
||
$enqueuePayload = [
|
||
'phone' => $phone,
|
||
'content' => $content,
|
||
];
|
||
$enqueueHeaders = [
|
||
'X-Api-Key: ' . (string)$apiKey,
|
||
'Content-Type: application/json; charset=utf-8',
|
||
'Accept: application/json',
|
||
];
|
||
|
||
Log::write('[sms_enqueue] url=' . $enqueueUrl . ' phone=' . $phone);
|
||
$enqueueResp = $this->postJson($enqueueUrl, $enqueuePayload, $enqueueHeaders);
|
||
if (empty($enqueueResp['ok'])) {
|
||
Log::write('[sms_enqueue] failed http_status=' . ($enqueueResp['status'] ?? '') . ' body=' . ($enqueueResp['body'] ?? ''));
|
||
return json([
|
||
'code' => 500,
|
||
'msg' => '短信网关入队失败:' . ($enqueueResp['error'] ?? 'unknown'),
|
||
]);
|
||
}
|
||
|
||
$smsGatewayTaskId = (string)($enqueueResp['json']['taskId'] ?? '');
|
||
Log::write('[sms_enqueue] success taskId=' . $smsGatewayTaskId);
|
||
|
||
$enqueueDebug = [
|
||
'enqueueOk' => (bool)($enqueueResp['ok'] ?? false),
|
||
'enqueueStatus' => $enqueueResp['status'] ?? null,
|
||
'enqueueJsonKeys' => array_keys((array)($enqueueResp['json'] ?? [])),
|
||
'gatewayTaskId' => $smsGatewayTaskId,
|
||
];
|
||
|
||
// 同步一条本地记录(用于前端列表展示/对账)
|
||
$now = date('Y-m-d H:i:s');
|
||
$taskId = Db::name('mete_system_sms_tasks')->insertGetId([
|
||
'api_key' => (string)$apiKey,
|
||
'tid' => $this->getTenantId(),
|
||
'phone' => $phone,
|
||
'content' => $content,
|
||
'status' => 0, // pending(本地不自动同步网关状态)
|
||
'code' => $code, // 预期验证码(用于对账/展示)
|
||
'create_time' => $now,
|
||
'update_time' => $now,
|
||
]);
|
||
|
||
$this->logSuccess('短信管理', '发送测试短信(调用网关入队)', [
|
||
'to' => $phone,
|
||
'local_task_id' => $taskId ?? null,
|
||
'gateway_task_id' => $smsGatewayTaskId,
|
||
]);
|
||
|
||
return json([
|
||
'code' => 200,
|
||
'msg' => '测试短信任务已入队,等待网关发送',
|
||
'data' => [
|
||
'taskId' => $smsGatewayTaskId ?: ($taskId ?? null),
|
||
'code' => $code,
|
||
],
|
||
'debug' => empty($smsGatewayTaskId) ? $enqueueDebug : null,
|
||
]);
|
||
} catch (ValidateException $e) {
|
||
$this->logFail('短信管理', '发送测试短信', $e->getMessage());
|
||
return json(['code' => 400, 'msg' => $e->getError()]);
|
||
} catch (\Throwable $e) {
|
||
$this->logFail('短信管理', '发送测试短信', $e->getMessage());
|
||
return json(['code' => 500, 'msg' => '发送失败:' . $e->getMessage()]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 简单 JSON POST(无 curl 扩展时使用)
|
||
* @return array{ok:bool,status?:int,body?:string,error?:string,json?:array}
|
||
*/
|
||
private function postJson(string $url, array $payload, array $headers): array
|
||
{
|
||
$body = json_encode($payload, JSON_UNESCAPED_UNICODE);
|
||
if ($body === false) {
|
||
return ['ok' => false, 'error' => 'json_encode failed'];
|
||
}
|
||
|
||
$headerStr = implode("\r\n", $headers);
|
||
$context = stream_context_create([
|
||
'http' => [
|
||
'method' => 'POST',
|
||
'header' => $headerStr,
|
||
'content' => $body,
|
||
'timeout' => 10,
|
||
],
|
||
]);
|
||
|
||
$respBody = @file_get_contents($url, false, $context);
|
||
$status = 0;
|
||
if (!empty($http_response_header) && is_array($http_response_header)) {
|
||
foreach ($http_response_header as $line) {
|
||
if (preg_match('#^HTTP/\\S+\\s+(\\d+)#', $line, $m)) {
|
||
$status = (int)$m[1];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if ($respBody === false) {
|
||
$last = error_get_last();
|
||
$errMsg = $last['message'] ?? 'http request failed';
|
||
return ['ok' => false, 'status' => $status, 'error' => (string)$errMsg];
|
||
}
|
||
|
||
$decoded = null;
|
||
$decoded = json_decode((string)$respBody, true);
|
||
if (!is_array($decoded)) {
|
||
$decoded = [];
|
||
}
|
||
|
||
$ok = $status >= 200 && $status < 300;
|
||
return [
|
||
'ok' => $ok,
|
||
'status' => $status,
|
||
'body' => (string)$respBody,
|
||
'json' => $decoded,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 简单 JSON GET(无 curl 扩展时使用)
|
||
* @return array{ok:bool,status?:int,body?:string,error?:string,json?:array}
|
||
*/
|
||
private function httpGetJson(string $url, array $headers): array
|
||
{
|
||
$headerStr = implode("\r\n", $headers);
|
||
$context = stream_context_create([
|
||
'http' => [
|
||
'method' => 'GET',
|
||
'header' => $headerStr,
|
||
'timeout' => 10,
|
||
// 对 4xx/5xx 也返回响应体,便于排障
|
||
'ignore_errors' => true,
|
||
],
|
||
]);
|
||
|
||
$respBody = @file_get_contents($url, false, $context);
|
||
$status = 0;
|
||
if (!empty($http_response_header) && is_array($http_response_header)) {
|
||
foreach ($http_response_header as $line) {
|
||
if (preg_match('#^HTTP/\\S+\\s+(\\d+)#', $line, $m)) {
|
||
$status = (int)$m[1];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if ($respBody === false) {
|
||
$last = error_get_last();
|
||
$errMsg = $last['message'] ?? 'http request failed';
|
||
return ['ok' => false, 'status' => $status, 'error' => (string)$errMsg];
|
||
}
|
||
|
||
$decoded = json_decode((string)$respBody, true);
|
||
if (!is_array($decoded)) {
|
||
$decoded = [];
|
||
}
|
||
|
||
$ok = $status >= 200 && $status < 300;
|
||
return [
|
||
'ok' => $ok,
|
||
'status' => $status,
|
||
'body' => (string)$respBody,
|
||
'json' => $decoded,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Android 网关轮询:获取待发送任务
|
||
*
|
||
* 约定:
|
||
* - apiKey 通过 header `X-API-KEY` / `X-SMS-GATEWAY-API-KEY` 或 body/query `apiKey`
|
||
*/
|
||
public function gatewayPoll()
|
||
{
|
||
$apiKey = $this->getApiKeyFromRequest();
|
||
if (empty($apiKey)) {
|
||
Log::write('[sms_gateway_poll] missing apiKey');
|
||
return json(['code' => 401, 'msg' => '缺少 apiKey']);
|
||
}
|
||
|
||
// 不强制要求 mete_system_site_settings(apiKey) 与网关入参完全一致;
|
||
// 直接按任务表的 api_key 匹配,提高兼容性与排障速度。
|
||
$apiKeySetting = $this->getSiteSettingValue('apiKey');
|
||
if (empty($apiKeySetting) || (string)$apiKeySetting !== (string)$apiKey) {
|
||
Log::write('[sms_gateway_poll] apiKey differs from setting; setting=' . $this->maskKey($apiKeySetting) . ' req=' . $this->maskKey($apiKey));
|
||
}
|
||
|
||
$task = Db::name('mete_system_sms_tasks')
|
||
->where('api_key', (string)$apiKey)
|
||
->where('status', 0)
|
||
->where('delete_time', null)
|
||
->order('id', 'asc')
|
||
->find();
|
||
|
||
// 没有任务
|
||
if (!$task) {
|
||
Log::write('[sms_gateway_poll] no task for apiKey=' . $this->maskKey($apiKey));
|
||
return json([
|
||
'code' => 200,
|
||
'msg' => 'no task',
|
||
'data' => null,
|
||
]);
|
||
}
|
||
|
||
// 标记为处理中,避免被重复领取
|
||
Db::name('mete_system_sms_tasks')->where('id', (int)$task['id'])->update([
|
||
'status' => 1,
|
||
'update_time' => date('Y-m-d H:i:s'),
|
||
]);
|
||
|
||
Log::write('[sms_gateway_poll] send task id=' . (int)$task['id'] . ' phone=' . (string)$task['phone']);
|
||
|
||
return json([
|
||
'code' => 200,
|
||
'msg' => 'success',
|
||
'data' => [
|
||
'taskId' => $task['id'],
|
||
'task_id' => $task['id'],
|
||
'phone' => $task['phone'],
|
||
'content' => $task['content'],
|
||
'smsContent' => $task['content'],
|
||
],
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* Android 网关上报:上传实际收到的短信内容并解析验证码
|
||
*/
|
||
public function gatewayReport()
|
||
{
|
||
try {
|
||
$raw = $this->request->post();
|
||
$apiKey = $this->getApiKeyFromRequest();
|
||
$taskId = $raw['taskId'] ?? $raw['task_id'] ?? $raw['id'] ?? null;
|
||
$smsContent = trim((string)($raw['smsContent'] ?? $raw['sms_content'] ?? $raw['content'] ?? $raw['message'] ?? $raw['sms'] ?? ''));
|
||
$status = $raw['status'] ?? $raw['result'] ?? $raw['state'] ?? null; // success/failed/int
|
||
|
||
if (empty($apiKey)) {
|
||
return json(['code' => 401, 'msg' => '缺少 apiKey']);
|
||
}
|
||
if (empty($taskId)) {
|
||
return json(['code' => 400, 'msg' => '缺少 taskId']);
|
||
}
|
||
if ($smsContent === '') {
|
||
return json(['code' => 400, 'msg' => '缺少 smsContent']);
|
||
}
|
||
|
||
$task = Db::name('mete_system_sms_tasks')
|
||
->where('id', (int)$taskId)
|
||
->where('api_key', (string)$apiKey)
|
||
->where('delete_time', null)
|
||
->find();
|
||
|
||
if (!$task) {
|
||
Log::write('[sms_gateway_report] task not found; taskId=' . (int)$taskId . ' apiKey=' . $this->maskKey($apiKey));
|
||
return json(['code' => 404, 'msg' => '任务不存在或 apiKey 不匹配']);
|
||
}
|
||
|
||
Log::write('[sms_gateway_report] received taskId=' . (int)$taskId . ' status=' . (string)($status ?? ''));
|
||
|
||
// 从短信内容里抓取验证码(通用策略:4-6 位数字)
|
||
$parsedCode = '';
|
||
if (preg_match('/(\d{4,6})/', $smsContent, $m)) {
|
||
$parsedCode = (string)$m[1];
|
||
}
|
||
|
||
$update = [
|
||
'report_raw' => $smsContent,
|
||
'code' => $parsedCode !== '' ? $parsedCode : (string)$task['code'],
|
||
'update_time' => date('Y-m-d H:i:s'),
|
||
];
|
||
|
||
if ($status === 'failed' || $status === 'error' || $status === 2) {
|
||
$update['status'] = 2;
|
||
} elseif ($status === 'success' || $status === 'reported' || $status === 3 || $status === 1) {
|
||
// success => reported/done
|
||
$update['status'] = 3;
|
||
} else {
|
||
// 默认当做已上报
|
||
$update['status'] = 3;
|
||
}
|
||
|
||
Db::name('mete_system_sms_tasks')->where('id', (int)$task['id'])->update($update);
|
||
|
||
$this->logSuccess('短信管理', '网关上报短信(解析验证码)', [
|
||
'task_id' => $task['id'],
|
||
'parsed_code' => $update['code'],
|
||
]);
|
||
|
||
return json(['code' => 200, 'msg' => '上报成功']);
|
||
} catch (DbException $e) {
|
||
$this->logFail('短信管理', '网关上报短信', $e->getMessage());
|
||
return json(['code' => 500, 'msg' => '服务器错误:' . $e->getMessage()]);
|
||
} catch (\Throwable $e) {
|
||
$this->logFail('短信管理', '网关上报短信', $e->getMessage());
|
||
return json(['code' => 500, 'msg' => '服务器错误:' . $e->getMessage()]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 管理员:获取短信任务列表(租户隔离)
|
||
*
|
||
* 支持参数(可选):
|
||
* - status: 任务状态
|
||
* - phone: 手机号关键词
|
||
*/
|
||
public function getSmsTaskList()
|
||
{
|
||
$backendUrl = $this->getSiteSettingValue('backendUrl');
|
||
$apiKey = $this->getSiteSettingValue('apiKey');
|
||
|
||
if (empty($backendUrl) || empty($apiKey)) {
|
||
return json([
|
||
'code' => 400,
|
||
'msg' => '短信网关配置未完成(backendUrl/apiKey 缺失)',
|
||
'list' => [],
|
||
]);
|
||
}
|
||
|
||
$status = (string)Request::param('status', '');
|
||
$phoneKeyword = trim((string)Request::param('phone', ''));
|
||
$limit = (int)Request::param('limit', 50, 'int');
|
||
$limit = $limit > 0 ? min($limit, 200) : 50;
|
||
|
||
// 前端 status: 0/1/2/3 -> uniapp backend status: pending/sending/failed/success
|
||
$statusMap = [
|
||
'0' => 'pending',
|
||
'1' => 'sending',
|
||
'2' => 'failed',
|
||
'3' => 'success',
|
||
];
|
||
$uniStatus = $status !== '' && isset($statusMap[$status]) ? $statusMap[$status] : '';
|
||
|
||
$url = rtrim($backendUrl, '/') . '/api/v1/business/outbound-tasks?limit=' . $limit;
|
||
if ($uniStatus !== '') {
|
||
$url .= '&status=' . urlencode($uniStatus);
|
||
}
|
||
if ($phoneKeyword !== '') {
|
||
$url .= '&phone=' . urlencode($phoneKeyword);
|
||
}
|
||
|
||
$headers = [
|
||
'X-Api-Key: ' . (string)$apiKey,
|
||
'Accept: application/json',
|
||
];
|
||
|
||
$resp = $this->httpGetJson($url, $headers);
|
||
if (empty($resp['ok'])) {
|
||
// 兼容:如果 uniapp 后端还未部署新增查询接口,则 fallback 到本地队列表(只能看到“已入队”,无法看到发送成功/失败)
|
||
$fallback = [];
|
||
try {
|
||
$fWhere = [
|
||
['api_key', '=', (string)$apiKey],
|
||
['delete_time', '=', null],
|
||
];
|
||
if ($phoneKeyword !== '') {
|
||
$fWhere[] = ['phone', 'like', '%' . $phoneKeyword . '%'];
|
||
}
|
||
// 前端 status=0/1/2/3,局部队列表字段同样使用数字状态
|
||
if ($status !== '') {
|
||
$fWhere[] = ['status', '=', (int)$status];
|
||
}
|
||
|
||
$fallback = Db::name('mete_system_sms_tasks')
|
||
->where($fWhere)
|
||
->order('id', 'desc')
|
||
->limit($limit)
|
||
->select()
|
||
->toArray();
|
||
} catch (\Throwable $e) {
|
||
$fallback = [];
|
||
}
|
||
|
||
return json([
|
||
'code' => 200,
|
||
'msg' => 'uniapp 查询任务接口不可用,已使用本地队列表(仅展示入队状态)',
|
||
'detail' => [
|
||
'http_status' => $resp['status'] ?? null,
|
||
'body_preview' => isset($resp['body']) ? substr((string)$resp['body'], 0, 300) : '',
|
||
],
|
||
'list' => $fallback,
|
||
]);
|
||
}
|
||
|
||
$tasks = (array)($resp['json']['tasks'] ?? []);
|
||
|
||
$statusNumMap = [
|
||
'pending' => 0,
|
||
'sending' => 1,
|
||
'failed' => 2,
|
||
'success' => 3,
|
||
];
|
||
|
||
$list = array_map(function ($t) use ($statusNumMap) {
|
||
$content = (string)($t['content'] ?? '');
|
||
$parsedCode = '';
|
||
if (preg_match('/(\d{4,6})/', $content, $m)) {
|
||
$parsedCode = (string)$m[1];
|
||
}
|
||
|
||
$statusStr = (string)($t['status'] ?? '');
|
||
|
||
return [
|
||
'id' => $t['taskId'] ?? '',
|
||
'phone' => $t['phone'] ?? '',
|
||
'content' => $content,
|
||
'code' => $parsedCode,
|
||
'status' => $statusNumMap[$statusStr] ?? 0,
|
||
'create_time' => isset($t['createdAt']) ? date('Y-m-d H:i:s', (int)($t['createdAt'] / 1000)) : '',
|
||
'update_time' => isset($t['updatedAt']) ? date('Y-m-d H:i:s', (int)($t['updatedAt'] / 1000)) : '',
|
||
];
|
||
}, $tasks);
|
||
|
||
return json([
|
||
'code' => 200,
|
||
'msg' => 'success',
|
||
'list' => $list,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 管理员:编辑短信任务(租户隔离)
|
||
*
|
||
* body:
|
||
* - status
|
||
* - code
|
||
* - report_raw
|
||
*/
|
||
public function editSmsTask($id = null)
|
||
{
|
||
try {
|
||
$tid = $this->getTenantId();
|
||
$id = $this->request->param('id', $id);
|
||
|
||
$data = Request::only(['status', 'code', 'report_raw']);
|
||
|
||
if (empty($id)) {
|
||
return json(['code' => 400, 'msg' => '任务ID不能为空']);
|
||
}
|
||
|
||
if (!isset($data['status'])) {
|
||
return json(['code' => 400, 'msg' => '状态不能为空']);
|
||
}
|
||
|
||
$this->validate($data, [
|
||
'status|状态' => 'integer',
|
||
'code|验证码' => 'max:20',
|
||
'report_raw|上报内容' => 'max:100000',
|
||
]);
|
||
|
||
$task = Db::name('mete_system_sms_tasks')
|
||
->where('id', (int)$id)
|
||
->where('tid', (int)$tid)
|
||
->find();
|
||
|
||
if (!$task) {
|
||
return json(['code' => 404, 'msg' => '任务不存在或无权限']);
|
||
}
|
||
|
||
$update = [
|
||
'status' => (int)$data['status'],
|
||
'code' => (string)($data['code'] ?? ''),
|
||
'report_raw' => (string)($data['report_raw'] ?? ''),
|
||
'update_time' => date('Y-m-d H:i:s'),
|
||
];
|
||
|
||
Db::name('mete_system_sms_tasks')
|
||
->where('id', (int)$id)
|
||
->where('tid', (int)$tid)
|
||
->update($update);
|
||
|
||
return json(['code' => 200, 'msg' => '编辑成功']);
|
||
} catch (ValidateException $e) {
|
||
return json(['code' => 400, 'msg' => $e->getError()]);
|
||
} catch (\Throwable $e) {
|
||
return json(['code' => 500, 'msg' => '编辑失败:' . $e->getMessage()]);
|
||
}
|
||
}
|
||
|
||
private function getApiKeyFromRequest(): string
|
||
{
|
||
$req = Request::instance();
|
||
|
||
$apiKey = (string)$req->header('X-SMS-GATEWAY-API-KEY', '');
|
||
if ($apiKey !== '') {
|
||
return $apiKey;
|
||
}
|
||
|
||
$apiKey = (string)$req->header('X-API-KEY', '');
|
||
if ($apiKey !== '') {
|
||
return $apiKey;
|
||
}
|
||
|
||
$apiKey = (string)$req->header('X-SMS-API-KEY', '');
|
||
if ($apiKey !== '') {
|
||
return $apiKey;
|
||
}
|
||
|
||
$apiKey = (string)$req->header('Authorization', '');
|
||
if ($apiKey !== '' && preg_match('/Bearer\\s+(.+)/i', $apiKey, $m)) {
|
||
return (string)$m[1];
|
||
}
|
||
|
||
// body/query
|
||
$apiKey = (string)($req->post('apiKey') ?? $req->post('api_key') ?? $req->param('apiKey', '') ?? $req->param('api_key', ''));
|
||
return $apiKey;
|
||
}
|
||
|
||
private function maskKey(string $key): string
|
||
{
|
||
$key = (string)$key;
|
||
if ($key === '') return '';
|
||
if (strlen($key) <= 6) return '***';
|
||
return substr($key, 0, 3) . '***' . substr($key, -3);
|
||
}
|
||
}
|
||
|