# SixShop NEWS **Repository Path**: sixdec-1/news ## Basic Information - **Project Name**: SixShop NEWS - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-08-29 - **Last Updated**: 2025-09-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 资讯模块 (News) - FFI 实现 ## 模块功能 本模块为 SixShop 系统提供资讯管理功能。其核心业务逻辑(数据库 CRUD)完全由 Go 语言实现,并通过 PHP FFI 进行调用。这种方式可以有效保护核心业务逻辑代码,同时利用 Go 的高性能特性。 ## 主要特性 - **后台管理**: - 资讯分类管理 (增删改查) - 资讯文章管理 (增删改查) - **API 接口**: - 获取资讯分类列表 - 获取指定分类下的文章列表(支持分页) - 获取文章详情 - **Go FFI 实现**: - 所有数据库操作均在编译好的 Go 共享库 (`.so`) 中执行。 - PHP 服务层仅作为调用 FFI 接口的桥梁。 ## 目录结构 ``` backend/extension/news/ ├── config/ │ ├── install.sql # 数据库表结构 │ └── uninstall.sql # 卸载表结构 ├── ffi/ │ ├── main.go # Go FFI 业务逻辑和导出函数 │ ├── go.mod # Go 依赖管理 │ ├── Makefile # 一键构建 Go 共享库的脚本 │ ├── model/ │ │ ├── category.go # 资讯分类 GORM 模型 │ │ └── news.go # 资讯文章 GORM 模型 │ ├── lib_news.so # [构建产物] Go 编译的共享库 │ └── lib_news.h # [构建产物] Go 编译的头文件 ├── route/ │ ├── admin.php # 后台路由 │ └── api.php # 前端API路由 ├── src/ │ ├── Controller/ │ ├── Model/ # ThinkPHP 模型 (仅用于框架集成, 不含业务逻辑) │ ├── Service/ │ │ ├── NewsCategoryService.php # 分类服务的 FFI 调用封装 │ │ ├── NewsService.php # 资讯服务的 FFI 调用封装 │ │ └── NewsFfiService.php # [核心] FFI 单例服务, 负责加载和调用 .so 文件 │ └── Hook/ ├── news.info.yml # 模块信息 └── README.md # 模块说明文档 ``` ## 数据库安装 - 在插件安装时,需执行 `config/install.sql` 文件,以创建 `cy_news_category` 和 `cy_news` 两张表。 ## 核心依赖 - Go 1.20+ - Docker (用于执行 `Makefile` 中的构建命令) - PHP 7.4+ 并已启用 FFI 扩展 ## 构建 Go 共享库 所有 Go 源代码的编译都通过 `Makefile` 完成。 ```bash # 进入 ffi 目录 cd backend/extension/news/ffi # 执行构建 make build ``` 该命令会使用 Docker 容器来编译 Go 代码,确保构建环境的一致性。成功后,会在当前目录下生成 `lib_news.so` 和 `lib_news.h` 两个文件。 ## PHP FFI 调用流程 1. `NewsFfiService` 通过单例模式加载 `lib_news.so` 文件,并根据 `lib_news.h` 的内容定义 C 函数签名。 2. `NewsService` 和 `NewsCategoryService` 在实例化时会获取 `NewsFfiService` 的单例。 3. 当控制器调用 `NewsService` 的方法时(如 `getList`),`NewsService` 会将请求参数(如查询条件、分页信息)打包成 JSON 字符串。 4. `NewsService` 调用 `NewsFfiService` 的相应方法,将 JSON 字符串作为参数传递给 Go 函数。 5. Go 函数接收参数,执行数据库操作,并将结果打包成 JSON 字符串返回。 6. `NewsFfiService` 接收返回的 JSON 字符串,并将其解码为 PHP 数组,最终返回给控制器。 ### FFI 接口清单 (Go -> PHP) 所有业务逻辑都已封装在以下 Go 函数中,并通过 FFI 导出: - `GetCategoryList(char* whereJson)` - `GetCategoryByID(int id)` - `CreateCategory(char* dataJson)` - `UpdateCategory(int id, char* dataJson)` - `DeleteCategory(int id)` - `GetNewsList(char* paramsJson)` - `GetNewsByID(int id)` - `CreateNews(char* dataJson)` - `UpdateNews(int id, char* dataJson)` - `DeleteNews(int id)` ### 注意事项 - **路径依赖**: `NewsFfiService.php` 中硬编码了 `lib_news.so` 的相对路径。如果移动文件,需要同步更新。 - **重新编译**: 任何对 `ffi/` 目录下 `.go` 文件的修改,都必须重新执行 `make build` 才能生效。 - **错误处理**: Go FFI 函数中的错误会以包含 `error` 键的 JSON 字符串形式返回,PHP 服务层应进行相应的检查和处理。 ## 服务层依赖注入分析 在 `news` 模块的控制器中,存在两种不同的服务层依赖注入(或实例化)方式。这两种方式在设计上存在差异,体现了不同的架构思路。 ### 方式一:通过适配器实例化 (Admin 控制器) 此方式应用于 `src/Controller/Admin/` 目录下的所有控制器。 - **实现**: 控制器在其构造函数中,通过 `new` 关键字直接实例化一个**适配器** (`NewsServiceAdapter` 或 `NewsCategoryServiceAdapter`)。 ```php // 文件: backend/extension/news/src/Controller/Admin/NewsController.php use SixShop\News\Service\NewsServiceAdapter; class NewsController { protected $service; public function __construct() { // 直接实例化适配器 $this->service = new NewsServiceAdapter(); } // ... } ``` - **优点**: - **松耦合**: 控制器本身不关心底层的具体实现。所有的业务逻辑切换(例如,在开发模式下使用纯 PHP 服务,在生产模式下使用 FFI 服务)都封装在适配器内部。 - **符合依赖倒置原则**: 控制器依赖于一个稳定的"适配器"抽象,而不是一个多变的具体服务。 - **易于维护和扩展**: 如果未来需要增加新的服务实现,只需要修改适配器,控制器代码无需改动。 - **结论**: 这是项目推荐的、更健壮、更灵活的设计模式。 ### 方式二:通过构造函数依赖注入 (Api 控制器) 此方式应用于 `src/Controller/Api/` 目录下的控制器。 - **实现**: 控制器在其构造函数的参数中,声明需要注入的**具体服务** (`NewsService`),由框架的依赖注入容器自动实例化并传入。 ```php // 文件: backend/extension/news/src/Controller/Api/NewsController.php use SixShop\News\Service\NewsService; class NewsController { private $service; public function __construct(NewsService $service) { // 由框架注入具体的 NewsService 实例 $this->service = $service; } // ... } ``` - **缺点**: - **紧耦合**: 控制器与具体的 `NewsService` 类紧密绑定。 - **缺乏灵活性**: 它无法利用适配器模式带来的好处。如果想为 API 控制器也实现开发/生产模式的切换,就必须重构其构造函数和依赖关系。 - **设计不一致**: 与 Admin 控制器的实现方式不统一,增加了项目的维护成本和认知负担。 - **改进建议**: 为了保持整个模块架构的一致性,建议未来将 Api 控制器的实现方式重构为方式一(使用适配器)。 ## API 接口说明 本插件提供两组符合 RESTful 规范的资源路由,分别用于后台管理和前端调用。 ### 后台管理 API 路由前缀: `/admin/extension/news` 所有后台接口均会通过 `Auth` 中间件进行权限验证。 #### 1. 资讯分类管理 (`/category`) - **获取分类列表**: `GET /category` - 说明: 获取所有资讯分类,支持 `name` 字段模糊查询。 - 示例: `GET /admin/extension/news/category?name=技术` - **获取单个分类**: `GET /category/{id}` - 说明: 获取指定 ID 的分类详情。 - **新增分类**: `POST /category` - 说明: 创建一个新的资讯分类。 - 请求体 (JSON): `{ "name": "新分类", "sort": 100, "status": 1 }` - **更新分类**: `PUT /category/{id}` - 说明: 更新指定 ID 的分类信息。 - 请求体 (JSON): `{ "name": "更新后的分类", "sort": 99 }` - **删除分类**: `DELETE /category/{id}` - 说明: 删除指定 ID 的分类。 #### 2. 资讯文章管理 (`/news`) - **获取文章列表**: `GET /news` - 说明: 获取所有资讯文章,支持分页,支持按 `title` 和 `category_id` 查询。 - 示例: `GET /admin/extension/news/news?title=六店&category_id=1&page=1&limit=10` - **获取单篇文章**: `GET /news/{id}` - 说明: 获取指定 ID 的文章详情。 - **新增文章**: `POST /news` - 说明: 创建一篇新的资讯文章。 - 请求体 (JSON): `{ "category_id": 1, "title": "文章标题", "content": "文章内容...", "status": 1 }` - **更新文章**: `PUT /news/{id}` - 说明: 更新指定 ID 的文章信息。 - 请求体 (JSON): `{ "title": "更新后的标题" }` - **删除文章**: `DELETE /news/{id}` - 说明: 删除指定 ID 的文章。 ### 移动端 API 路由前缀: `/api/extension/news` #### 1. 资讯分类接口 (`/category`) - **获取分类列表**: `GET /category` - 说明: 获取所有已启用的资讯分类列表。 #### 2. 资讯文章接口 (`/news`) - **获取文章列表**: `GET /news` - 说明: 获取所有已发布的资讯文章,支持按 `category_id` 筛选和分页。 - 示例: `GET /api/extension/news/news?category_id=1&page=1&limit=10` - **获取文章详情**: `GET /news/{id}` - 说明: 获取一篇已发布的文章详情。 ## 数据库安装与卸载 - **安装表结构**: - 执行 `config/install.sql`,自动创建资讯分类表(cy_news_category)和资讯文章表(cy_news)。 - **卸载表结构**: - 执行 `config/uninstall.sql`,自动删除本模块相关表结构。 > 推荐在模块安装、升级、卸载时自动调用对应 SQL,便于独立管理和维护。 ## FFI 动态库能力说明 本模块内置 Go FFI 动态库(`ffi/` 目录),用于高性能、可闭源的资讯业务处理,PHP 可通过 FFI 调用。 ### 依赖安装 - 需 Go 1.20+ - 依赖 GORM、yaml.v2 - 安装依赖: ```bash cd ffi go mod tidy ``` ### 数据库配置 - 配置文件:`config/db.yaml` - 支持 MySQL,参数与 PHP 项目一致 ### 构建动态库 - 推荐用 Makefile 一键构建: ```bash cd ffi make build # 生成 lib_news.so 和 lib_news.h ``` - 也可用 Dockerfile 构建 ### 主要接口 - `GetList()` 获取资讯列表(JSON) - `GetById(id)` 获取单条资讯详情(JSON) - `Create(json)` 新增资讯,参数为 JSON - `Update(id, json)` 编辑资讯 - `Delete(id)` 删除资讯 - `GenerateSummary(content)` 生成摘要 - `CheckContent(content)` 内容审核 - `ExtractKeywords(content)` 关键词提取 ### PHP FFI 调用示例 ```php $ffi = FFI::cdef(file_get_contents('lib_news.h'), 'lib_news.so'); $list = json_decode(FFI::string($ffi->GetList()), true); $info = json_decode(FFI::string($ffi->GetById(1)), true); $id = $ffi->Create(json_encode($data)); $ok = $ffi->Update($id, json_encode($data)); $ok = $ffi->Delete($id); $summary = FFI::string($ffi->GenerateSummary($content)); $isIllegal = $ffi->CheckContent($content); $keywords = explode(',', FFI::string($ffi->ExtractKeywords($content))); ``` ### 注意事项 - 动态库需与 PHP FFI 路径一致 - 数据库表结构需提前安装(见 config/install.sql) - 推荐配合 PHP Service 层切换调用 ## 插件编译与分发 ### 一键编译与分发 - 推荐使用 shell 脚本(需 bash 环境): ```bash cd backend/extension/news ./build.sh ``` - 或使用 PHP 脚本(适合 PHP-only 环境): ```bash cd backend/extension/news php build.php ``` #### 编译/分发脚本高级特性 - **增量编译**:自动检测 ffi/ 下所有 Go 源码变更,无变更则跳过 make build,极大提升效率。 - **编译日志**:所有编译和打包输出写入 `build/build.log`,便于排查问题。 - **自动打包**:编译完成后自动将 `build/` 目录内容打包为 `build/package.zip`,方便分发和备份。 - **产物一致**:无论用 shell 还是 PHP 脚本,体验和产物完全一致。 ### 编译产物 - `build/lib_news.so`、`build/lib_news.h`:Go FFI 动态库及头文件 - `build/NewsServiceFFI.php`:PHP FFI 适配层,控制器/接口直接调用 - `build/package.zip`:自动打包的分发包 - `build/build.log`:编译和打包日志 ### 开发与分发模式 - **开发模式**:保留全部源码,便于调试和测试 - **分发模式**:只保留 build 目录和必要接口,核心业务完全闭源 ### 控制器调用 FFI 适配层示例 ```php require_once __DIR__ . '/../build/NewsServiceFFI.php'; $service = new NewsService(); $list = $service->getList(); ``` ## 服务适配层(ServiceAdapter)设计说明 为实现开发模式(PHP Service)与分发模式(FFI 适配层)的无缝切换,所有控制器均依赖适配层(如 NewsServiceAdapter、NewsCategoryServiceAdapter),而不直接依赖具体 Service 实现。 - **开发模式**:适配层自动加载并调用 PHP Service,便于本地开发和调试。 - **分发模式**:适配层自动加载并调用 FFI 适配层(build/NewsServiceFFI.php),核心逻辑闭源,安全分发。 - 控制器层代码无需变动,适配层自动切换底层实现。 ### 控制器用法示例 ```php $service = new NewsServiceAdapter(); $list = $service->getList(); ``` 适配层代码位于 `src/Service/NewsServiceAdapter.php`、`src/Service/NewsCategoryServiceAdapter.php`,可根据实际业务扩展更多方法。