Ever Gauzy 商品评论插件(@gauzy/plugin-product-reviews)完整指南:从安装构建到源码级原理

原创2026-09-30 15:59:191,733 阅读
文章标签:企业应用后端前端桌面应用

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...`));
		}
	}
}

可以拆解出三层关键信息:

  1. @Plugin 装饰器:来自 @gauzy/plugin 包,负责把类声明为 Gauzy 平台的插件。声明内容包含:

    • imports: []:可导入其它模块(当前为空);
    • entities: [ProductReview]:把 ProductReview 实体注册进平台的数据层,使其被 ORM 自动建表/同步;
    • extensions.schema:注入 GraphQL schema 扩展;
    • extensions.resolvers:预留的解析器数组(当前为空,说明 GraphQL 数据操作主要依赖平台默认 CRUD 机制)。
  2. 生命周期钩子:实现了 IOnPluginBootstrap(插件初始化时触发)与 IOnPluginDestroy(插件销毁时触发)。代码使用 chalk 对日志着色(绿色表示启动、红色表示销毁),并注释说明默认启用日志但可通过 logEnabled 关闭,以避免刷屏。

  3. 命名细节:启动日志同时打印了类名与 '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,可以梳理出一条清晰的实战路径:

  1. 接入:在 Gauzy 应用(如 apps/api/src/plugins.ts)中导入并注册 ProductReviewsPlugin,其 entities: [ProductReview] 会让 product_review 表随平台同步创建;
  2. 使用:通过多 ORM 仓储(TypeORM/MikroORM)执行评论 CRUD,遵循 rating 0–10、status 三态(pending/approved/rejected)等校验与状态约定;
  3. 构建发布:yarn nx build plugin-product-reviews 产出 dist/packages/plugins/product-reviews,再经 npm publish 发布;也可通过 yarn add @gauzy/plugin-product-reviews 直接安装使用;
  4. 扩展:如需对外暴露更多评论字段,可在 schema-extensions.ts 中扩展 GraphQL 类型,并在插件 resolvers 中补充解析逻辑。

该插件体现了 Gauzy 插件体系的典型范式:声明式 @Plugin 元数据 + 生命周期钩子 + 多 ORM 实体 + GraphQL schema 扩展,为在 ERP/电商业务中快速叠加商品评价模块提供了可复制的工程样板。

登录后查看全文
ever-gauzy