Merge branch 'master' of ssh://47.101.48.251:/data/git/wx.sstbc.com

master
lion 3 months ago
commit 8537942692

@ -4,6 +4,7 @@ namespace App\Console\Commands;
use App\Models\BirthdayMessage;
use App\Models\Config;
use App\Models\EmailRecordUser;
use App\Models\User;
use App\Repositories\MeetRepository;
use Illuminate\Console\Command;
@ -56,42 +57,82 @@ class CheckBirthday extends Command
// 发送通知给用户
$smsSign = Config::getValueByKey('sms_sign') ?: '';
$userSuccessCount = 0;
$userFailCount = 0;
$userSmsSuccessCount = 0;
$userSmsFailCount = 0;
$userEmailSuccessCount = 0;
$userEmailFailCount = 0;
foreach ($users as $user) {
// 检查用户是否有手机号
if (empty($user->mobile)) {
continue;
$username = $user->username ?: '校友';
$hasMobile = !empty($user->mobile);
$hasEmail = !empty($user->email);
// 发送短信
if ($hasMobile) {
// 获取随机短信模板
$smsMessage = BirthdayMessage::getRandomSmsMessage();
if ($smsMessage) {
// 替换用户名占位符
$smsContent = str_replace('{username}', $username, $smsMessage);
// 添加短信签名
$smsContent = $smsSign . $smsContent;
// 直接发送短信
$result = ymSms($user->mobile, $smsContent);
if ($result) {
$userSmsSuccessCount++;
$this->info("已向用户 {$username}({$user->mobile}) 发送生日祝福短信");
} else {
$userSmsFailCount++;
$this->error("向用户 {$username}({$user->mobile}) 发送短信失败");
}
}
}
// 获取随机文案
$message = BirthdayMessage::getRandomMessage();
if ($message) {
// 替换用户名占位符
$username = $user->username ?: '校友';
$content = str_replace('{username}', $username, $message);
// 添加短信签名
$content = $smsSign . $content;
// 直接发送短信
$result = ymSms($user->mobile, $content);
if ($result) {
$userSuccessCount++;
$this->info("已向用户 {$user->username}({$user->mobile}) 发送生日祝福短信");
// 发送邮件
if ($hasEmail) {
// 获取随机邮件模板
$emailTemplate = BirthdayMessage::getRandomEmailMessage();
if ($emailTemplate) {
try {
// 准备变量数据
$varData = [
'username' => $username,
];
// 使用模板方法替换邮件标题和内容中的变量
$emailSubject = EmailRecordUser::template($emailTemplate['subject'], $varData);
$emailContent = EmailRecordUser::template($emailTemplate['content'], $varData);
// 发送邮件
EmailRecordUser::email($emailSubject, $emailContent, $user->email);
$userEmailSuccessCount++;
$this->info("已向用户 {$username}({$user->email}) 发送生日祝福邮件");
} catch (\Exception $e) {
$userEmailFailCount++;
$this->error("向用户 {$username}({$user->email}) 发送邮件失败: " . $e->getMessage());
}
} else {
$userFailCount++;
$this->error("向用户 {$user->username}({$user->mobile}) 发送短信失败");
$this->warn("未找到可用的邮件模板,跳过向用户 {$username}({$user->email}) 发送邮件");
}
}
}
if ($userSuccessCount > 0) {
$this->info("共向 {$userSuccessCount} 位用户发送生日祝福短信成功");
// 输出短信发送统计
if ($userSmsSuccessCount > 0) {
$this->info("共向 {$userSmsSuccessCount} 位用户发送生日祝福短信成功");
}
if ($userSmsFailCount > 0) {
$this->error("共 {$userSmsFailCount} 位用户短信发送失败");
}
// 输出邮件发送统计
if ($userEmailSuccessCount > 0) {
$this->info("共向 {$userEmailSuccessCount} 位用户发送生日祝福邮件成功");
}
if ($userFailCount > 0) {
$this->error("共 {$userFailCount} 位用户短信发送失败");
if ($userEmailFailCount > 0) {
$this->error("共 {$userEmailFailCount} 位用户邮件发送失败");
}
// 如果有生日用户,给管理员发送短信

@ -42,6 +42,16 @@ class UpdateLetter extends Command
*/
public function handle()
{
// 检测所有用户,如果 name 和 username 不一样,就把 username 赋值给 name 保持一致
$usersToUpdate = User::whereNotNull('username')
->where(function ($query) {
$query->whereNull('name')->orWhereColumn('name', '!=', 'username');
})->limit(100)->get();
foreach ($usersToUpdate as $user) {
$user->name = $user->username;
$user->save();
}
// 更新用户首字母
$users = User::whereNull('letter')
->where(function ($query) {

@ -9,6 +9,7 @@ use App\Helpers\ResponseCode;
use App\Models\AccompanyOrder;
use App\Models\AppointmentTotalLog;
use App\Models\Calendar;
use App\Models\Company;
use App\Models\Config;
use App\Models\Course;
use App\Models\CourseAppointmentTotal;
@ -124,10 +125,10 @@ class CourseController extends CommonController
]);
}
])->withCount([
'courseSigns as my_user' => function ($query) {
$query->where('user_id', $this->getUserId());
}
])->find($all['course_id']);
'courseSigns as my_user' => function ($query) {
$query->where('user_id', $this->getUserId());
}
])->find($all['course_id']);
return $this->success($detail);
}
@ -266,6 +267,17 @@ class CourseController extends CommonController
return $this->fail([ResponseCode::ERROR_PARAMETER, '以下字段为必填项:' . implode('、', $missingFields)]);
}
}
// 检测 company_name 字段是否包含特殊符号
if (isset($all['data']) && is_array($all['data'])) {
foreach ($all['data'] as $item) {
if (isset($item['field']) && $item['field'] === 'company_name' && !empty($item['value'])) {
$validation = Company::validateCompanyName($item['value']);
if (!$validation['valid']) {
return $this->fail([ResponseCode::ERROR_BUSINESS, $validation['message']]);
}
}
}
}
$result = CourseSign::create([
'is_change' => $all['is_change'] ?? 0,
'course_id' => $all['course_id'],
@ -803,54 +815,54 @@ class CourseController extends CommonController
$query->where('status', 1);
}
})->with([
'courseSigns' => function ($query) use ($all) {
$query->where('status', 1)->whereHas('course', function ($q) {
$q->where('is_fee', 1);
})->with('course.teacher', 'course.typeDetail')
->orderByRaw("FIELD(fee_status, 1, 0, 2,3)");
if (isset($all['course_id'])) {
$query->where('course_id', $all['course_id']);
}
}
])->where(function ($query) use ($all) {
if ($all['type'] == 1) {
$query->where('is_schoolmate', 1);
}
if (isset($all['name'])) {
$query->where('name', 'like', '%' . $all['name'] . '%');
}
if (isset($all['company_business'])) {
$query->where('company_business', 'like', '%' . $all['company_business'] . '%');
}
if (isset($all['company_name'])) {
$query->where('company_name', 'like', '%' . $all['company_name'] . '%');
}
if (isset($all['company_position'])) {
$query->where('company_position', $all['company_position']);
}
if (isset($all['company_area'])) {
$query->where('company_area', 'like', '%' . $all['company_area'] . '%');
}
if (isset($all['company_type'])) {
$company_type = explode(',', $all['company_type']);
$query->where(function ($q) use ($company_type) {
foreach ($company_type as $v) {
$q->orWhereRaw('FIND_IN_SET(?, company_type)', [$v]);
'courseSigns' => function ($query) use ($all) {
$query->where('status', 1)->whereHas('course', function ($q) {
$q->where('is_fee', 1);
})->with('course.teacher', 'course.typeDetail')
->orderByRaw("FIELD(fee_status, 1, 0, 2,3)");
if (isset($all['course_id'])) {
$query->where('course_id', $all['course_id']);
}
}
});
}
if (isset($all['company_industry'])) {
$company_industry = explode(',', $all['company_industry']);
$query->where(function ($q) use ($company_industry) {
foreach ($company_industry as $v) {
$q->orWhereRaw('FIND_IN_SET(?, company_industry)', [$v]);
])->where(function ($query) use ($all) {
if ($all['type'] == 1) {
$query->where('is_schoolmate', 1);
}
if (isset($all['name'])) {
$query->where('name', 'like', '%' . $all['name'] . '%');
}
if (isset($all['company_business'])) {
$query->where('company_business', 'like', '%' . $all['company_business'] . '%');
}
if (isset($all['company_name'])) {
$query->where('company_name', 'like', '%' . $all['company_name'] . '%');
}
if (isset($all['company_position'])) {
$query->where('company_position', $all['company_position']);
}
if (isset($all['company_area'])) {
$query->where('company_area', 'like', '%' . $all['company_area'] . '%');
}
if (isset($all['company_type'])) {
$company_type = explode(',', $all['company_type']);
$query->where(function ($q) use ($company_type) {
foreach ($company_type as $v) {
$q->orWhereRaw('FIND_IN_SET(?, company_type)', [$v]);
}
});
}
if (isset($all['company_industry'])) {
$company_industry = explode(',', $all['company_industry']);
$query->where(function ($q) use ($company_industry) {
foreach ($company_industry as $v) {
$q->orWhereRaw('FIND_IN_SET(?, company_industry)', [$v]);
}
});
}
if (isset($all['letter'])) {
$query->where('letter', $all['letter']);
}
});
}
if (isset($all['letter'])) {
$query->where('letter', $all['letter']);
}
});
if (isset($all['type']) && $all['type'] == 2) {
$list = $list->orderBy('letter')->paginate(10);
} else {

@ -10,6 +10,7 @@ use App\Helpers\StarterResponseCode;
use App\Jobs\SendAppointCar;
use App\Jobs\SendCourseCar;
use App\Models\Appointment;
use App\Models\Company;
use App\Models\Config;
use App\Models\CourseContentCheck;
use App\Models\CourseSign;
@ -181,6 +182,13 @@ class UserController extends CommonController
if (isset($all['name']) && !empty($all['name'])) {
$all['letter'] = strtoupper(Pinyin::abbr(mb_substr($all['name'], 0, 1))[0]);
}
// 如果上传了company_name检测是否包含特殊符号
if (isset($all['company_name']) && !empty($all['company_name'])) {
$validation = Company::validateCompanyName($all['company_name']);
if (!$validation['valid']) {
return $this->fail([ResponseCode::ERROR_BUSINESS, $validation['message']]);
}
}
$model->fill($all);
$model->save();
// 如果有公司信息,就更新一下公司
@ -232,10 +240,10 @@ class UserController extends CommonController
{
$user = User::with('appointments')
->withCount([
'appointments as pass_appointments' => function ($query) {
$query->whereIn('status', [0, 1]);
}
])->with([
'appointments as pass_appointments' => function ($query) {
$query->whereIn('status', [0, 1]);
}
])->with([
'courseSigns' => function ($query) {
$query->whereHas('course')->with('course.typeDetail')->where('status', 1)->where('fee_status', 1);
}

@ -6,7 +6,7 @@ class BirthdayMessage extends SoftDeletesModel
{
/**
* 随机获取一条启用的生日祝福文案
* 随机获取一条启用的生日祝福文案(短信模板)
*
* @return string|null
*/
@ -18,5 +18,42 @@ class BirthdayMessage extends SoftDeletesModel
return $message ? $message->content : null;
}
/**
* 随机获取一条启用的短信模板
*
* @return string|null
*/
public static function getRandomSmsMessage()
{
$message = self::where('status', 1)
->where('type', 1)
->inRandomOrder()
->first();
return $message ? $message->content : null;
}
/**
* 随机获取一条启用的邮件模板
*
* @return array|null 返回包含 subject 和 content 的数组,如果没有找到则返回 null
*/
public static function getRandomEmailMessage()
{
$message = self::where('status', 1)
->where('type', 2)
->inRandomOrder()
->first();
if (!$message) {
return null;
}
return [
'subject' => $message->email_subject ?: '生日快乐',
'content' => $message->content,
];
}
}

@ -13,9 +13,9 @@ class Company extends SoftDeletesModel
public function getIsYhInvestedTextAttribute()
{
if (empty($this->is_yh_invested)) {
return '';
return '';
}
return $this->is_yh_invested == 1 ? '是' : '否';
return $this->is_yh_invested == 1 ? '被投企业' : '';
}
public function users()
@ -331,4 +331,92 @@ class Company extends SoftDeletesModel
return false;
}
/**
* 验证公司名称是否包含特殊符号
* @param string $companyName 公司名称
* @return array 返回结果 ['valid' => bool, 'message' => string]
*/
public static function validateCompanyName($companyName)
{
if (empty($companyName)) {
return ['valid' => true, 'message' => ''];
}
// 定义不允许的特殊符号(包含中英文标点符号,键盘上能打出来的所有标点符号)
$forbiddenChars = [
// 英文标点符号
'/',
'\\',
'.',
',',
';',
':',
"'",
'"',
'?',
'!',
'@',
'#',
'$',
'%',
'^',
'&',
'*',
'(',
')',
'[',
']',
'{',
'}',
'|',
'`',
'~',
'-',
'_',
'+',
'=',
'<',
'>',
// 中文标点符号
'。',
'',
'、',
'',
'',
'',
'',
'…',
'—',
'·',
'',
'¥',
'',
'',
'【',
'】',
'《',
'》',
'〈',
'〉',
'「',
'」',
'『',
'』',
'',
'',
];
// 添加中文引号字符(使用十六进制编码避免语法错误)
$chineseQuotes = ["\xE2\x80\x9C", "\xE2\x80\x9D", "\xE2\x80\x98", "\xE2\x80\x99"]; // " " ' '
$forbiddenChars = array_merge($forbiddenChars, $chineseQuotes);
foreach ($forbiddenChars as $char) {
if (strpos($companyName, $char) !== false) {
return ['valid' => false, 'message' => '公司名称不能包含特殊符号'];
}
}
return ['valid' => true, 'message' => ''];
}
}

@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
return new class extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('birthday_messages', function (Blueprint $table) {
// 添加模板类型字段1=短信模板2=邮件模板
$table->tinyInteger('type')->default(1)->after('id')->comment('模板类型1=短信模板2=邮件模板');
// 添加邮件标题字段(仅邮件模板使用)
$table->string('email_subject')->nullable()->after('content')->comment('邮件标题,支持{username}占位符');
});
// 将现有所有记录标记为短信模板type=1
DB::table('birthday_messages')->whereNull('type')->update(['type' => 1]);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('birthday_messages', function (Blueprint $table) {
$table->dropColumn(['type', 'email_subject']);
});
}
};
Loading…
Cancel
Save