# 文章内容社区 API

面向长文阅读、作者关系和内容运营的 Laravel 分层单体 API。HTTP 与 Service 按 `Backend`（后台）、`Frontend`（前台）及具体功能组织；Model 与 DAO 按 Account、Access、Content、Taxonomy、Interaction、Reading 等领域组织。

产品范围见 [docs/PRODUCT_SCOPE.md](docs/PRODUCT_SCOPE.md)，架构约定见 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)。

## 技术基线

- PHP `^8.0.2`
- Laravel `9.52.21`
- `tymon/jwt-auth 2.3.0`
- PHPUnit 9.6
- SQLite（开发/测试），生产可切换 MySQL 或 PostgreSQL

> PHP 8.0 和 Laravel 9 均已结束官方安全维护。这是为兼容现有 PHP 8.0 环境作出的约束；准备生产上线时应优先升级 PHP，再升级 Laravel。Composer 已设置 `platform.php=8.0.30`，防止在高版本开发机上锁入 PHP 8.1+ 依赖。

## 已实现能力

- 推荐、最新、热门、关注文章流，以及关键词、分类、标签、作者筛选
- 文章详情、相关文章、阅读量去重、预计阅读时长
- JWT 注册、登录、刷新、注销和黑名单撤销
- 点赞、收藏、评论/回复、关注作者、作者推荐
- 阅读进度、阅读历史和继续阅读
- 后台内容查询、正文版本/恢复、投稿审核、专栏、分类、标签、封面上传、发布与下架
- 项目自维护 RBAC：角色、原子权限、临时角色、用户角色分配和维护接口
- 文章正文分表、历史 slug、媒体资产、每日指标、收藏夹、举报和操作审计表
- 前后台和功能模块独立日志
- Factory、Demo Seeder 和内存数据库功能测试

## 目录

```text
app
├── Dao
│   ├── Access
│   ├── Account
│   ├── Content
│   ├── Interaction
│   ├── Reading
│   └── Taxonomy
├── Exceptions
├── Http
│   ├── Controllers
│   │   ├── Backend/{Access,Article,Auth,Category,Column,Review,Tag,Upload}
│   │   └── Frontend/{Article,Auth,Category,Column,Feed,Interaction,Library,Tag,User}
│   ├── Requests
│   │   ├── Backend/<Feature>
│   │   └── Frontend/<Feature>
│   ├── Resources
│   │   ├── Backend/<Feature>
│   │   └── Frontend/<Feature>
│   └── Middleware
├── Models/{Access,Account,Content,Interaction,Reading,Taxonomy}
├── Services
│   ├── Backend/<Feature>
│   └── Frontend/<Feature>
└── Support

routes
├── api.php
├── backend.php
├── frontend.php
└── console.php

database
├── factories
├── migrations
└── seeders
```

没有 `V1` 目录和 `/api/v1` 前缀。需要公开第二版且产生破坏性变更时，再在路由边界引入版本，而不是提前增加空层级。

Controller 只处理 HTTP，Service 负责业务规则与事务编排，DAO 封装查询和持久化，Model 负责关系、Cast 和局部领域行为。每层最多保留两级业务目录，例如 `Services/Backend/Article`、`Dao/Content`、`Models/Content`，不再增加无业务价值的 `Concerns` 等空泛层级。

## 安装运行

```bash
composer install
cp .env.example .env
php artisan key:generate
php artisan jwt:secret
php artisan migrate:fresh --seed
php artisan storage:link
php artisan serve --host=127.0.0.1 --port=8000
```

默认 SQLite 数据库为 `database/database.sqlite`。执行 `migrate:fresh` 会清空所连接数据库，只应在本地开发环境使用。

Demo 账号密码均为 `password123`：

- `admin@example.com`：管理员
- `writer@example.com`：运营/作者
- `reviewer@example.com`：内容审核员
- `reader@example.com`：读者

健康检查：`GET /api/health`。

## 主要接口

前台：

```text
POST   /api/auth/register
POST   /api/auth/login
POST   /api/auth/refresh
POST   /api/auth/logout                     JWT
GET    /api/auth/me                         JWT
GET    /api/feed
GET    /api/feed/following                  JWT
GET    /api/articles/{article}
GET    /api/articles/{article}/comments
GET    /api/categories
GET    /api/tags/popular
GET    /api/columns
GET    /api/columns/{column}
GET    /api/authors/{user}
GET    /api/authors/recommended             JWT
POST   /api/articles/{article}/like         JWT
POST   /api/articles/{article}/bookmark     JWT
POST   /api/articles/{article}/comments     JWT
POST   /api/authors/{user}/follow           JWT
GET    /api/me/bookmarks                    JWT
GET    /api/me/reading-history              JWT
PUT    /api/me/reading-progress/{article}   JWT
```

后台统一以 `/api/admin` 开始，包括用户、角色、权限、内容治理、版本恢复、投稿审核、专栏、分类、标签和上传接口。路由使用 `Controller@method` 字符串，完整清单使用：

```bash
php artisan route:list --path=api
```

JWT 请求头：

```text
Authorization: Bearer <access_token>
Accept: application/json
```

## 响应资源与异常

业务实体使用 Laravel `JsonResource`。单条和列表统一保留 `data`，分页使用 Laravel 通用的顶层 `links`、`meta`；额外返回业务码、消息和追踪 ID：

```json
{
  "data": [],
  "links": {},
  "meta": {},
  "code": 10000,
  "message": "请求成功",
  "trace_id": "..."
}
```

校验、认证、授权、404、限流、业务异常和未知异常由 `app/Exceptions/Handler.php` 集中转换。客户端可传 `X-Request-Id`，响应会原样返回合法值；否则服务端生成 UUID。

## 日志

日志使用 Laravel `config/logging.php` 中的多个命名 channel，而不是多份配置文件。各 channel 按功能写入 `storage/logs`，包括 `frontend-auth`、`frontend-content`、`frontend-interaction`、`frontend-library`、`backend-auth`、`backend-access`、`backend-article`、`backend-review`、`backend-column`、`backend-taxonomy`、`backend-upload` 和 `exceptions`。保留天数通过 `LOG_DAYS` 配置。

## 测试

```bash
php artisan test
```

测试使用内存 SQLite，不会修改本地数据库。
