You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

352 lines
9.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Models;
use EasyWeChat\Factory;
use EasyWeChat\Kernel\Http\StreamResponse;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Log;
class Course extends SoftDeletesModel
{
protected $appends = [
'date_status',
'publicize',
'sign_date_status',
// qrcode 不自动 append列表/关联序列化时会调微信,易导致接口 500
'teacher_detail',
'status_text',
'is_fee_text',
'is_arrange_text',
'show_txl_text',
'show_mobile_text',
'auto_schoolmate_text',
'free_enter_schoolmate_text',
'is_virtual_text'
];
protected $casts = ['publicize_ids' => 'json'];
public function getStatusTextAttribute()
{
$array = [0 => '待发布', 1 => '已发布'];
return $array[$this->attributes['status'] ?? 0] ?? '';
}
public function getIsFeeTextAttribute()
{
$array = [0 => '免费', 1 => '收费'];
return $array[$this->attributes['is_fee'] ?? 0] ?? '';
}
public function getIsArrangeTextAttribute()
{
$array = [0 => '否', 1 => '是'];
return $array[$this->attributes['is_arrange'] ?? 0] ?? '';
}
public function getShowTxlTextAttribute()
{
$array = [0 => '否', 1 => '是'];
return $array[$this->attributes['show_txl'] ?? 0] ?? '';
}
public function getShowMobileTextAttribute()
{
$array = [0 => '否', 1 => '是'];
return $array[$this->attributes['show_mobile'] ?? 0] ?? '';
}
public function getAutoSchoolmateTextAttribute()
{
$array = [0 => '否', 1 => '是'];
return $array[$this->attributes['auto_schoolmate'] ?? 0] ?? '';
}
public function getFreeEnterSchoolmateTextAttribute()
{
$array = [0 => '否', 1 => '是'];
return $array[$this->attributes['free_enter_schoolmate'] ?? 0] ?? '';
}
public function getIsVirtualTextAttribute()
{
$array = [0 => '否', 1 => '是'];
return $array[$this->attributes['is_virtual'] ?? 0] ?? '';
}
public function getQrcodeAttribute($value)
{
try {
return $this->getCourseQrcode($this->attributes['id']);
} catch (\Throwable $e) {
Log::warning('course qrcode append failed', [
'course_id' => $this->attributes['id'] ?? null,
'message' => $e->getMessage(),
]);
return '';
}
}
public function getTeacherDetailAttribute($value)
{
if (empty($this->teacher_id)) {
return collect();
}
$teacherIds = explode(',', $this->teacher_id);
return Teacher::whereIn('id', $teacherIds)->get();
}
public function getPublicizeAttribute($value)
{
if (empty($this->publicize_ids))
return [];
return Upload::whereIn('id', $this->publicize_ids)->get();
}
public function getDateStatusAttribute()
{
$text = '课程未开始';
if ($this->course_status == 10) {
$text = '课程进行中';
}
if ($this->course_status == 40) {
$text = '课程已结束';
}
return $text;
}
public function getSignDateStatusAttribute()
{
$text = '未开始';
if ($this->sign_status == 10) {
$text = '进行中';
}
if ($this->sign_status == 40) {
$text = '已结束';
}
return $text;
}
public function typeDetail()
{
return $this->hasOne(CourseType::class, 'id', 'type');
}
public function courseForms()
{
return $this->hasMany(CourseForm::class, 'course_id', 'id');
}
public function teacher()
{
return $this->hasOne(Teacher::class, 'id', 'teacher_id');
}
public function courseSettings()
{
return $this->hasMany(CourseSetting::class, 'course_id', 'id');
}
public function coursePeriods()
{
return $this->hasMany(CoursePeriod::class, 'course_id', 'id');
}
public function courseSigns()
{
return $this->hasMany(CourseSign::class, 'course_id', 'id');
}
public function image()
{
return $this->hasOne(Upload::class, 'id', 'image_id');
}
public function qunImage()
{
return $this->hasOne(Upload::class, 'id', 'qun_image_id');
}
public function courseContents()
{
return $this->hasMany(CourseContent::class, 'course_id', 'id');
}
public function courseContentEvaluation()
{
return $this->hasMany(CourseContentEvaluation::class, 'course_id', 'id');
}
/**
* 更新课程报名状态
*/
public static function updateSignStatus($courseId)
{
// 进行中-未开始-已结束
$now = date('Y-m-d');
$course = Course::find($courseId);
// 默认未开始
$sign_status = 30;
if ($course->sign_start_date && $now < $course->sign_start_date) {
// 未开始
$sign_status = 20;
}
if ($course->sign_start_date && $now >= $course->sign_start_date) {
// 进行中
$sign_status = 10;
}
if ($course->sign_end_date && $now > $course->sign_end_date) {
// 已结束
$sign_status = 40;
}
$course->sign_status = $sign_status;
$course->save();
return $course;
}
/**
* 更新课程状态
*/
public static function updateStatus($courseId)
{
// 进行中-未开始-已结束
$now = date('Y-m-d');
$course = Course::find($courseId);
// 默认待定
$course_status = 30;
if ($course->start_date && $now < $course->start_date) {
// 未开始
$course_status = 20;
}
if ($course->start_date && $now >= $course->start_date) {
// 进行中
$course_status = 10;
}
if ($course->end_date && $now > $course->end_date) {
// 已结束
$course_status = 40;
}
$course->course_status = $course_status;
$course->save();
return $course;
}
/**
* 获取课程详情小程序码
* @param mixed $courseId
* @param bool $generate 是否在文件不存在时尝试生成
*/
public function getCourseQrcode($courseId, $generate = true)
{
$course = Course::find($courseId);
if (empty($course)) {
return '';
}
return $this->resolveMiniProgramQrcode(
'course_qrcode/' . $course->id . '.png',
'packages/course/detail?id=' . $courseId,
['course_id' => $courseId],
$generate
);
}
/**
* 获取课程报名小程序码
*/
public function getCourseCheckQrcode($courseId, $generate = true)
{
$course = Course::find($courseId);
if (empty($course)) {
return '';
}
return $this->resolveMiniProgramQrcode(
'course_check_qrcode/' . $course->id . '.png',
'packages/sign/course?course_id=' . $courseId,
['course_id' => $courseId],
$generate
);
}
/**
* 获取问卷小程序码
*/
public function getEvaluationQrcode($id, $generate = true)
{
$courseContentEvaluation = CourseContentEvaluation::find($id);
if (empty($courseContentEvaluation)) {
return '';
}
return $this->resolveMiniProgramQrcode(
'course_evaluation_qrcode/' . $courseContentEvaluation->id . '.png',
'packages/surveyFill/index?id=' . $id,
['evaluation_id' => $id],
$generate
);
}
/**
* 读取或生成小程序码;微信/缓存失败时返回空字符串,避免列表接口 500
*/
protected function resolveMiniProgramQrcode(string $relativePath, string $page, array $context = [], bool $generate = true): string
{
$path = config('filesystems.disks.public.root') . '/' . $relativePath;
$url = config('filesystems.disks.public.url') . '/' . $relativePath;
$fileSys = new Filesystem();
if ($fileSys->exists($path)) {
return $url;
}
if (!$generate) {
return '';
}
$appId = (string) config('app.applet_appid');
$secret = (string) config('app.applet_secret');
if ($appId === '' || $secret === '') {
Log::warning('mini program qrcode skipped: missing appid/secret', $context);
return '';
}
try {
$app = Factory::miniProgram(miniProgramConfig([
'app_id' => $appId,
'secret' => $secret,
]));
$tmp = $app->app_code->get($page, [
'env_version' => 'release',
]);
$content = $tmp instanceof StreamResponse ? $tmp->getBodyContents() : (string) $tmp;
if ($content === '' || str_starts_with(ltrim($content), '{')) {
Log::warning('mini program qrcode invalid response', $context + [
'body' => substr($content, 0, 300),
]);
return '';
}
$fileSys->ensureDirectoryExists(dirname($path), 0755, true);
$fileSys->put($path, $content);
return $url;
} catch (\Throwable $e) {
Log::warning('mini program qrcode failed', $context + [
'message' => $e->getMessage(),
]);
return '';
}
}
/**
* 获取课程统计项元数据
* 返回每个统计项的计算逻辑和验证方法说明
* 从 statistics_metadata 表中读取
* @return array
*/
public static function getStatisticsMetadata()
{
return StatisticsMetadata::getAllAsArray();
}
}