教师
This commit is contained in:
2
.env
2
.env
@@ -9,7 +9,7 @@ DB_USERNAME=hyperf_data
|
|||||||
DB_PASSWORD=4cfDRXZSksn7npiP
|
DB_PASSWORD=4cfDRXZSksn7npiP
|
||||||
DB_CHARSET=utf8mb4
|
DB_CHARSET=utf8mb4
|
||||||
DB_COLLATION=utf8mb4_unicode_ci
|
DB_COLLATION=utf8mb4_unicode_ci
|
||||||
DB_PREFIX=da_
|
DB_PREFIX=
|
||||||
|
|
||||||
REDIS_HOST=localhost
|
REDIS_HOST=localhost
|
||||||
REDIS_AUTH=(null)
|
REDIS_AUTH=(null)
|
||||||
|
|||||||
162
app/Controller/TeacherController.php
Normal file
162
app/Controller/TeacherController.php
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use Aether\AetherController;
|
||||||
|
use Aether\AetherResponse;
|
||||||
|
use App\Exception\BusinessException;
|
||||||
|
use App\Model\Teacher;
|
||||||
|
use App\Validator\TeacherValidator;
|
||||||
|
use Hyperf\Di\Annotation\Inject;
|
||||||
|
use Hyperf\HttpServer\Contract\RequestInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Controller(prefix="/api/v1/teacher")
|
||||||
|
*/
|
||||||
|
class TeacherController extends AetherController
|
||||||
|
{
|
||||||
|
#[Inject]
|
||||||
|
protected TeacherValidator $validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建教师.
|
||||||
|
* @PostMapping(path="")
|
||||||
|
*/
|
||||||
|
public function create(RequestInterface $request): array
|
||||||
|
{
|
||||||
|
$data = $request->all();
|
||||||
|
$validator = $this->validator->validateCreate($data);
|
||||||
|
if ($validator->fails()) {
|
||||||
|
throw new BusinessException(400, $validator->errors()->first());
|
||||||
|
}
|
||||||
|
|
||||||
|
$teacher = new Teacher();
|
||||||
|
$teacher->fill($data);
|
||||||
|
$teacher->save();
|
||||||
|
|
||||||
|
return AetherResponse::success($teacher->toArray(), '教师创建成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取教师详情.
|
||||||
|
* @GetMapping(path="/{id}")
|
||||||
|
*/
|
||||||
|
public function get(int $id): array
|
||||||
|
{
|
||||||
|
$teacher = Teacher::find($id);
|
||||||
|
if (! $teacher) {
|
||||||
|
throw new BusinessException(10101);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AetherResponse::success($teacher->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新教师信息.
|
||||||
|
* @PutMapping(path="/{id}")
|
||||||
|
*/
|
||||||
|
public function update(int $id, RequestInterface $request): array
|
||||||
|
{
|
||||||
|
$data = $request->all();
|
||||||
|
$data['id'] = $id;
|
||||||
|
|
||||||
|
$validator = $this->validator->validateUpdate($data);
|
||||||
|
if ($validator->fails()) {
|
||||||
|
throw new BusinessException(400, $validator->errors()->first());
|
||||||
|
}
|
||||||
|
|
||||||
|
$teacher = Teacher::find($id);
|
||||||
|
if (! $teacher) {
|
||||||
|
throw new BusinessException(10101);
|
||||||
|
}
|
||||||
|
|
||||||
|
$teacher->fill($data);
|
||||||
|
$teacher->save();
|
||||||
|
|
||||||
|
return AetherResponse::success($teacher->toArray(), '教师信息更新成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除教师.
|
||||||
|
* @DeleteMapping(path="/{id}")
|
||||||
|
*/
|
||||||
|
public function delete(int $id): array
|
||||||
|
{
|
||||||
|
$teacher = Teacher::find($id);
|
||||||
|
if (! $teacher) {
|
||||||
|
throw new BusinessException(10101);
|
||||||
|
}
|
||||||
|
|
||||||
|
$teacher->delete();
|
||||||
|
return AetherResponse::success(null, '教师删除成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 教师列表.
|
||||||
|
* @GetMapping(path="/list")
|
||||||
|
*/
|
||||||
|
public function list(RequestInterface $request): array
|
||||||
|
{
|
||||||
|
$data = $request->all();
|
||||||
|
$validator = $this->validator->validateQuery($data);
|
||||||
|
if ($validator->fails()) {
|
||||||
|
throw new BusinessException(400, $validator->errors()->first());
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = Teacher::query();
|
||||||
|
|
||||||
|
if (! empty($data['id'])) {
|
||||||
|
$query->where('id', $data['id']);
|
||||||
|
}
|
||||||
|
if (! empty($data['name'])) {
|
||||||
|
$query->where('name', 'like', "%{$data['name']}%");
|
||||||
|
}
|
||||||
|
if (isset($data['campus_id'])) {
|
||||||
|
$query->where('campus_id', $data['campus_id']);
|
||||||
|
}
|
||||||
|
if (! empty($data['major_subject'])) {
|
||||||
|
$query->where('major_subject', 'like', "%{$data['major_subject']}%");
|
||||||
|
}
|
||||||
|
if (isset($data['status'])) {
|
||||||
|
$query->where('status', $data['status']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$page = $data['page'] ?? 1;
|
||||||
|
$size = $data['size'] ?? 20;
|
||||||
|
|
||||||
|
$total = $query->count();
|
||||||
|
$list = $query->forPage($page, $size)->get()->toArray();
|
||||||
|
|
||||||
|
return AetherResponse::success([
|
||||||
|
'total' => $total,
|
||||||
|
'page' => $page,
|
||||||
|
'size' => $size,
|
||||||
|
'list' => $list,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索教师.
|
||||||
|
* @GetMapping(path="/search")
|
||||||
|
*/
|
||||||
|
public function search(RequestInterface $request): array
|
||||||
|
{
|
||||||
|
$keyword = $request->input('keyword', '');
|
||||||
|
$page = (int) $request->input('page', 1);
|
||||||
|
$size = (int) $request->input('size', 20);
|
||||||
|
|
||||||
|
$query = Teacher::search($keyword)->enabled();
|
||||||
|
|
||||||
|
$total = $query->count();
|
||||||
|
$list = $query->forPage($page, $size)->get()->toArray();
|
||||||
|
|
||||||
|
return AetherResponse::success([
|
||||||
|
'total' => $total,
|
||||||
|
'page' => $page,
|
||||||
|
'size' => $size,
|
||||||
|
'list' => $list,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,6 +38,9 @@ class BusinessException extends ServerException
|
|||||||
10002 => '校区已存在',
|
10002 => '校区已存在',
|
||||||
10003 => '父级校区不存在',
|
10003 => '父级校区不存在',
|
||||||
10004 => '层级参数错误',
|
10004 => '层级参数错误',
|
||||||
|
10101 => '教师不存在',
|
||||||
|
10102 => '教师已存在',
|
||||||
|
10103 => '该教师已有关联课程,无法删除',
|
||||||
];
|
];
|
||||||
|
|
||||||
return $messages[$code] ?? '业务处理失败';
|
return $messages[$code] ?? '业务处理失败';
|
||||||
|
|||||||
88
app/JsonRpc/Service/TeacherService.php
Normal file
88
app/JsonRpc/Service/TeacherService.php
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\JsonRpc\Service;
|
||||||
|
|
||||||
|
use Aether\Exception\BusinessException;
|
||||||
|
use App\Model\Teacher;
|
||||||
|
use Hyperf\RpcServer\Annotation\RpcService;
|
||||||
|
use MicroService\Contract\TeacherServiceInterface;
|
||||||
|
|
||||||
|
#[RpcService(
|
||||||
|
name: 'DataTeacher',
|
||||||
|
server: 'jsonrpc-http',
|
||||||
|
protocol: 'jsonrpc-http',
|
||||||
|
publishTo: 'nacos'
|
||||||
|
)]
|
||||||
|
class TeacherService implements TeacherServiceInterface
|
||||||
|
{
|
||||||
|
public function getTeacherById(int $id): array
|
||||||
|
{
|
||||||
|
$teacher = Teacher::find($id);
|
||||||
|
if (! $teacher || $teacher->status != 1) {
|
||||||
|
throw new BusinessException('教师不存在或已禁用', 10101);
|
||||||
|
}
|
||||||
|
return $teacher->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTeachersByIds(array $ids): array
|
||||||
|
{
|
||||||
|
return Teacher::whereIn('id', $ids)
|
||||||
|
->enabled()
|
||||||
|
->orderBy('sort_order', 'asc')
|
||||||
|
->get()
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTeachersByCampusId(int $campusId, int $page = 1, int $size = 20): array
|
||||||
|
{
|
||||||
|
$query = Teacher::campusId($campusId)
|
||||||
|
->enabled()
|
||||||
|
->orderBy('sort_order', 'asc');
|
||||||
|
|
||||||
|
$total = $query->count();
|
||||||
|
$list = $query->forPage($page, $size)->get()->toArray();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'total' => $total,
|
||||||
|
'page' => $page,
|
||||||
|
'size' => $size,
|
||||||
|
'list' => $list,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTeachersBySubject(string $subject, int $page = 1, int $size = 20): array
|
||||||
|
{
|
||||||
|
$query = Teacher::subject($subject)
|
||||||
|
->enabled()
|
||||||
|
->orderBy('sort_order', 'asc');
|
||||||
|
|
||||||
|
$total = $query->count();
|
||||||
|
$list = $query->forPage($page, $size)->get()->toArray();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'total' => $total,
|
||||||
|
'page' => $page,
|
||||||
|
'size' => $size,
|
||||||
|
'list' => $list,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchTeachers(string $keyword, int $page = 1, int $size = 20): array
|
||||||
|
{
|
||||||
|
$query = Teacher::search($keyword)
|
||||||
|
->enabled()
|
||||||
|
->orderBy('sort_order', 'asc');
|
||||||
|
|
||||||
|
$total = $query->count();
|
||||||
|
$list = $query->forPage($page, $size)->get()->toArray();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'total' => $total,
|
||||||
|
'page' => $page,
|
||||||
|
'size' => $size,
|
||||||
|
'list' => $list,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ use Hyperf\Database\Model\Relations\HasMany;
|
|||||||
*/
|
*/
|
||||||
class Campus extends AetherModel
|
class Campus extends AetherModel
|
||||||
{
|
{
|
||||||
protected ?string $table = 'campus';
|
protected ?string $table = 'da_campus';
|
||||||
|
|
||||||
protected array $fillable = [
|
protected array $fillable = [
|
||||||
'name',
|
'name',
|
||||||
|
|||||||
106
app/Model/Teacher.php
Normal file
106
app/Model/Teacher.php
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Model;
|
||||||
|
|
||||||
|
use Aether\AetherModel;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Hyperf\Database\Model\Builder;
|
||||||
|
use Hyperf\Database\Model\Relations\BelongsTo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 教师模型.
|
||||||
|
* @property int $id
|
||||||
|
* @property string $name 教师姓名
|
||||||
|
* @property int $age 年龄
|
||||||
|
* @property int $gender 性别:1-男,2-女
|
||||||
|
* @property string $avatar 头像URL
|
||||||
|
* @property string $title 职称
|
||||||
|
* @property string $major_subject 主讲科目
|
||||||
|
* @property string $teaching_style 授课风格
|
||||||
|
* @property string $introduction 教师简介
|
||||||
|
* @property int $campus_id 所属校区ID
|
||||||
|
* @property int $status 状态:0-禁用,1-启用
|
||||||
|
* @property int $sort_order 排序
|
||||||
|
* @property Carbon $created_at
|
||||||
|
* @property Carbon $updated_at
|
||||||
|
* @property Carbon $deleted_at
|
||||||
|
*/
|
||||||
|
class Teacher extends AetherModel
|
||||||
|
{
|
||||||
|
protected ?string $table = 'da_teacher';
|
||||||
|
|
||||||
|
protected array $fillable = [
|
||||||
|
'name',
|
||||||
|
'age',
|
||||||
|
'gender',
|
||||||
|
'avatar',
|
||||||
|
'title',
|
||||||
|
'major_subject',
|
||||||
|
'teaching_style',
|
||||||
|
'introduction',
|
||||||
|
'campus_id',
|
||||||
|
'status',
|
||||||
|
'sort_order',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected array $casts = [
|
||||||
|
'id' => 'integer',
|
||||||
|
'age' => 'integer',
|
||||||
|
'gender' => 'integer',
|
||||||
|
'campus_id' => 'integer',
|
||||||
|
'status' => 'integer',
|
||||||
|
'sort_order' => 'integer',
|
||||||
|
'created_at' => 'datetime',
|
||||||
|
'updated_at' => 'datetime',
|
||||||
|
'deleted_at' => 'datetime',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所属校区.
|
||||||
|
*/
|
||||||
|
public function campus(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Campus::class, 'campus_id', 'id')
|
||||||
|
->where('status', 1)
|
||||||
|
->whereNull('deleted_at');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按校区查询.
|
||||||
|
*/
|
||||||
|
public function scopeCampusId(Builder $query, int $campusId): Builder
|
||||||
|
{
|
||||||
|
return $query->where('campus_id', $campusId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按科目查询.
|
||||||
|
*/
|
||||||
|
public function scopeSubject(Builder $query, string $subject): Builder
|
||||||
|
{
|
||||||
|
return $query->where('major_subject', 'like', "%{$subject}%");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索教师.
|
||||||
|
*/
|
||||||
|
public function scopeSearch(Builder $query, string $keyword): Builder
|
||||||
|
{
|
||||||
|
return $query->where(function ($q) use ($keyword) {
|
||||||
|
$q->where('name', 'like', "%{$keyword}%")
|
||||||
|
->orWhere('title', 'like', "%{$keyword}%")
|
||||||
|
->orWhere('major_subject', 'like', "%{$keyword}%")
|
||||||
|
->orWhere('introduction', 'like', "%{$keyword}%");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询启用的教师.
|
||||||
|
*/
|
||||||
|
public function scopeEnabled(Builder $query): Builder
|
||||||
|
{
|
||||||
|
return $query->where('status', 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
77
app/Validator/TeacherValidator.php
Normal file
77
app/Validator/TeacherValidator.php
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Validator;
|
||||||
|
|
||||||
|
use Hyperf\Validation\Contract\ValidatorFactoryInterface;
|
||||||
|
use Hyperf\Validation\Validator;
|
||||||
|
|
||||||
|
class TeacherValidator
|
||||||
|
{
|
||||||
|
public function __construct(protected ValidatorFactoryInterface $validationFactory)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateCreate(array $data): Validator
|
||||||
|
{
|
||||||
|
return $this->validationFactory->make($data, [
|
||||||
|
'name' => 'required|string|max:50',
|
||||||
|
'age' => 'nullable|integer|min:18|max:65',
|
||||||
|
'gender' => 'nullable|integer|in:1,2',
|
||||||
|
'avatar' => 'nullable|url|max:255',
|
||||||
|
'title' => 'nullable|string|max:100',
|
||||||
|
'major_subject' => 'required|string|max:100',
|
||||||
|
'teaching_style' => 'nullable|string',
|
||||||
|
'introduction' => 'nullable|string',
|
||||||
|
'campus_id' => 'nullable|integer|min:0',
|
||||||
|
'status' => 'nullable|integer|in:0,1',
|
||||||
|
'sort_order' => 'nullable|integer|min:0',
|
||||||
|
], [
|
||||||
|
'name.required' => '教师姓名不能为空',
|
||||||
|
'major_subject.required' => '主讲科目不能为空',
|
||||||
|
'age.min' => '年龄不能小于18岁',
|
||||||
|
'age.max' => '年龄不能大于65岁',
|
||||||
|
'gender.in' => '性别只能是1(男)或2(女)',
|
||||||
|
'avatar.url' => '头像必须是有效的URL',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateUpdate(array $data): Validator
|
||||||
|
{
|
||||||
|
return $this->validationFactory->make($data, [
|
||||||
|
'id' => 'required|integer|min:1',
|
||||||
|
'name' => 'nullable|string|max:50',
|
||||||
|
'age' => 'nullable|integer|min:18|max:65',
|
||||||
|
'gender' => 'nullable|integer|in:1,2',
|
||||||
|
'avatar' => 'nullable|url|max:255',
|
||||||
|
'title' => 'nullable|string|max:100',
|
||||||
|
'major_subject' => 'nullable|string|max:100',
|
||||||
|
'teaching_style' => 'nullable|string',
|
||||||
|
'introduction' => 'nullable|string',
|
||||||
|
'campus_id' => 'nullable|integer|min:0',
|
||||||
|
'status' => 'nullable|integer|in:0,1',
|
||||||
|
'sort_order' => 'nullable|integer|min:0',
|
||||||
|
], [
|
||||||
|
'id.required' => '教师ID不能为空',
|
||||||
|
'age.min' => '年龄不能小于18岁',
|
||||||
|
'age.max' => '年龄不能大于65岁',
|
||||||
|
'gender.in' => '性别只能是1(男)或2(女)',
|
||||||
|
'avatar.url' => '头像必须是有效的URL',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateQuery(array $data): Validator
|
||||||
|
{
|
||||||
|
return $this->validationFactory->make($data, [
|
||||||
|
'id' => 'nullable|integer|min:1',
|
||||||
|
'name' => 'nullable|string|max:50',
|
||||||
|
'campus_id' => 'nullable|integer|min:0',
|
||||||
|
'major_subject' => 'nullable|string|max:100',
|
||||||
|
'status' => 'nullable|integer|in:0,1',
|
||||||
|
'page' => 'nullable|integer|min:1',
|
||||||
|
'size' => 'nullable|integer|min:1|max:100',
|
||||||
|
'keyword' => 'nullable|string|max:100',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,5 +48,19 @@ return [
|
|||||||
// 'heartbeat' => 5,
|
// 'heartbeat' => 5,
|
||||||
// 'heartbeat_timeout' => 15,
|
// 'heartbeat_timeout' => 15,
|
||||||
// ],
|
// ],
|
||||||
|
[
|
||||||
|
'name' => 'DataTeacher',
|
||||||
|
'host' => env('NACOS_HOST', '192.168.28.199'),
|
||||||
|
'port' => env('NACOS_PORT', 8848),
|
||||||
|
'username' => env('NACOS_USERNAME', 'nacos'),
|
||||||
|
'password' => env('NACOS_PASSWORD', 'nacos'),
|
||||||
|
'namespace' => env('NACOS_NAMESPACE', 'e42b853c-5195-478b-b5e3-6d49f6a45053'),
|
||||||
|
'group_name' => env('NACOS_GROUP', 'DEFAULT_GROUP'),
|
||||||
|
'service_name' => 'DataTeacher',
|
||||||
|
'metadata' => [
|
||||||
|
'protocol' => 'jsonrpc-http',
|
||||||
|
'server' => 'jsonrpc-http',
|
||||||
|
],
|
||||||
|
],
|
||||||
// ],
|
// ],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,8 +1,44 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace MicroService\Contract;
|
namespace MicroService\Contract;
|
||||||
|
|
||||||
interface TeacherServiceInterface
|
interface TeacherServiceInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* 获取教师详情.
|
||||||
|
* @param int $id 教师ID
|
||||||
|
*/
|
||||||
|
public function getTeacherById(int $id): array;
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* 批量获取教师信息.
|
||||||
|
* @param array $ids 教师ID列表
|
||||||
|
*/
|
||||||
|
public function getTeachersByIds(array $ids): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据校区获取教师列表.
|
||||||
|
* @param int $campusId 校区ID
|
||||||
|
* @param int $page 页码
|
||||||
|
* @param int $size 每页条数
|
||||||
|
*/
|
||||||
|
public function getTeachersByCampusId(int $campusId, int $page = 1, int $size = 20): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据科目获取教师列表.
|
||||||
|
* @param string $subject 科目名称
|
||||||
|
* @param int $page 页码
|
||||||
|
* @param int $size 每页条数
|
||||||
|
*/
|
||||||
|
public function getTeachersBySubject(string $subject, int $page = 1, int $size = 20): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索教师.
|
||||||
|
* @param string $keyword 搜索关键词
|
||||||
|
* @param int $page 页码
|
||||||
|
* @param int $size 每页条数
|
||||||
|
*/
|
||||||
|
public function searchTeachers(string $keyword, int $page = 1, int $size = 20): array;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user