feat(models): 添加模型类文档注释并实现MarkdownFileVO扩展功能

- 为Grouping、Image、MarkdownFile、RegistrationCode、SystemSetting、User等模型类添加详细的PHP文档注释
- 为各个模型类添加公共属性定义,包括分组、图片、笔记、注册码、系统设置和用户相关字段
- 实现MarkdownFileVO视图对象类,扩展MarkdownFile模型以支持分组名称查询
- 在MarkdownController中使用MarkdownFileVO替代原模型获取包含分组名称的数据
- 添加TrashItemVo视图对象用于回收站项目的展示
- 添加UpdatePasswordBo业务对象用于密码更新请求处理
- 为RegistrationCode模型添加按ID和代码查询的方法及详细验证功能
- 为SystemSetting模型添加带描述的设置获取和设置方法
This commit is contained in:
ikmkj
2026-01-26 21:42:53 +08:00
parent 881c3f5535
commit 39bb93cc2f
12 changed files with 533 additions and 2 deletions

View File

@@ -0,0 +1,92 @@
<?php
namespace App\BO;
/**
* UpdatePasswordBo - 更新密码业务对象
* 用于接收前端的密码更新请求
*/
class UpdatePasswordBo
{
/**
* @var string 旧密码(必填)
*/
public $oldPassword;
/**
* @var string 新密码(必填)
*/
public $newPassword;
/**
* 构造函数
* @param string $oldPassword 旧密码
* @param string $newPassword 新密码
*/
public function __construct($oldPassword = '', $newPassword = '')
{
$this->oldPassword = $oldPassword;
$this->newPassword = $newPassword;
}
/**
* 验证密码更新对象的有效性
* @return array 返回验证结果 ['valid' => bool, 'errors' => array]
*/
public function validate()
{
$errors = [];
if (empty($this->oldPassword)) {
$errors[] = '旧密码不能为空';
}
if (empty($this->newPassword)) {
$errors[] = '新密码不能为空';
}
if (!empty($this->oldPassword) && !empty($this->newPassword)) {
if ($this->oldPassword === $this->newPassword) {
$errors[] = '新密码不能与旧密码相同';
}
if (strlen($this->newPassword) < 6) {
$errors[] = '新密码长度不能少于6个字符';
}
if (strlen($this->newPassword) > 32) {
$errors[] = '新密码长度不能超过32个字符';
}
}
return [
'valid' => empty($errors),
'errors' => $errors
];
}
/**
* 将对象转换为数组
* @return array
*/
public function toArray()
{
return [
'oldPassword' => $this->oldPassword,
'newPassword' => $this->newPassword,
];
}
/**
* 从数组创建对象
* @param array $data
* @return UpdatePasswordBo
*/
public static function fromArray($data)
{
return new self(
$data['oldPassword'] ?? '',
$data['newPassword'] ?? ''
);
}
}

View File

@@ -5,6 +5,7 @@ namespace App\Controllers;
use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ServerRequestInterface as Request;
use App\Models\MarkdownFile; use App\Models\MarkdownFile;
use App\Models\MarkdownFileVO;
use App\Utils\Response as ApiResponse; use App\Utils\Response as ApiResponse;
class MarkdownController class MarkdownController
@@ -98,8 +99,9 @@ class MarkdownController
public function getRecent(Request $request, Response $response) public function getRecent(Request $request, Response $response)
{ {
$model = new MarkdownFile(); // 使用 MarkdownFileVO 获取包含分组名称的数据
$files = $model->getRecent(12); $voModel = new MarkdownFileVO();
$files = $voModel->getRecentWithGrouping(12);
$response->getBody()->write(ApiResponse::json(ApiResponse::success($files))); $response->getBody()->write(ApiResponse::json(ApiResponse::success($files)));
return $response->withHeader('Content-Type', 'application/json; charset=utf-8'); return $response->withHeader('Content-Type', 'application/json; charset=utf-8');

View File

@@ -5,10 +5,30 @@ namespace App\Models;
use App\Utils\Database; use App\Utils\Database;
use PDO; use PDO;
/**
* Grouping - 分组模型
*
* 数据库表: grouping
* 主要字段:
* - id (Long) - 分组ID雪花算法生成
* - parentId (Long) - 上级分组ID支持分组嵌套
* - grouping (String) - 分组名称
* - is_deleted (Integer) - 逻辑删除标志0-未删除1-已删除)
* - deleted_at (DateTime) - 删除时间
* - deleted_by (Long) - 删除人ID
*/
class Grouping class Grouping
{ {
private $db; private $db;
// 分组属性
public $id;
public $parentId;
public $grouping;
public $isDeleted;
public $deletedAt;
public $deletedBy;
public function __construct() public function __construct()
{ {
$this->db = Database::getInstance()->getConnection(); $this->db = Database::getInstance()->getConnection();

View File

@@ -5,10 +5,34 @@ namespace App\Models;
use App\Utils\Database; use App\Utils\Database;
use PDO; use PDO;
/**
* Image - 图片模型
*
* 数据库表: image
* 主要字段:
* - id (Long) - 图片ID自增主键
* - markdown_id (Long) - 关联的Markdown文件ID
* - original_name (String) - 原始文件名
* - stored_name (String) - 系统生成的唯一文件名
* - url (String) - 图片访问URL
* - size (Long) - 图片大小(字节)
* - content_type (String) - MIME类型如image/jpeg
* - created_at (DateTime) - 上传时间
*/
class Image class Image
{ {
private $db; private $db;
// 图片属性
public $id;
public $markdownId;
public $originalName;
public $storedName;
public $url;
public $size;
public $contentType;
public $createdAt;
public function __construct() public function __construct()
{ {
$this->db = Database::getInstance()->getConnection(); $this->db = Database::getInstance()->getConnection();

View File

@@ -4,6 +4,10 @@ namespace App\Models;
use App\Utils\Database; use App\Utils\Database;
/**
* ImageCleanup - 图片清理模型
* 用于清理冗余图片和维护数据库与文件系统的一致性
*/
class ImageCleanup class ImageCleanup
{ {
private $db; private $db;

View File

@@ -5,10 +5,40 @@ namespace App\Models;
use App\Utils\Database; use App\Utils\Database;
use PDO; use PDO;
/**
* MarkdownFile - 笔记文件模型
*
* 数据库表: markdown_file
* 主要字段:
* - id (Long) - 笔记ID自增主键
* - grouping_id (Long) - 所属分组ID
* - title (String) - 笔记标题
* - file_name (String) - 文件名(带.md后缀
* - content (String) - Markdown内容
* - created_at (DateTime) - 创建时间
* - updated_at (DateTime) - 更新时间
* - is_deleted (Integer) - 逻辑删除标志
* - deleted_at (DateTime) - 删除时间
* - deleted_by (Long) - 删除人ID
* - is_private (Integer) - 是否私密0-公开1-私密)
*/
class MarkdownFile class MarkdownFile
{ {
private $db; private $db;
// 笔记属性
public $id;
public $groupingId;
public $title;
public $fileName;
public $content;
public $createdAt;
public $updatedAt;
public $isDeleted;
public $deletedAt;
public $deletedBy;
public $isPrivate;
public function __construct() public function __construct()
{ {
$this->db = Database::getInstance()->getConnection(); $this->db = Database::getInstance()->getConnection();

View File

@@ -0,0 +1,159 @@
<?php
namespace App\Models;
use App\Utils\Database;
use PDO;
/**
* MarkdownFileVO - 笔记文件视图对象
* 扩展 MarkdownFile添加分组名称等展示字段
*/
class MarkdownFileVO extends MarkdownFile
{
private $db;
public function __construct()
{
parent::__construct();
$this->db = Database::getInstance()->getConnection();
}
/**
* 获取笔记详情,包含分组名称
* @param $id 笔记ID
* @param bool $includeDeleted 是否包含已删除的笔记
* @return array|false
*/
public function findByIdWithGrouping($id, $includeDeleted = false)
{
$sql = "SELECT
mf.id,
mf.title,
mf.file_name as fileName,
mf.content,
mf.grouping_id as groupingId,
mf.created_at as createdAt,
mf.updated_at as updatedAt,
mf.is_deleted as isDeleted,
mf.deleted_at as deletedAt,
mf.deleted_by as deletedBy,
mf.is_private as isPrivate,
g.grouping as groupingName
FROM markdown_file mf
LEFT JOIN `grouping` g ON mf.grouping_id = g.id
WHERE mf.id = ?";
if (!$includeDeleted) {
$sql .= " AND mf.is_deleted = 0";
}
$stmt = $this->db->prepare($sql);
$stmt->execute([$id]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
/**
* 获取所有笔记,包含分组名称
* @return array
*/
public function getAllWithGrouping()
{
$stmt = $this->db->prepare(
"SELECT
mf.id,
mf.title,
mf.file_name as fileName,
mf.created_at as createdAt,
mf.updated_at as updatedAt,
mf.is_private as isPrivate,
mf.grouping_id as groupingId,
g.grouping as groupingName
FROM markdown_file mf
LEFT JOIN `grouping` g ON mf.grouping_id = g.id
WHERE mf.is_deleted = 0
ORDER BY mf.updated_at DESC"
);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* 按分组ID获取笔记包含分组名称
* @param $groupingId 分组ID
* @return array
*/
public function getByGroupingIdWithGrouping($groupingId)
{
$stmt = $this->db->prepare(
"SELECT
mf.id,
mf.title,
mf.file_name as fileName,
mf.created_at as createdAt,
mf.updated_at as updatedAt,
mf.is_private as isPrivate,
mf.grouping_id as groupingId,
g.grouping as groupingName
FROM markdown_file mf
LEFT JOIN `grouping` g ON mf.grouping_id = g.id
WHERE mf.grouping_id = ? AND mf.is_deleted = 0
ORDER BY mf.updated_at DESC"
);
$stmt->execute([$groupingId]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* 搜索笔记,包含分组名称
* @param $keyword 搜索关键词
* @return array
*/
public function searchByTitleWithGrouping($keyword)
{
$stmt = $this->db->prepare(
"SELECT
mf.id,
mf.title,
mf.file_name as fileName,
mf.created_at as createdAt,
mf.updated_at as updatedAt,
mf.is_private as isPrivate,
mf.grouping_id as groupingId,
g.grouping as groupingName
FROM markdown_file mf
LEFT JOIN `grouping` g ON mf.grouping_id = g.id
WHERE mf.title LIKE ? AND mf.is_deleted = 0
ORDER BY mf.updated_at DESC"
);
$stmt->execute(['%' . $keyword . '%']);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* 获取最近的笔记,包含分组名称
* @param int $limit 限制数量
* @return array
*/
public function getRecentWithGrouping($limit = 12)
{
$stmt = $this->db->prepare(
"SELECT
mf.id,
mf.title,
mf.file_name as fileName,
mf.created_at as createdAt,
mf.updated_at as updatedAt,
mf.is_private as isPrivate,
mf.grouping_id as groupingId,
g.grouping as groupingName
FROM markdown_file mf
LEFT JOIN `grouping` g ON mf.grouping_id = g.id
WHERE mf.is_deleted = 0
ORDER BY mf.updated_at DESC
LIMIT ?"
);
$stmt->execute([$limit]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}

View File

@@ -5,10 +5,28 @@ namespace App\Models;
use App\Utils\Database; use App\Utils\Database;
use PDO; use PDO;
/**
* RegistrationCode - 注册码模型
*
* 数据库表: registration_codes
* 主要字段:
* - id (Long) - 主键ID自增
* - code (String) - 注册码
* - expiry_time (DateTime) - 过期时间
* - created_by (String) - 创建者
* - created_at (DateTime) - 创建时间
*/
class RegistrationCode class RegistrationCode
{ {
private $db; private $db;
// 注册码属性
public $id;
public $code;
public $expiryTime;
public $createdBy;
public $createdAt;
public function __construct() public function __construct()
{ {
$this->db = Database::getInstance()->getConnection(); $this->db = Database::getInstance()->getConnection();
@@ -27,6 +45,38 @@ class RegistrationCode
return $code; return $code;
} }
public function generateCodeWithId($createdBy)
{
$code = bin2hex(random_bytes(16));
$expiryTime = date('Y-m-d H:i:s', strtotime('+7 days'));
$stmt = $this->db->prepare(
"INSERT INTO registration_codes (code, expiry_time, created_by, created_at) VALUES (?, ?, ?, NOW())"
);
$stmt->execute([$code, $expiryTime, $createdBy]);
// 返回包含 id 的完整记录
return $this->getCodeByCode($code);
}
public function getCodeByCode($code)
{
$stmt = $this->db->prepare(
"SELECT id, code, expiry_time, created_by, created_at FROM registration_codes WHERE code = ?"
);
$stmt->execute([$code]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
public function getCodeById($id)
{
$stmt = $this->db->prepare(
"SELECT id, code, expiry_time, created_by, created_at FROM registration_codes WHERE id = ?"
);
$stmt->execute([$id]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
public function validateCode($code) public function validateCode($code)
{ {
$stmt = $this->db->prepare( $stmt = $this->db->prepare(
@@ -35,4 +85,13 @@ class RegistrationCode
$stmt->execute([$code]); $stmt->execute([$code]);
return $stmt->fetch() !== false; return $stmt->fetch() !== false;
} }
public function validateCodeWithDetails($code)
{
$stmt = $this->db->prepare(
"SELECT id, code, expiry_time, created_by, created_at FROM registration_codes WHERE code = ? AND expiry_time > NOW()"
);
$stmt->execute([$code]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
} }

View File

@@ -5,10 +5,24 @@ namespace App\Models;
use App\Utils\Database; use App\Utils\Database;
use PDO; use PDO;
/**
* SystemSetting - 系统设置模型
*
* 数据库表: system_settings
* 主要字段:
* - setting_key (String) - 设置键(主键)
* - setting_value (String) - 设置值
* - description (String) - 设置描述
*/
class SystemSetting class SystemSetting
{ {
private $db; private $db;
// 系统设置属性
public $settingKey;
public $settingValue;
public $description;
public function __construct() public function __construct()
{ {
$this->db = Database::getInstance()->getConnection(); $this->db = Database::getInstance()->getConnection();
@@ -31,6 +45,22 @@ class SystemSetting
return $stmt->execute([$key, $value, $value]); return $stmt->execute([$key, $value, $value]);
} }
public function getSettingWithDescription($key)
{
$stmt = $this->db->prepare("SELECT setting_value, description FROM system_settings WHERE setting_key = ?");
$stmt->execute([$key]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
public function setSettingWithDescription($key, $value, $description = null)
{
$stmt = $this->db->prepare(
"INSERT INTO system_settings (setting_key, setting_value, description) VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE setting_value = ?, description = ?"
);
return $stmt->execute([$key, $value, $description, $value, $description]);
}
public function isRegistrationEnabled() public function isRegistrationEnabled()
{ {
$value = $this->getSetting('registration_enabled'); $value = $this->getSetting('registration_enabled');

View File

@@ -4,6 +4,10 @@ namespace App\Models;
use App\Utils\Database; use App\Utils\Database;
/**
* Trash - 回收站模型
* 用于管理已删除的笔记和分组
*/
class Trash class Trash
{ {
private $db; private $db;

View File

@@ -0,0 +1,83 @@
<?php
namespace App\Models;
/**
* TrashItemVo - 回收站项目视图对象
* 用于返回回收站中的笔记或分组信息
*/
class TrashItemVo
{
/**
* @var string 项目ID
*/
public $id;
/**
* @var string 项目名称(笔记标题或分组名称)
*/
public $title;
/**
* @var string 项目类型note 或 group
*/
public $type;
/**
* @var string 删除时间格式yyyy-MM-dd HH:mm:ss
*/
public $deletedAt;
/**
* @var string 删除者ID
*/
public $deletedBy;
/**
* 构造函数
* @param string $id 项目ID
* @param string $title 项目名称
* @param string $type 项目类型note 或 group
* @param string $deletedAt 删除时间
* @param string $deletedBy 删除者ID
*/
public function __construct($id = '', $title = '', $type = '', $deletedAt = '', $deletedBy = '')
{
$this->id = $id;
$this->title = $title;
$this->type = $type;
$this->deletedAt = $deletedAt;
$this->deletedBy = $deletedBy;
}
/**
* 将对象转换为数组
* @return array
*/
public function toArray()
{
return [
'id' => $this->id,
'title' => $this->title,
'type' => $this->type,
'deletedAt' => $this->deletedAt,
'deletedBy' => $this->deletedBy,
];
}
/**
* 从数组创建对象
* @param array $data
* @return TrashItemVo
*/
public static function fromArray($data)
{
return new self(
$data['id'] ?? '',
$data['title'] ?? '',
$data['type'] ?? '',
$data['deletedAt'] ?? '',
$data['deletedBy'] ?? ''
);
}
}

View File

@@ -5,10 +5,34 @@ namespace App\Models;
use App\Utils\Database; use App\Utils\Database;
use PDO; use PDO;
/**
* User - 用户模型
*
* 数据库表: user
* 主要字段:
* - id (Long) - 用户ID自增主键
* - username (String) - 用户名
* - password (String) - 密码(加密存储)
* - email (String) - 邮箱
* - created_at (DateTime) - 创建时间
* - updated_at (DateTime) - 更新时间
* - token (String) - 用户token
* - token_enddata (DateTime) - token过期时间
*/
class User class User
{ {
private $db; private $db;
// 用户属性
public $id;
public $username;
public $password;
public $email;
public $createdAt;
public $updatedAt;
public $token;
public $tokenEnddata;
public function __construct() public function __construct()
{ {
$this->db = Database::getInstance()->getConnection(); $this->db = Database::getInstance()->getConnection();