Ever Gauzy 商品评论插件(@gauzy/plugin-product-reviews)完整指南:从安装构建到源码级原理
Ever Gauzy 商品评论插件(@gauzy/plugin-product-reviews)完整指南:从安装构建到源码级原理
本篇技术指南围绕 Ever® Gauzy™ 开源业务管理平台(ERP/CRM/HRM/ATS/PM)中的商品评论插件 @gauzy/plugin-product-reviews 展开,完整覆盖该插件的构建、单元测试、发布与安装流程,并结合仓库源码深入剖析其 ProductReview 实体结构、审核状态机、多 ORM 仓储实现与 GraphQL 扩展原理。读完本文,你将掌握如何在 Gauzy 平台中集成商品评价能力、理解其数据模型与生命周期钩子,并能独立完成该插件库的构建与发布。
插件定位:为 Gauzy 平台注入商品评价能力
@gauzy/plugin-product-reviews 是 Ever Gauzy 插件体系(位于 packages/plugins/product-reviews)中负责商品评论/评价管理的独立库。从该库的 package.json 可见其官方描述为 "Gauzy Product Reviews Plugin - Integration for managing and displaying product reviews in the Gauzy Platform",即它是平台中管理商品评论的集成模块。
在 Gauzy 的插件机制中,该插件被注册进 API 服务端。从 apps/api/src/plugins.ts 可以看到:
import { ProductReviewsPlugin } from '@gauzy/plugin-product-reviews';
// 插件列表中注册
ProductReviewsPlugin,
这表明它作为平台可插拔能力的一部分,随 API 服务启动时被加载。其 project.json 中声明了 "tags": ["type:plugin"],与其它 Gauzy 插件保持一致。
快速开始:构建、测试、发布与安装
本插件的 README.md 给出了完整的工作流。以下所有命令均需在仓库根目录执行(项目使用 Nx 作为构建编排工具)。
构建插件库
yarn nx build plugin-product-reviews
该命令通过 Nx 执行编译,产出目录为 dist/packages/plugins/product-reviews。从 project.json 可以看到构建目标的具体配置:
- 执行器:
@nx/js:tsc(基于 TypeScript 编译器) - 输出路径:
dist/packages/plugins/product-reviews - 入口文件:
packages/plugins/product-reviews/src/index.ts - 编译配置:
packages/plugins/product-reviews/tsconfig.lib.json - 资源复制:
packages/plugins/product-reviews/*.md(README 等文档会随包一起产出) - 依赖约束:
"dependsOn": ["^build"],即会先构建其依赖的contracts、core、plugin等隐式依赖库(见implicitDependencies)
package.json 中还封装了三个便捷脚本:
"scripts": {
"lib:build": "yarn nx build plugin-product-reviews",
"lib:build:prod": "yarn nx build plugin-product-reviews",
"lib:watch": "yarn nx build plugin-product-reviews --watch"
}
--watch 模式适用于开发迭代,改动源码后自动增量编译。
运行单元测试
yarn run test plugin-product-reviews
该命令通过 Nx 的 Jest 执行器(@nx/jest:jest)运行测试,配置指向 packages/plugins/product-reviews/jest.config.ts,覆盖率输出到 coverage/packages/plugins/product-reviews。项目根目录的 jest.config.ts 与 jest.preset.js 提供了统一的 Jest 预设。
发布到 npm
构建完成后,进入产物目录并发布:
cd dist/packages/plugins/product-reviews
npm publish
project.json 中为此配置了 nx-release-publish 目标(packageRoot 指向 dist/{projectRoot}),说明仓库支持通过 Nx 的发布流程管理版本。值得注意的是,当前 package.json 中 "private": true 且版本为 0.1.0,表示该插件目前主要在仓库内部/工作区消费,实际对外发布前需移除 private 标记并按需调整版本号。
安装插件
在需要使用该插件的项目中,用你偏好的包管理器安装:
npm install @gauzy/plugin-product-reviews
# or
yarn add @gauzy/plugin-product-reviews
由于该插件是平台的扩展模块,安装后还需在应用的模块配置中将其引入(如 apps/api/src/plugins.ts 所示),使其实体与生命周期钩子生效。
插件核心实现:装饰器、生命周期与导出面
插件类与声明式配置
插件主体的实现位于 src/lib/product-reviews.plugin.ts,源码如下(关键部分):
import * as chalk from 'chalk';
import { GauzyCorePlugin as Plugin, IOnPluginBootstrap, IOnPluginDestroy } from '@gauzy/plugin';
import { ProductReview } from './entities/product-review.entity';
import { schemaExtensions } from './graphql/schema-extensions';
@Plugin({
imports: [],
entities: [ProductReview],
extensions: {
schema: schemaExtensions,
resolvers: []
}
})
export class ProductReviewsPlugin implements IOnPluginBootstrap, IOnPluginDestroy {
// We disable by default additional logging for each event to avoid cluttering the logs
private logEnabled = true;
onPluginBootstrap(): void | Promise<void> {
if (this.logEnabled) {
console.log(chalk.green(`${ProductReviewsPlugin.name} is being bootstrapped...`));
console.log('ReviewsPlugin is being bootstrapped...');
}
}
onPluginDestroy(): void | Promise<void> {
if (this.logEnabled) {
console.log(chalk.red(`${ProductReviewsPlugin.name} is being destroyed...`));
}
}
}
可以拆解出三层关键信息:
-
@Plugin装饰器:来自@gauzy/plugin包,负责把类声明为 Gauzy 平台的插件。声明内容包含:imports: []:可导入其它模块(当前为空);entities: [ProductReview]:把ProductReview实体注册进平台的数据层,使其被 ORM 自动建表/同步;extensions.schema:注入 GraphQL schema 扩展;extensions.resolvers:预留的解析器数组(当前为空,说明 GraphQL 数据操作主要依赖平台默认 CRUD 机制)。
-
生命周期钩子:实现了
IOnPluginBootstrap(插件初始化时触发)与IOnPluginDestroy(插件销毁时触发)。代码使用chalk对日志着色(绿色表示启动、红色表示销毁),并注释说明默认启用日志但可通过logEnabled关闭,以避免刷屏。 -
命名细节:启动日志同时打印了类名与
'ReviewsPlugin'字样,这与后续实体命名统一为ProductReview略有出入,属于代码中保留的历史命名痕迹。
公共 API 导出面
src/index.ts 定义了库的公共 API 表面:
export * from './lib/product-reviews.plugin';
export * from './lib/entities';
export * from './lib/graphql/schema-extensions';
即对外暴露:插件类、实体(经由 entities/index.ts 再导出 ProductReview)、以及 GraphQL schema 扩展。
数据模型:ProductReview 实体的完整字段
商品评论的数据结构定义在 src/lib/entities/product-review.entity.ts。该实体继承自 Gauzy 核心的 TenantOrganizationBaseEntity(因此天然携带租户与组织维度,适合多租户 SaaS 场景),并标注了 @MultiORMEntity('product_review', ...),表名为 product_review。
核心字段与校验规则
| 字段 | 类型 | 约束/校验 | 说明 |
|---|---|---|---|
title |
string |
可选(@IsOptional),可空 |
评论标题 |
description |
string |
可选,text 类型可空 |
评论正文 |
rating |
number |
必填,0 ≤ rating ≤ 10 |
评分(@Min(0) / @Max(10)) |
upvotes |
int |
默认 0,带索引 |
赞成票数 |
downvotes |
int |
默认 0,带索引 |
反对票数 |
status |
varchar |
枚举校验,默认 pending |
审核状态 |
editedAt |
timestamp/text |
可空,带索引 | 最近编辑时间 |
isEdited |
boolean |
虚拟列 | 是否被编辑过(由 editedAt 派生) |
逐项说明:
rating:通过class-validator的@IsNotEmpty()、@IsNumber()、@Min(0)、@Max(10)强约束,Swagger 文档(@ApiProperty)中同样标注minimum: 0, maximum: 10,评分体系为 0–10 分制。upvotes/downvotes:均为int类型、默认值 0,且都带有@ColumnIndex()索引——若后续需要按票数排序或筛选,数据库层面已有索引支撑。status:默认值为ProductReviewStatusEnum.PENDING,即新评论默认进入"待审核"状态。editedAt与isEdited:editedAt是真实存储列;isEdited是@VirtualMultiOrmColumn()虚拟列,不会落库,而是由 ORM 在读取时派生(判断editedAt是否存在)。editedAt的类型兼容:源码中@MultiORMColumn({ type: isBetterSqlite3() ? 'text' : 'timestamp' })依据@gauzy/config的isBetterSqlite3()运行时判断数据库类型——使用 better-sqlite3 时存为text,否则存为timestamp,这是 Gauzy 多数据库支持(TypeORM + MikroORM)的典型写法。
关联关系
实体与产品、用户建立了多对一关联,且删除时级联(onDelete: 'CASCADE'):
product?/productId?:被评论的Product实体。@MultiORMManyToOne(() => Product)+@JoinColumn(),productId为@IsUUID()校验的关联 ID 列(@RelationId+relationId: true),带索引。user?/userId?:发表评论的User实体,同样级联删除并带@ColumnIndex()的userId关联列。
这种设计意味着:删除产品或其所属用户时,相关评论会一并删除;同时通过 productId / userId 索引可以高效地按产品或用户查询评论集合。
审核状态机与类型契约
评论状态定义在 src/lib/product-review.types.ts:
export type ProductReviewStatus = 'approved' | 'pending' | 'rejected';
export enum ProductReviewStatusEnum {
APPROVED = 'approved',
PENDING = 'pending',
REJECTED = 'rejected'
}
export interface IProductReview extends IBasePerTenantAndOrganizationEntityModel {
title: string;
description: string;
rating: number;
upvotes: number;
downvotes: number;
status: ProductReviewStatus;
product?: IProduct;
productId?: ID;
user?: IUser;
userId?: ID;
}
三态审核流程:新评论创建时默认 pending,经平台管理操作可流转为 approved(对外展示)或 rejected(隐藏/拒绝)。IProductReview 接口继承 IBasePerTenantAndOrganizationEntityModel,保证了评论天然具备租户与组织归属。从源码结构看,isEdited 与 editedAt 字段在接口中未显式声明,属于实体层面的派生/附加字段。
多 ORM 仓储实现
Gauzy 平台同时支持 TypeORM 与 MikroORM,本插件为两种 ORM 各提供了仓储实现(位于 src/lib/entities/repository):
- mikro-orm-product-review.repository.ts:继承
MikroOrmBaseEntityRepository<ProductReview>,空实现即获得基础 CRUD 能力,并在实体上通过@MultiORMEntity(..., { mikroOrmRepository: () => MikroOrmProductReviewRepository })绑定。 - type-orm-product-review.repository.ts:
@Injectable()的 NestJS 服务类,继承 TypeORM 的Repository<ProductReview>,构造器通过@InjectRepository(ProductReview)注入底层仓储并透传给父类。
这种"一套实体 + 双仓储"的结构,让插件无论运行在哪种 ORM 模式下都能获得一致的数据访问能力,是 Gauzy 跨数据库抽象(MultiORM* 系列装饰器)在业务插件中的直接体现。
GraphQL Schema 扩展
插件通过 src/lib/graphql/schema-extensions.ts 向平台的 GraphQL API 注入类型定义:
import { gql } from 'graphql-tag';
export const schemaExtensions = gql`
type ProductReview {
id: ID!
body: String
rating: Float!
}
`;
它声明了 ProductReview GraphQL 类型(id、body、rating),并通过 @Plugin 的 extensions.schema 合并进平台 schema。注意该 GraphQL 类型与 ORM 实体字段并不完全一一对应(例如 GraphQL 层使用 body 而非 description,且未暴露状态字段),从源码结构看这更像一个最小化的对外契约骨架;由于 resolvers 目前为空数组,完整的数据操作仍依赖平台默认的查询/变更机制。若要在实际业务中公开更多字段(如 status、upvotes、productId),需要同步扩展此处的 schema 定义。
依赖与运行环境
从 package.json 可确认以下工程约束:
- Node/Yarn 版本要求:
engines声明node >= 22、yarn >= 1.22,构建与运行前需保证环境满足。 - 运行时依赖:
@gauzy/contracts、@gauzy/core、@gauzy/plugin(平台核心)、@nestjs/common/@nestjs/core(peerDependencies,^11.1.26)、@nestjs/swagger、@nestjs/typeorm、typeorm、class-validator、graphql-tag、chalk、tslib。 - 许可与归属:AGPL-3.0 许可,作者为 Ever Co. LTD(ever@ever.co)。
- 关键词:
gauzy、product-reviews、plugin、nestjs、typescript、reviews、feedback、e-commerce——即面向电商场景的评价/反馈能力。
总结:从接入到扩展的完整路径
围绕 @gauzy/plugin-product-reviews,可以梳理出一条清晰的实战路径:
- 接入:在 Gauzy 应用(如 apps/api/src/plugins.ts)中导入并注册
ProductReviewsPlugin,其entities: [ProductReview]会让product_review表随平台同步创建; - 使用:通过多 ORM 仓储(TypeORM/MikroORM)执行评论 CRUD,遵循
rating0–10、status三态(pending/approved/rejected)等校验与状态约定; - 构建发布:
yarn nx build plugin-product-reviews产出dist/packages/plugins/product-reviews,再经npm publish发布;也可通过yarn add @gauzy/plugin-product-reviews直接安装使用; - 扩展:如需对外暴露更多评论字段,可在 schema-extensions.ts 中扩展 GraphQL 类型,并在插件
resolvers中补充解析逻辑。
该插件体现了 Gauzy 插件体系的典型范式:声明式 @Plugin 元数据 + 生命周期钩子 + 多 ORM 实体 + GraphQL schema 扩展,为在 ERP/电商业务中快速叠加商品评价模块提供了可复制的工程样板。