chuan 2 months ago
parent
commit
48bd60dc59
  1. 16
      app/admin/controller/Admin.php
  2. 51
      app/admin/controller/Upload.php
  3. 43
      app/admin/job/SendEmailFromBackend.php
  4. 51
      app/admin/job/SendSmsFromBackend.php
  5. 17
      app/admin/route/app.php
  6. 122
      app/admin/service/AdminService.php
  7. 4
      app/home/service/LoginService.php

16
app/admin/controller/Admin.php

@ -148,7 +148,7 @@ class Admin extends AdminBaseController
return json($returnData);
}
// 一键推送消息,邮件或短信
// 单次发送邮件或短信消息
public function sendEmailOrSms()
{
$returnData = (new AdminService())->sendEmailOrSms($this->request->param());
@ -161,18 +161,4 @@ class Admin extends AdminBaseController
$returnData = (new AdminService())->batchSendEmailOrSms($this->request->param());
return json($returnData);
}
// 定时任务执行 批量发送短信
public function execBatchSendSms()
{
$returnData = (new AdminService())->execBatchSendSms();
return json($returnData);
}
// 定时任务窒执行 批量发送邮件
public function execBatchSendEmail()
{
$returnData = (new AdminService())->execBatchSendEmail();
return json($returnData);
}
}

51
app/admin/controller/Upload.php

@ -239,19 +239,6 @@ class Upload extends AdminBaseController
return json(['code' => 400, 'message' => '上传分片失败']);
}
// 更新上传分片记录
if (empty($awsS3->parts)) {
$buildParts = json_encode([
['PartNumber'=>trim($param['partNumber']), 'ETag'=>$result['ETag']]
]);
$awsS3->parts = $buildParts;
} else {
$parsePart = json_decode($awsS3->parts, true);
$parsePart[] = ['PartNumber'=>trim($param['partNumber']), 'ETag'=>$result['ETag']];
$awsS3->parts = json_encode($parsePart);
}
$awsS3->save();
return json([
'code' => 200,
'message' => 'success',
@ -276,20 +263,11 @@ class Upload extends AdminBaseController
if (empty($param['uploadId'])) {
return json(['code' => 400, 'message' => '缺少参数']);
}
// 注意:$parts 是一个数组,里面需要有每个分段编号,以及每个分段对应的ETag
// $parts = [
// ['PartNumber' => 1, 'ETag' => 'etag-for-part-1'],
// ['PartNumber' => 2, 'ETag' => 'etag-for-part-2'],
// ];
// 获取分段上传记录信息
$awsS3 = AwsS3Model::where(['upload_id'=>trim($param['uploadId'])])->find();
if (empty($awsS3) || empty($awsS3['parts']) || empty($awsS3['key'])) {
if (empty($awsS3) || empty($awsS3['key'])) {
return json(['code' => 400, 'message' => 'uploadId对应的数据错误']);
}
// 取出上传的分片记录,并排序
$parts = json_decode($awsS3['parts'],true);
$partNumbers = array_column($parts, 'PartNumber');
array_multisort($partNumbers, SORT_ASC, $parts);
// 初始化s3客户端
$s3Config = Config::get('common.aws_s3');
$s3Client = new S3Client([
@ -300,16 +278,37 @@ class Upload extends AdminBaseController
'secret' => $s3Config['aws_secret'],
],
]);
// 获取已经上传的分片
$getParts = $s3Client->listParts([
'Bucket' => $s3Config['aws_bucket'],
'Key' => $awsS3['key'],
'UploadId' => trim($param['uploadId']),
]);
if (empty($getParts) || !isset($getParts['Parts'])) {
return json(['code' => 400, 'message' => '获取已上传的分片数据失败']);
}
// 按顺序组装分片数据
$buildParts = [];
foreach ($getParts['Parts'] as $p) {
$buildParts[] = [
'PartNumber' => trim($p['PartNumber'], '"'),
'ETag' => $p['ETag']
];
}
$sortIndex = array_column($buildParts, 'PartNumber');
array_multisort($sortIndex, SORT_ASC, $buildParts);
Log::info("升序排列后的分片数据:".json_encode($buildParts));
// 请求s3完成分段上传任务
$result = $s3Client->completeMultipartUpload([
'Bucket' => $s3Config['aws_bucket'],
'Key' => $awsS3['key'],
'UploadId' => $param['uploadId'],
'MultipartUpload' => [
'Parts' => $parts
'Parts' => $buildParts
],
])->toArray();
Log::info("完成分段上传结果:".json_encode($result));
// 更新分片上传记录
$awsS3->is_complete = 1;
$awsS3->s3_url = $result['Location'];

43
app/admin/job/SendEmailFromBackend.php

@ -0,0 +1,43 @@
<?php
namespace app\admin\job;
use think\queue\Job;
class SendEmailFromBackend
{
/**
* @desc 发送邮件
* @param Job $job
* @param $data
* @return void
*/
public function fire(Job $job, $data)
{
trace('管理端发送邮件任务开始', 'info');
$phpEmail = new \app\utility\SendEmail();
$success = false;
for($times = 1; $times <= 3; $times++) {
$bool = $phpEmail->sendEmail($data['email'], $data['title'], $data['content']);
if ($bool) {
$success = true;
break;
}
}
// 任务失败
if (!$success) {
trace($job->getJobId().'---管理端发送邮件任务失败-------'.json_encode($data), 'info');
}
// 删除任务
$job->delete();
}
public function failed($data)
{
// 失败任务
$dataStr = json_encode($data);
trace('queue job 任务失败---'.$dataStr, 'error');
}
}

51
app/admin/job/SendSmsFromBackend.php

@ -0,0 +1,51 @@
<?php
namespace app\admin\job;
class SendSmsFromBackend
{
/**
* @desc 获取配置文件中的短信秘钥对
* @return array
*/
public function getConfig()
{
// 配置文件获取
$configStr = env('SMS.CONFIG');
$arr = explode(',', $configStr);
$config = [];
foreach ($arr as $value){
$conArr = explode('@', $value);
$config[] = [
'access_key' => $conArr[0],
'secret' => $conArr[1]
];
}
return $config;
}
/**
* @desc 发送短信验证码
* @param Job $job
* @param $data
* @return void
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function fire(Job $job, $data)
{
trace($job->getJobId().'---管理端发送短信任务开始--------'.json_encode($data), 'info');
$to = $data['mobile'];
$message = $data['message'];
$from = 'Bourse';
$accessKey = env('SMS.ACCESS_KEY_ID');
$secret = env('SMS.ACCESS_KEY_SECRET');
if (empty($accessKey) || empty($secret)) {
trace('------- 短信账号配置错误 -------', 'error');
return;
}
(new \app\utility\SendSms())->sendMessageToGlobe($to, $message, $from, $accessKey, $secret);
// 删除任务
$job->delete();
}
}

17
app/admin/route/app.php

@ -28,8 +28,11 @@ Route::group('/', function () {
Route::post('config/sms_template_list', 'Config/smsTemplateList'); //获取短信模板列表
Route::post('config/edit_sms_template', 'Config/editSmsTemplate'); //编辑短信模板
// 消息推送
Route::post('notice/popup', 'Notice/popUp'); // 弹窗推送消息
Route::post('notice/push_message', 'Notice/pushMessage'); // 使用Pusher推送系统级通知
Route::post('notice/push_message', 'Notice/pushMessage'); // 使用Pusher推送系统级消息
Route::post('admin/send_email_or_sms', 'Admin/sendEmailOrSms'); // 给用户发邮件或者短信
Route::post('admin/batch_send_email_or_sms', 'Admin/batchSendEmailOrSms'); // 批量给用户发邮件或者短信
// 渠道列表
Route::post('/channel/list', 'Channel/list'); //渠道列表
@ -52,6 +55,7 @@ Route::group('/', function () {
Route::post('add_video', 'video/addVideoOnDemand')->middleware('admin_log'); //添加点播视频
Route::post('edit_video', 'video/editVideoOnDemand')->middleware('admin_log'); //编辑点播视频
// 直播间管理
Route::post('video/blocked_word_list', 'Video/blockedWordList'); //屏蔽词列表
Route::post('video/blocked_word_add', 'Video/blockedWordAdd')->middleware('admin_log'); //添加屏蔽词
Route::post('video/blocked_word_edit', 'Video/blockedWordEdit')->middleware('admin_log'); //编辑屏蔽词
@ -219,10 +223,6 @@ Route::group('/', function () {
// 送会员
Route::post('admin/give_vip', 'Admin/giveVip')->middleware('admin_log'); //赠送用户vip
Route::post('admin/send_email_or_sms', 'Admin/sendEmailOrSms'); // 给用户发邮件或者短信
Route::post('admin/batch_send_email_or_sms', 'Admin/batchSendEmailOrSms'); // 批量给用户发邮件或者短信
Route::post('admin/exec_batch_send_sms', 'Admin/execBatchSendSms'); // 定时任务执行 - 批量发送短信
Route::post('admin/exec_batch_send_email', 'Admin/execBatchSendEmail'); // 定时任务执行 - 批量发送邮件
// 配置管理
// 外汇插针行情
@ -521,12 +521,10 @@ Route::group('/', function () {
Route::post('/setting/block_stock_add', 'setting.BlockStock/add')->middleware('admin_log');
Route::post('/setting/block_stock_edit', 'setting.BlockStock/edit')->middleware('admin_log');
// 股票指数
Route::post('/setting/stock_index_list', 'setting.StockIndex/index');
Route::post('/setting/stock_index_update', 'setting.StockIndex/update');
// 资金管理
Route::post('/flow/digital', 'Flow/digital');
Route::post('/flow/stock', 'Flow/stock');
@ -553,14 +551,9 @@ Route::group('/', function () {
Route::post('/flow/block_stock', 'Flow/blockStock'); //股票大宗流水
Route::post('flow/inr_stock_index', 'Flow/inrStockIndex'); //印度股指资产流水
Route::post('/flow/fund_stock', 'Flow/fundStock');
Route::post('/flow/in_option_stock', 'Flow/inOptionStock');
// 充值订单
Route::post('/recharge/index', 'Recharge/index'); // 充值订单
Route::post('/recharge/info', 'Recharge/info');

122
app/admin/service/AdminService.php

@ -22,17 +22,13 @@ use think\exception\ValidateException;
use app\utility\UnqId;
use think\facade\Db;
use think\facade\Log;
use think\facade\Queue;
use function Sodium\compare;
use think\facade\Request;
class AdminService extends AdminBaseService
{
const BATCH_SMS_LIST_KEY = "BATCH_SMS_LIST_KEY"; // 批量发送短信redis Key
const BATCH_SMS_CONTENT_KEY = "BATCH_SMS_CONTENT_KEY"; // 批量发送短信内容Key
const BATCH_EMAIL_LIST_KEY = "BATCH_EMAIL_LIST_KEY"; // 批量发送邮件redis Key
const BATCH_EMAIL_CONTENT_KEY = "BATCH_EMAIL_CONTENT_KEY"; // 邮件内容redis key
// 根据角色获取账号列表
public function getUsersByRole($param)
{
@ -879,6 +875,7 @@ class AdminService extends AdminBaseService
}
}
// 单次发送邮件或短信
public function sendEmailOrSms($param)
{
try {
@ -956,37 +953,41 @@ class AdminService extends AdminBaseService
switch ($param['type']) {
case 1: // 发送短信
$userList = UserModel::where('user_id', 'in', $param['user_ids'])->column('phone_number', 'user_id');
$userList = UserModel::where('user_id', 'in', $param['user_ids'])->column('phone_number,country_code', 'user_id');
if (empty($userList)) {
return $this->toData('500', '用户列表为空');
}
// 将短信内容存储在redis中
Cache::store('redis')->set(self::BATCH_SMS_CONTENT_KEY, $param['content']);
// 将用户手机号存储到redis list中
foreach ($userList as $phone) {
if (empty($phone)) {
// 循环将每个用户发送短信任务加入到队列
foreach ($userList as $v) {
if (empty($v)) {
continue;
}
Cache::store('redis')->lpush(self::BATCH_SMS_LIST_KEY, $phone);
$jobName = 'app\admin\job\SendSmsFromBackend';
Queue::push($jobName, [
'mobile' => $v['country_code'].$v['phone_number'],
'message' => $param['content'],
], 'sendSmsFromBackend');
}
break;
case 2: // 发送邮件
if (empty($param['title'])) {
return $this->toData('400', '邮件标题不能为空');
}
$userList = UserModel::where('user_id', 'in', $param['user_ids'])->column('email', 'user_id');
$userList = UserModel::where('user_id', 'in', $param['user_ids'])->column('email');
if (empty($userList)) {
return $this->toData('500', '用户列表为空');
}
// 将邮件内容存储在redis中
$buildContent = $param['title']."@".$param['content'];
Cache::store('redis')->set(self::BATCH_EMAIL_CONTENT_KEY, $buildContent);
// 将用邮箱存储到redis list中
// 循环将每个用户发送邮件任务加入到队列
foreach ($userList as $email) {
if (empty($email)) {
continue;
}
Cache::store('redis')->lpush(self::BATCH_EMAIL_LIST_KEY, $email);
$jobName = 'app\admin\job\SendEmailFromBackend';
Queue::push($jobName, [
'email' => $email,
'title' => $param['title'],
'content' => $param['content'],
], 'sendEmailFromBackend');
}
break;
default:
@ -999,89 +1000,4 @@ class AdminService extends AdminBaseService
}
}
// 定时任务处理匹批量发送短信
public function execBatchSendSms()
{
try {
// 每次处理2000个用户
$maxNum = 2000;
$smsLen = Cache::store('redis')->llen(self::BATCH_SMS_LIST_KEY);
if ($smsLen <= 0) {
return;
}
if ($smsLen < $maxNum) {
$maxNum = $smsLen;
}
$accessKey = env('SMS.ACCESS_KEY_ID');
$secret = env('SMS.ACCESS_KEY_SECRET');
if (empty($accessKey) || empty($secret)) {
Log::info("批量短信发送 - 短信配置数据未找到");
return;
}
$content = Cache::store('redis')->get(self::BATCH_SMS_CONTENT_KEY);
if (empty($content)) {
Log::info("批量短信发送 - 短信内容不能为空");
return;
}
$smsObj = new \app\utility\SendSms();
for ($i=1; $i<=$maxNum; $i++) {
$phone = Cache::store('redis')->rpop(self::BATCH_SMS_LIST_KEY);
if (empty($phone)) {
continue;
}
$from = 'Auto';
$bool = $smsObj->sendMessageToGlobe($phone, $content, $from, $accessKey, $secret);
if (!$bool) {
Log::info("批量短信发送 - 当前用户发送失败, phone=".$phone);
}
}
return $this->toData('0', 'ok');
} catch (\Exception $e) {
Log::info("批量短信发送异常:err==". $e->getMessage()." trace==".$e->getTraceAsString());
return $this->toData('500', 'The system is busy', [$e->getMessage(), $e->getTrace()]);
}
}
// 定时任务执行批量发送邮件任务
public function execBatchSendEmail(){
try {
// 每次处理2000个用户
$maxNum = 2000;
$emailLen = Cache::store('redis')->llen(self::BATCH_EMAIL_LIST_KEY);
if ($emailLen <= 0) {
return;
}
if ($emailLen < $maxNum) {
$maxNum = $emailLen;
}
$content = Cache::store('redis')->get(self::BATCH_EMAIL_CONTENT_KEY);
if (empty($content)) {
Log::info("批量邮件发送 - 邮件内容不能为空");
return;
}
// 邮件内容需要以@符号分割,然后获取到title 和 content
$splStr = explode('@', $content);
$title = $splStr[0];
$msg = $splStr[1];
$phpEmail = new \app\utility\SendEmail();
for ($i=1; $i<=$maxNum; $i++) {
$email = Cache::store('redis')->rpop(self::BATCH_EMAIL_LIST_KEY);
if (empty($email)) {
continue;
}
$emailTemplate['email'] = $email;
$title = trim($title);
$content = trim($msg);
$bool = $phpEmail->sendEmail($emailTemplate['email'], $title, $content);
if (!$bool) {
Log::info("批量发送邮件 - 当前用户发送失败, email=".$email);
}
}
return $this->toData('0', 'ok');
} catch (\Exception $e) {
Log::info("批量短信发送异常:err==". $e->getMessage()." trace==".$e->getTraceAsString());
return $this->toData('500', 'The system is busy', [$e->getMessage(), $e->getTrace()]);
}
}
}

4
app/home/service/LoginService.php

@ -457,9 +457,9 @@ class LoginService extends BaseHomeService
$this->checkRegisterLimit($ipCanRegisterNumPerIpPerDay);
// 校验验证码
if ($param['sms_code'] != 8888) { // 方便测试,8888为万能验证码
$mobile = $param['nation'] . $param['phone'];
$smsKey = 'DB:USER:UNLOGIN:SMS_CODE:' . $mobile;
if ($param['sms_code'] != 8888) { // 方便测试,8888为万能验证码
if (!$this->checkCode($smsKey, $param['sms_code'])) {
return $this->toData('100300', 'The verification code is incorrect.', []);
//注册验证码
@ -658,9 +658,9 @@ class LoginService extends BaseHomeService
$param['phone'] = trim($param['phone']);
// 验证短信验证码
if ($param['sms_code'] != 8888) {
$mobile = $param['nation'] . $param['phone'];
$smsKey = 'DB:USER:UNLOGIN:SMS_CODE:' . $mobile;
if ($param['sms_code'] != 8888) {
if (!$this->checkCode($smsKey, $param['sms_code'])) {
return $this->toData('100300', 'The verification code is incorrect.', []);
};

Loading…
Cancel
Save