计划课程

master
lion 3 months ago
parent 81afdcd68f
commit c9ea33d4e2

@ -0,0 +1,150 @@
<?php
namespace App\Console\Commands;
use App\Models\ScheduleOverviewLocation;
use App\Models\ScheduleOverviewOwner;
use App\Models\ScheduleOverviewSchedule;
use App\Models\ScheduleOverviewScheduleGroup;
use App\Models\ScheduleOverviewScheduleModule;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class MigrateScheduleOverviewLegacySchedules extends Command
{
protected $signature = 'schedule-overview:migrate-legacy-schedules {--year=} {--dry-run}';
protected $description = '将旧的 schedule_overview_schedules 数据迁移到 schedule_groups + modules不删除旧数据';
public function handle(): int
{
$year = $this->option('year');
$dryRun = (bool) $this->option('dry-run');
$query = ScheduleOverviewSchedule::query()->orderBy('id');
if (!empty($year)) {
$query->where('year', (string) $year);
}
$legacyList = $query->get();
if ($legacyList->isEmpty()) {
$this->info('未找到需要迁移的数据。');
return self::SUCCESS;
}
$this->info('待迁移条数:' . $legacyList->count() . ($dryRun ? 'dry-run' : ''));
$createdGroups = 0;
$createdModules = 0;
$skippedModules = 0;
DB::beginTransaction();
try {
foreach ($legacyList as $legacy) {
$group = ScheduleOverviewScheduleGroup::query()
->where('year', $legacy->year)
->where('system_id', $legacy->system_id)
->where('course_id', $legacy->course_id)
->where('title', $legacy->title)
->first();
if (empty($group)) {
$group = new ScheduleOverviewScheduleGroup();
$group->year = (string) $legacy->year;
$group->system_id = (int) $legacy->system_id;
$group->course_id = (int) $legacy->course_id;
$group->title = (string) $legacy->title;
$group->admin_id = $legacy->admin_id;
$group->department_id = $legacy->department_id;
if (!$dryRun) {
$group->save();
}
$createdGroups++;
}
$locationName = trim((string) ($legacy->location ?? ''));
$ownerName = trim((string) ($legacy->owner ?? ''));
if ($locationName === '' || $ownerName === '') {
// 旧数据不完整时跳过,避免写入无法编辑的 module
$this->warn("跳过 legacy#{$legacy->id}:地点或负责人为空");
continue;
}
$location = ScheduleOverviewLocation::query()->where('name', $locationName)->first();
if (empty($location)) {
$location = new ScheduleOverviewLocation();
$location->name = $locationName;
$location->sort = 0;
$location->status = 1;
$location->admin_id = $legacy->admin_id;
$location->department_id = $legacy->department_id;
if (!$dryRun) {
$location->save();
}
}
$owner = ScheduleOverviewOwner::query()->where('name', $ownerName)->first();
if (empty($owner)) {
$owner = new ScheduleOverviewOwner();
$owner->name = $ownerName;
$owner->sort = 0;
$owner->status = 1;
$owner->admin_id = $legacy->admin_id;
$owner->department_id = $legacy->department_id;
if (!$dryRun) {
$owner->save();
}
}
$exists = ScheduleOverviewScheduleModule::query()
->where('group_id', $group->id)
->where('month', (int) $legacy->month)
->where('location_id', $location->id)
->where('owner_id', $owner->id)
->where('count_text', (string) ($legacy->count_text ?? ''))
->exists();
if ($exists) {
$skippedModules++;
continue;
}
$module = new ScheduleOverviewScheduleModule();
$module->group_id = $group->id;
$module->name = null;
$module->month = (int) $legacy->month;
$module->location_id = $location->id;
$module->owner_id = $owner->id;
$module->count_text = (string) ($legacy->count_text ?? '');
$module->sort = 0;
$module->admin_id = $legacy->admin_id;
$module->department_id = $legacy->department_id;
if (!$dryRun) {
$module->save();
}
$createdModules++;
}
if ($dryRun) {
DB::rollBack();
} else {
DB::commit();
}
} catch (\Throwable $throwable) {
DB::rollBack();
$this->error($throwable->getMessage());
return self::FAILURE;
}
$this->info("迁移完成:新增 groups={$createdGroups},新增 modules={$createdModules},跳过重复 modules={$skippedModules}");
if ($dryRun) {
$this->info('dry-run 未写入数据库。');
}
return self::SUCCESS;
}
}

@ -3,8 +3,13 @@
namespace App\Http\Controllers\Admin;
use App\Helpers\ResponseCode;
use App\Models\CourseType;
use App\Models\ScheduleOverviewCourse;
use App\Models\ScheduleOverviewLocation;
use App\Models\ScheduleOverviewOwner;
use App\Models\ScheduleOverviewSchedule;
use App\Models\ScheduleOverviewScheduleGroup;
use App\Models\ScheduleOverviewScheduleModule;
use App\Models\ScheduleOverviewSystem;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
@ -14,12 +19,20 @@ class ScheduleOverviewController extends CommonController
protected ScheduleOverviewSystem $systemModel;
protected ScheduleOverviewCourse $courseModel;
protected ScheduleOverviewSchedule $scheduleModel;
protected ScheduleOverviewScheduleGroup $scheduleGroupModel;
protected ScheduleOverviewScheduleModule $scheduleModuleModel;
protected ScheduleOverviewLocation $locationModel;
protected ScheduleOverviewOwner $ownerModel;
public function __construct()
{
$this->systemModel = new ScheduleOverviewSystem();
$this->courseModel = new ScheduleOverviewCourse();
$this->scheduleModel = new ScheduleOverviewSchedule();
$this->scheduleGroupModel = new ScheduleOverviewScheduleGroup();
$this->scheduleModuleModel = new ScheduleOverviewScheduleModule();
$this->locationModel = new ScheduleOverviewLocation();
$this->ownerModel = new ScheduleOverviewOwner();
}
public function overview()
@ -29,7 +42,8 @@ class ScheduleOverviewController extends CommonController
return $this->success([
'systems' => $this->getSystems($year),
'courses' => $this->getCourses($year),
'schedules' => $this->getSchedules($year),
// 兼容前端矩阵/统计:展开带班组 modules 为“按月扁平记录”
'schedules' => $this->getSchedulesFlatFromGroups($year),
]);
}
@ -155,7 +169,7 @@ class ScheduleOverviewController extends CommonController
return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]);
}
$detail = $this->courseModel->with('system')->find($all['id']);
$detail = $this->courseModel->with(['system', 'courseType'])->find($all['id']);
if (empty($detail)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '课程不存在']);
}
@ -168,6 +182,7 @@ class ScheduleOverviewController extends CommonController
$all = request()->all();
$validator = Validator::make($all, [
'system_id' => 'required|integer',
'course_type_id' => 'nullable|integer',
'name' => 'required|string|max:255',
'sort' => 'nullable|integer',
], [
@ -184,6 +199,14 @@ class ScheduleOverviewController extends CommonController
return $this->fail([ResponseCode::ERROR_BUSINESS, '所属体系不存在']);
}
$courseType = null;
if (!empty($all['course_type_id'])) {
$courseType = CourseType::query()->find($all['course_type_id']);
if (empty($courseType)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '课程类别不存在']);
}
}
if (!empty($all['id'])) {
$model = $this->courseModel->find($all['id']);
if (empty($model)) {
@ -199,7 +222,8 @@ class ScheduleOverviewController extends CommonController
try {
$originSystemId = $model->system_id;
$model->system_id = (int) $all['system_id'];
$model->name = $all['name'];
$model->course_type_id = !empty($all['course_type_id']) ? (int) $all['course_type_id'] : null;
$model->name = $courseType ? $courseType->name : $all['name'];
$model->sort = (int) ($all['sort'] ?? 0);
$model->save();
@ -210,7 +234,7 @@ class ScheduleOverviewController extends CommonController
}
DB::commit();
return $this->success($model->load('system'));
return $this->success($model->load(['system', 'courseType']));
} catch (\Throwable $throwable) {
DB::rollBack();
return $this->fail([ResponseCode::ERROR_INSIDE, $throwable->getMessage()]);
@ -252,7 +276,15 @@ class ScheduleOverviewController extends CommonController
{
$year = request('year', date('Y'));
return $this->success($this->getSchedules($year));
$list = $this->scheduleGroupModel
->with(['system', 'course', 'modules.location', 'modules.owner'])
->where('year', $year)
->orderBy('system_id')
->orderBy('course_id')
->orderBy('id')
->get();
return $this->success($list);
}
public function scheduleShow()
@ -261,16 +293,16 @@ class ScheduleOverviewController extends CommonController
$validator = Validator::make($all, [
'id' => 'required',
], [
'id.required' => '编排ID必填',
'id.required' => '带班ID必填',
]);
if ($validator->fails()) {
return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]);
}
$detail = $this->scheduleModel->with(['system', 'course'])->find($all['id']);
$detail = $this->scheduleGroupModel->with(['system', 'course', 'modules.location', 'modules.owner'])->find($all['id']);
if (empty($detail)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '编排不存在']);
return $this->fail([ResponseCode::ERROR_BUSINESS, '带班不存在']);
}
return $this->success($detail);
@ -279,139 +311,294 @@ class ScheduleOverviewController extends CommonController
public function scheduleSave()
{
$all = request()->all();
$monthValue = $all['month'] ?? null;
$monthList = is_array($monthValue) ? $monthValue : [$monthValue];
$rules = [
'year' => 'required|string|size:4',
'system_id' => 'required|integer',
'course_id' => 'required|integer',
'course_id' => 'nullable|integer',
'title' => 'required|string|max:255',
'owner' => 'required|string|max:255',
'location' => 'required|string|max:255',
'count_text' => 'nullable|string|max:255',
'modules' => 'required|array|min:1',
'modules.*.name' => 'nullable|string|max:255',
'modules.*.month' => 'required|integer|between:1,12',
'modules.*.location_id' => 'required|integer',
'modules.*.owner_id' => 'required|integer',
'modules.*.count_text' => 'nullable|string|max:255',
];
if (!empty($all['id'])) {
$rules['month'] = 'required|integer|between:1,12';
} else {
$rules['month'] = 'required|array|min:1';
$rules['month.*'] = 'required|integer|between:1,12';
}
$validator = Validator::make($all, $rules, [
'year.required' => '年份必填',
'system_id.required' => '体系必填',
'course_id.required' => '课程必填',
'month.required' => '月份必填',
'month.array' => '月份格式不正确',
'month.min' => '请至少选择一个月份',
'month.integer' => '月份格式不正确',
'month.between' => '月份范围需在1到12之间',
'month.*.required' => '月份必填',
'month.*.integer' => '月份格式不正确',
'month.*.between' => '月份范围需在1到12之间',
'title.required' => '编排标题必填',
'owner.required' => '负责人必填',
'location.required' => '地点必填',
'title.required' => '课程名称必填',
'modules.required' => '模块/期数必填',
'modules.array' => '模块/期数格式不正确',
'modules.min' => '模块/期数至少需要一条数据',
'modules.*.month.required' => '月份必填',
'modules.*.month.integer' => '月份格式不正确',
'modules.*.month.between' => '月份范围需在1到12之间',
'modules.*.location_id.required' => '地点必填',
'modules.*.owner_id.required' => '负责人必填',
]);
if ($validator->fails()) {
return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]);
}
if (empty($monthList)) {
return $this->fail([ResponseCode::ERROR_PARAMETER, '月份必填']);
$system = $this->systemModel->find($all['system_id']);
if (empty($system)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '体系不存在']);
}
foreach ($monthList as $month) {
if (!is_numeric($month) || (int) $month < 1 || (int) $month > 12) {
return $this->fail([ResponseCode::ERROR_PARAMETER, '月份范围需在1到12之间']);
$course = null;
if (!empty($all['course_id'])) {
$course = $this->courseModel->find($all['course_id']);
if (empty($course)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '课程不存在']);
}
if ((int) $course->system_id !== (int) $system->id) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '课程不属于当前体系']);
}
}
$system = $this->systemModel->find($all['system_id']);
if (empty($system)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '体系不存在']);
if ((string) $system->year !== (string) $all['year']) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '体系不属于当前年份']);
}
$course = $this->courseModel->find($all['course_id']);
if (empty($course)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '课程不存在']);
$modules = $all['modules'] ?? [];
$locationIds = collect($modules)->pluck('location_id')->filter()->unique()->values()->toArray();
$ownerIds = collect($modules)->pluck('owner_id')->filter()->unique()->values()->toArray();
$locations = $this->locationModel->whereIn('id', $locationIds)->get()->keyBy('id');
$owners = $this->ownerModel->whereIn('id', $ownerIds)->get()->keyBy('id');
foreach ($locationIds as $locationId) {
if (!$locations->has($locationId)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '地点不存在']);
}
}
foreach ($ownerIds as $ownerId) {
if (!$owners->has($ownerId)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '负责人不存在']);
}
}
if ((int) $course->system_id !== (int) $system->id) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '课程不属于当前体系']);
if (!empty($all['id'])) {
$model = $this->scheduleGroupModel->find($all['id']);
if (empty($model)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '带班不存在']);
}
} else {
$model = new ScheduleOverviewScheduleGroup();
$model->admin_id = $this->getUserId();
$model->department_id = optional($this->getUser())->department_id;
}
if ((string) $system->year !== (string) $all['year']) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '体系不属于当前年份']);
DB::beginTransaction();
try {
$model->year = $all['year'];
$model->system_id = (int) $all['system_id'];
$model->course_id = !empty($all['course_id']) ? (int) $all['course_id'] : null;
$model->title = $all['title'];
$model->save();
// 简化:保存时用“全量覆盖”的方式更新 modules
$this->scheduleModuleModel->where('group_id', $model->id)->delete();
foreach ($modules as $index => $moduleData) {
$module = new ScheduleOverviewScheduleModule();
$module->group_id = $model->id;
$module->name = $moduleData['name'] ?? null;
$module->month = (int) $moduleData['month'];
$module->location_id = (int) $moduleData['location_id'];
$module->owner_id = (int) $moduleData['owner_id'];
$module->count_text = $moduleData['count_text'] ?? '';
$module->sort = (int) ($moduleData['sort'] ?? $index);
$module->admin_id = $this->getUserId();
$module->department_id = optional($this->getUser())->department_id;
$module->save();
}
DB::commit();
return $this->success($model->load(['system', 'course', 'modules.location', 'modules.owner']));
} catch (\Throwable $throwable) {
DB::rollBack();
return $this->fail([ResponseCode::ERROR_INSIDE, $throwable->getMessage()]);
}
}
public function scheduleDestroy()
{
$all = request()->all();
$validator = Validator::make($all, [
'id' => 'required',
], [
'id.required' => '带班ID必填',
]);
if ($validator->fails()) {
return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]);
}
$model = $this->scheduleGroupModel->find($all['id']);
if (empty($model)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '带班不存在']);
}
DB::beginTransaction();
try {
$this->scheduleModuleModel->where('group_id', $model->id)->delete();
$model->delete();
DB::commit();
} catch (\Throwable $throwable) {
DB::rollBack();
return $this->fail([ResponseCode::ERROR_INSIDE, $throwable->getMessage()]);
}
return $this->success('删除成功');
}
public function locationIndex()
{
$list = $this->locationModel
->orderBy('sort')
->orderBy('id')
->get();
return $this->success($list);
}
public function locationSave()
{
$all = request()->all();
$validator = Validator::make($all, [
'name' => 'required|string|max:255',
'sort' => 'nullable|integer',
'status' => 'nullable|integer|in:0,1',
], [
'name.required' => '地点名称必填',
]);
if ($validator->fails()) {
return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]);
}
if (!empty($all['id'])) {
$model = $this->scheduleModel->find($all['id']);
$model = $this->locationModel->find($all['id']);
if (empty($model)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '编排不存在']);
return $this->fail([ResponseCode::ERROR_BUSINESS, '地点不存在']);
}
} else {
$model = new ScheduleOverviewSchedule();
$model = new ScheduleOverviewLocation();
$model->admin_id = $this->getUserId();
$model->department_id = optional($this->getUser())->department_id;
}
DB::beginTransaction();
try {
if (!empty($all['id'])) {
$model->year = $all['year'];
$model->system_id = (int) $all['system_id'];
$model->course_id = (int) $all['course_id'];
$model->month = (int) $monthList[0];
$model->title = $all['title'];
$model->owner = $all['owner'];
$model->location = $all['location'];
$model->count_text = $all['count_text'] ?? '';
$model->save();
} else {
foreach ($monthList as $month) {
$schedule = new ScheduleOverviewSchedule();
$schedule->admin_id = $this->getUserId();
$schedule->department_id = optional($this->getUser())->department_id;
$schedule->year = $all['year'];
$schedule->system_id = (int) $all['system_id'];
$schedule->course_id = (int) $all['course_id'];
$schedule->month = (int) $month;
$schedule->title = $all['title'];
$schedule->owner = $all['owner'];
$schedule->location = $all['location'];
$schedule->count_text = $all['count_text'] ?? '';
$schedule->save();
$model = $schedule;
}
$model->name = $all['name'];
$model->sort = (int) ($all['sort'] ?? 0);
$model->status = (int) ($all['status'] ?? 1);
$model->save();
DB::commit();
return $this->success($model);
} catch (\Throwable $throwable) {
DB::rollBack();
return $this->fail([ResponseCode::ERROR_INSIDE, $throwable->getMessage()]);
}
}
public function locationDestroy()
{
$all = request()->all();
$validator = Validator::make($all, [
'id' => 'required',
], [
'id.required' => '地点ID必填',
]);
if ($validator->fails()) {
return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]);
}
$model = $this->locationModel->find($all['id']);
if (empty($model)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '地点不存在']);
}
$used = $this->scheduleModuleModel->where('location_id', $model->id)->exists();
if ($used) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '地点已被使用,无法删除']);
}
$model->delete();
return $this->success('删除成功');
}
public function ownerIndex()
{
$list = $this->ownerModel
->orderBy('sort')
->orderBy('id')
->get();
return $this->success($list);
}
public function ownerSave()
{
$all = request()->all();
$validator = Validator::make($all, [
'name' => 'required|string|max:255',
'sort' => 'nullable|integer',
'status' => 'nullable|integer|in:0,1',
], [
'name.required' => '负责人名称必填',
]);
if ($validator->fails()) {
return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]);
}
if (!empty($all['id'])) {
$model = $this->ownerModel->find($all['id']);
if (empty($model)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '负责人不存在']);
}
} else {
$model = new ScheduleOverviewOwner();
$model->admin_id = $this->getUserId();
$model->department_id = optional($this->getUser())->department_id;
}
DB::beginTransaction();
try {
$model->name = $all['name'];
$model->sort = (int) ($all['sort'] ?? 0);
$model->status = (int) ($all['status'] ?? 1);
$model->save();
DB::commit();
return $this->success($model->load(['system', 'course']));
return $this->success($model);
} catch (\Throwable $throwable) {
DB::rollBack();
return $this->fail([ResponseCode::ERROR_INSIDE, $throwable->getMessage()]);
}
}
public function scheduleDestroy()
public function ownerDestroy()
{
$all = request()->all();
$validator = Validator::make($all, [
'id' => 'required',
], [
'id.required' => '编排ID必填',
'id.required' => '负责人ID必填',
]);
if ($validator->fails()) {
return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]);
}
$model = $this->scheduleModel->find($all['id']);
$model = $this->ownerModel->find($all['id']);
if (empty($model)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '编排不存在']);
return $this->fail([ResponseCode::ERROR_BUSINESS, '负责人不存在']);
}
$used = $this->scheduleModuleModel->where('owner_id', $model->id)->exists();
if ($used) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '负责人已被使用,无法删除']);
}
$model->delete();
@ -430,7 +617,7 @@ class ScheduleOverviewController extends CommonController
protected function getCourses($year)
{
return $this->courseModel
->with('system')
->with(['system', 'courseType'])
->whereHas('system', function ($query) use ($year) {
$query->where('year', $year);
})
@ -448,4 +635,50 @@ class ScheduleOverviewController extends CommonController
->orderBy('id')
->get();
}
protected function getSchedulesFlatFromGroups($year)
{
$groups = $this->scheduleGroupModel
->with(['system', 'course', 'modules.location', 'modules.owner'])
->where('year', $year)
->orderBy('id')
->get();
$flat = [];
foreach ($groups as $group) {
foreach ($group->modules as $module) {
$flat[] = [
'id' => $module->id,
'group_id' => $group->id,
'year' => $group->year,
'system_id' => $group->system_id,
'course_id' => $group->course_id,
'month' => $module->month,
'module_name' => $module->name,
'location_id' => $module->location_id,
'owner_id' => $module->owner_id,
'title' => $group->title,
'owner' => optional($module->owner)->name ?? '',
'location' => optional($module->location)->name ?? '',
'count_text' => $module->count_text ?? '',
'system' => $group->system,
'course' => $group->course,
];
}
}
usort($flat, function ($a, $b) {
$yearDiff = strcmp((string) ($a['year'] ?? ''), (string) ($b['year'] ?? ''));
if ($yearDiff !== 0) {
return $yearDiff;
}
$monthDiff = (int) ($a['month'] ?? 0) - (int) ($b['month'] ?? 0);
if ($monthDiff !== 0) {
return $monthDiff;
}
return (int) ($a['id'] ?? 0) - (int) ($b['id'] ?? 0);
});
return $flat;
}
}

@ -2,6 +2,8 @@
namespace App\Models;
use App\Models\CourseType;
class ScheduleOverviewCourse extends SoftDeletesModel
{
protected $table = 'schedule_overview_courses';
@ -11,6 +13,11 @@ class ScheduleOverviewCourse extends SoftDeletesModel
return $this->belongsTo(ScheduleOverviewSystem::class, 'system_id', 'id');
}
public function courseType()
{
return $this->belongsTo(CourseType::class, 'course_type_id', 'id');
}
public function schedules()
{
return $this->hasMany(ScheduleOverviewSchedule::class, 'course_id', 'id');

@ -0,0 +1,14 @@
<?php
namespace App\Models;
class ScheduleOverviewLocation extends SoftDeletesModel
{
protected $table = 'schedule_overview_locations';
public function modules()
{
return $this->hasMany(ScheduleOverviewScheduleModule::class, 'location_id', 'id');
}
}

@ -0,0 +1,14 @@
<?php
namespace App\Models;
class ScheduleOverviewOwner extends SoftDeletesModel
{
protected $table = 'schedule_overview_owners';
public function modules()
{
return $this->hasMany(ScheduleOverviewScheduleModule::class, 'owner_id', 'id');
}
}

@ -0,0 +1,24 @@
<?php
namespace App\Models;
class ScheduleOverviewScheduleGroup extends SoftDeletesModel
{
protected $table = 'schedule_overview_schedule_groups';
public function system()
{
return $this->belongsTo(ScheduleOverviewSystem::class, 'system_id', 'id');
}
public function course()
{
return $this->belongsTo(ScheduleOverviewCourse::class, 'course_id', 'id');
}
public function modules()
{
return $this->hasMany(ScheduleOverviewScheduleModule::class, 'group_id', 'id')->orderBy('month')->orderBy('sort')->orderBy('id');
}
}

@ -0,0 +1,24 @@
<?php
namespace App\Models;
class ScheduleOverviewScheduleModule extends SoftDeletesModel
{
protected $table = 'schedule_overview_schedule_modules';
public function group()
{
return $this->belongsTo(ScheduleOverviewScheduleGroup::class, 'group_id', 'id');
}
public function location()
{
return $this->belongsTo(ScheduleOverviewLocation::class, 'location_id', 'id');
}
public function owner()
{
return $this->belongsTo(ScheduleOverviewOwner::class, 'owner_id', 'id');
}
}

@ -43,7 +43,7 @@ return new class extends Migration
$table->unsignedBigInteger('system_id')->comment('体系ID');
$table->unsignedBigInteger('course_id')->comment('课程ID');
$table->unsignedTinyInteger('month')->comment('月份');
$table->string('title')->comment('编排标题');
$table->string('title')->comment('带班标题');
$table->string('owner')->comment('负责人');
$table->string('location')->comment('地点');
$table->string('count_text')->nullable()->comment('显示说明');

@ -0,0 +1,41 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
if (!Schema::hasTable('schedule_overview_courses')) {
return;
}
Schema::table('schedule_overview_courses', function (Blueprint $table) {
if (!Schema::hasColumn('schedule_overview_courses', 'course_type_id')) {
$table->unsignedBigInteger('course_type_id')->nullable()->after('system_id')->comment('课程类别ID');
$table->index('course_type_id');
}
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
if (!Schema::hasTable('schedule_overview_courses') || !Schema::hasColumn('schedule_overview_courses', 'course_type_id')) {
return;
}
Schema::table('schedule_overview_courses', function (Blueprint $table) {
$table->dropIndex(['course_type_id']);
$table->dropColumn('course_type_id');
});
}
};

@ -0,0 +1,52 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('schedule_overview_schedule_groups', function (Blueprint $table) {
$table->id();
$table->string('year', 4)->comment('年份');
$table->unsignedBigInteger('system_id')->comment('计划体系ID');
$table->unsignedBigInteger('course_id')->comment('课程体系ID');
$table->string('title')->comment('带班标题');
$table->bigInteger('admin_id')->nullable()->comment('创建管理员');
$table->bigInteger('department_id')->nullable()->comment('部门ID');
$table->timestamps();
$table->softDeletes();
$table->index(['year', 'system_id']);
$table->index(['system_id', 'course_id']);
});
Schema::create('schedule_overview_schedule_modules', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('group_id')->comment('带班组ID');
$table->string('name')->nullable()->comment('模块/期数名称');
$table->unsignedTinyInteger('month')->comment('月份');
$table->unsignedBigInteger('location_id')->comment('地点ID');
$table->unsignedBigInteger('owner_id')->comment('负责人ID');
$table->string('count_text')->nullable()->comment('显示说明');
$table->integer('sort')->default(0)->comment('排序');
$table->bigInteger('admin_id')->nullable()->comment('创建管理员');
$table->bigInteger('department_id')->nullable()->comment('部门ID');
$table->timestamps();
$table->softDeletes();
$table->index(['group_id', 'month']);
$table->index('location_id');
$table->index('owner_id');
});
}
public function down(): void
{
Schema::dropIfExists('schedule_overview_schedule_modules');
Schema::dropIfExists('schedule_overview_schedule_groups');
}
};

@ -0,0 +1,46 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('schedule_overview_locations', function (Blueprint $table) {
$table->id();
$table->string('name')->comment('地点名称');
$table->integer('sort')->default(0)->comment('排序');
$table->tinyInteger('status')->default(1)->comment('状态0禁用1启用');
$table->bigInteger('admin_id')->nullable()->comment('创建管理员');
$table->bigInteger('department_id')->nullable()->comment('部门ID');
$table->timestamps();
$table->softDeletes();
$table->index(['status', 'sort']);
$table->unique(['name'], 'uniq_schedule_overview_locations_name');
});
Schema::create('schedule_overview_owners', function (Blueprint $table) {
$table->id();
$table->string('name')->comment('负责人名称');
$table->integer('sort')->default(0)->comment('排序');
$table->tinyInteger('status')->default(1)->comment('状态0禁用1启用');
$table->bigInteger('admin_id')->nullable()->comment('创建管理员');
$table->bigInteger('department_id')->nullable()->comment('部门ID');
$table->timestamps();
$table->softDeletes();
$table->index(['status', 'sort']);
$table->unique(['name'], 'uniq_schedule_overview_owners_name');
});
}
public function down(): void
{
Schema::dropIfExists('schedule_overview_owners');
Schema::dropIfExists('schedule_overview_locations');
}
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -95,6 +95,14 @@ Route::group(["namespace" => "Admin", "prefix" => "admin"], function () {
Route::post('schedule-overview/schedules/save', [\App\Http\Controllers\Admin\ScheduleOverviewController::class, "scheduleSave"]);
Route::get('schedule-overview/schedules/destroy', [\App\Http\Controllers\Admin\ScheduleOverviewController::class, "scheduleDestroy"]);
// 课程排期总览:地点/负责人字典
Route::get('schedule-overview/locations/index', [\App\Http\Controllers\Admin\ScheduleOverviewController::class, "locationIndex"]);
Route::post('schedule-overview/locations/save', [\App\Http\Controllers\Admin\ScheduleOverviewController::class, "locationSave"]);
Route::get('schedule-overview/locations/destroy', [\App\Http\Controllers\Admin\ScheduleOverviewController::class, "locationDestroy"]);
Route::get('schedule-overview/owners/index', [\App\Http\Controllers\Admin\ScheduleOverviewController::class, "ownerIndex"]);
Route::post('schedule-overview/owners/save', [\App\Http\Controllers\Admin\ScheduleOverviewController::class, "ownerSave"]);
Route::get('schedule-overview/owners/destroy', [\App\Http\Controllers\Admin\ScheduleOverviewController::class, "ownerDestroy"]);
// 通讯录
Route::get('users/index', [\App\Http\Controllers\Admin\UserController::class, "index"]);
Route::get('users/study', [\App\Http\Controllers\Admin\UserController::class, "study"]);

Loading…
Cancel
Save