ever-gauzy 实战指南:开源ERP部署、二次开发与项目管理全解析 很多团队选开源管理系统第一反应是 Odoo第二反应是 ERPNext却很少有人认真聊 ever-gauzy。我第一次把它从 GitHub 拉下来部署时心理预期只是找一个能自托管的项目看板但跑起来之后它同时给出的客户管理、报价、发票、员工考勤、薪资核算、招聘流程让我把原来散落在三个系统里的东西逐渐归到了一起。这篇文章就把我这一年多折腾 ever-gauzy 的经验完整写出来从选型理由、部署跑通、真实业务演练到二次开发和上线踩坑一次性讲透。如果你正在给小团队选管理后台或者单纯想把项目、客户、财务这几摊事用一个能改源码的系统管起来这篇文章应该能帮你少走不少弯路。1. ever-gauzy 是什么一套 TypeScript 全栈的开源企业管理平台1.1 基本定位与技术栈ever-gauzy 是 Ever 团队开源的企业管理平台产品名通常直接叫 Gauzy。它不是一个类似“收银系统”那样的单点工具而是把一家中小型公司日常运转需要的几个核心场景打包在一起销售线索、客户关系、项目管理、任务看板、工时记录、发票账单、费用报销、员工考勤、工资核算甚至招聘流程都在覆盖范围里。这套系统的技术栈对前端和后端工程师都比较友好。后端主体是 NestJS基于 TypeScript 写的ORM 层使用 TypeORM数据库默认是 PostgreSQL缓存和队列相关用 Redis前端是 Angular整个仓库以 monorepo 方式组织一次拉下来就能同时看到 API 代码和界面代码。对我来说最舒服的一点是前后端同一门语言改后端接口的时候不需要在脑内切换语言上下文。它的运行方式也符合“数据自己掌控”这个开源诉求。你可以完全离线部署在自己的服务器上数据库、文件存储、定时任务都在自己手里不像某些 SaaS 产品数据搬进去容易搬出来难。1.2 主要模块与能力边界我实际用下来ever-gauzy 的功能大概可以分成四块给你做个快速速览销售与 CRM管理客户、联系人、销售线索、商机阶段、报价单支持把报价单一键转成项目项目管理项目、任务、里程碑、看板视图、团队协作还有配套的时间跟踪能力会计与发票客户发票、供应商账单、费用记录、税率设置、收款状态跟踪以及基础的利润和收入报表人力与薪酬员工档案、考勤、请假审批、工资项配置能把工时数据汇总到工资计算里。这里要提醒一句它的会计模块属于“业务型财务”适合记录发票、费用、收入、应收应付但如果你需要完整的总账、资产负债表、多级科目这种专业财务能力还是建议把它跟前端数据同步到专业财务系统比如 Odoo 的会计模块或独立财务软件别指望一个开源项目解决所有财务合规问题。技术层面还有一个特点值得强调多租户设计。我们讲的“租户”在这里更像“空间”的概念一套部署可以给不同公司或不同事业部做数据隔离每个租户下可以有多个组织。这个设计对软件服务商来说非常友好你可以在一个实例上服务多个客户而每个客户看到的数据互相隔离。2. 为什么我在 Odoo、ERPNext、ever-gauzy 之间做了这个选择2.1 三款主流开源 ERP 平台的对比选型阶段我其实认真比较了三套系统Odoo 社区版、ERPNext还有 ever-gauzy。它们都能自托管都能做二次开发但针对“项目制交付团队”这个场景差异还挺明显。对比维度Odoo 社区版ERPNextever-gauzy技术栈Python / XML / 自研 ORMPython / Frappe 框架TypeScript / NestJS / Angular模块覆盖非常广但很多模块在社区版有限制广ERP 味道重聚焦中小团队的业务闭环二次开发门槛需要学 Odoo 的模块机制和视图语法需要学 Frappe 的 DocType 和 Jinja 模板熟悉 NestJS Angular 就能上手项目/工时/发票链路项目模块偏基础工时和发票要凑模块项目模块不错但界面定制不轻松这条链路是原生主推场景社区活跃度很高很高相对小但更新频率不低中文与本地化社区生态完善中文资料不少需要自己补充翻译和本地化我当时最纠结的其实是 Odoo。社区版功能确实强大但它的整体设计更偏向标准 ERP 流程项目管理和工时、发票之间的打通没有 ever-gauzy 那么顺。ERPNext 同样优秀可对我来说最大的问题是团队核心都是 TypeScript 背景如果硬上一个 Python 技术栈后续扩功能的成本会明显高出一截。2.2 从对比到落地哪种团队值得选它没有绝对最好的系统只有适不适合。我自己的判断标准是这样的如果你是做项目制交付的团队比如软件外包、设计公司、咨询公司核心诉求是“一个客户从线索到合同再到项目执行和开票收款整个过程尽量别换系统”那 ever-gauzy 的主链路非常贴合。原生就有线索、商机、报价单、项目、任务、工时、发票这一条完整路径不用自己开发模块去把断点接起来。但如果你是制造业或者有复杂进销存、生产工单、MRP 需求的团队我建议直接放弃 ever-gauzy老老实实看 Odoo 或 ERPNext它们在物料和生产领域比 ever-gauzy 成熟太多。如果你希望系统装好之后业务人员马上能用团队里却没有一个能看懂 TypeScript 的开发者我也会建议你再想想因为这类开源系统出现小问题时没人懂代码会非常被动。总结一句话ever-gauzy 是个“懂项目管理团队诉求”的选手适合有开发能力、业务以项目交付为主的中小团队不适合复杂制造的标准化大厂。3. 本地开发环境搭建从克隆仓库到跑通前后端3.1 准备工作与依赖搭建本地环境其实不复杂但有几个前置条件得先确认。我踩过最大的坑是 Node 版本不对导致依赖编译直接报错花了一个小时排查才发现是版本问题。建议直接用 Node.js 长期支持版本我当时用的是 18 LTS后续在 v20 上也跑过没有问题。需要提前装好的基础服务包括PostgreSQL版本建议 14 以上因为新版数据库的 JSON 和索引能力对平台这类复杂实体关系有好处Redis它的主要用途是缓存、队列和部分 session 支持Git这个不用多说Yarn仓库是 monorepo 结构使用 workspaces用 Yarn 装依赖最省事。这些都准备好之后把代码拉下来git clone https://github.com/ever-co/ever-gauzy.git cd ever-gauzy yarn install依赖安装过程可能比较久因为整个仓库的包数量很多。如果网络环境不好可以把 npm registry 切到国内镜像再装但装完之后记得留意 lock 文件有没有被动过不要随意提交上去。3.2 环境变量与数据库初始化这是整个部署流程里最容易出错的一步。仓库里通常会有环境变量示例文件比如.env.example或.env.compose你需要复制一份出来改成自己的配置cp .env.example .env我常用的最小配置大概是这样的具体变量名以你拉取到的仓库为准但核心逻辑是相通的# 数据库 DB_HOST127.0.0.1 DB_PORT5432 DB_NAMEgauzy DB_USERgauzy DB_PASSchange_me # Redis REDIS_HOST127.0.0.1 REDIS_PORT6379 # 服务 API_PORT3000 # JWT 签名密钥务必换成随机长字符串 JWT_SECRETplease_really_change_me配置好之后需要把数据库结构同步进去。ever-gauzy 的实体和迁移都放在后端代码里通常可以用迁移脚本执行类似yarn migration:run如果没有现成脚本也可以通过 TypeORM 的同步模式先跑起来但那只适合开发环境用生产环境千万别开自动同步否则改实体时可能误动表结构。3.3 启动 API 和前端后端和前端要分别启动。后端一般跑在 3000 端口前端开发服务器由 Angular CLI 管理默认通常跑在 4200。我在本地一般开两个终端窗口# 终端一API yarn start:api # 终端二前端 UI yarn start:gauzy两个服务都起来后访问前端地址第一次进入会要求初始化管理员账号。这里有一个非常容易被忽略的安全点初始管理员密码不要用示例里的默认值正式使用前一定要改成强密码因为这类系统一旦暴露到公网扫描器会在几分钟内尝试默认口令。首次登录成功你会看到左侧一堆菜单。别着急先熟悉角色切换。系统里面至少分管理员和员工两种主角色管理员能看到全组织的数据员工角色能看到的菜单和操作权限是受限的可以通过后台的角色权限配置动态调整。3.4 开发环境最值得注意的三个细节第一时区设置。这个系统涉及工时、考勤、发票日期如果服务器和本地时区不一致你会发现报表里的“今天”和你的理解对不上。建议在环境变量里显式配置时区不要依赖默认值。第二Redis 不是可有可无的装饰品。有些队列任务和缓存依赖它跳过 Redis 虽然系统能启动但某些异步任务会一直失败排查起来很费劲。第三别用 root 账号跑开发服务。我做演示时不管但日常开发建议建一个专用系统用户避免项目自动生成的上传文件权限乱掉。4. 用真实业务跑一遍全流程客户、项目、工时、发票4.1 客户与联系人管理理论上跑通环境只是开始真正让我认可这套系统的是它能把一条业务链路完整走下来。我用一个具体场景说明假设你做外包签了一个零售品牌客户合同金额 15 万交付周期 3 个月。第一步不是建项目而是在销售模块里把客户和联系人建好。客户资料里可以记录行业、规模、地址联系人可以保存姓名、职位、手机、邮箱。这里有个贴心设计联系人可以设置“是否公开”团队内部可见或者仅对特定角色可见。对服务型公司来说这能避免销售负责人离职后把客户联系方式一并带走的问题因为数据归公司系统所有。4.2 从报价到项目与任务拆解客户建好之后接下来是创建报价单。报价单里可以添加多个报价条目比如“需求调研”“UI 设计”“开发实施”“测试交付”每个条目有自己的数量和单价。税率也能配置生成报价单后可以发给客户确认。业务上最关键的转折点在这里报价单确认后可以直接转化成项目项目下再按功能模块拆分成多个里程碑里程碑里再建立任务任务支持看板视图。我经常把看板按“待办、开发中、测试中、已完成”四个列表组织每个任务可以指派负责人、设置截止日期、关联到里程碑还可以从任务里直接记录工时。这套链路好在哪里好在你不需要把客户信息在“销售系统”和“项目系统”里复制粘贴一遍系统里客户、报价、项目是关联的报表可以一直追溯到源头。4.3 时间跟踪与发票生成项目执行过程中成员在任务上记录耗费的工时。你可以手动在任务里填也可以用配套的时间跟踪方式记录起止时间。到月底或者里程碑完成时项目负责人把工时汇总给管理员管理员确认工时后就可以在发票模块创建客户发票。发票可以从项目一键带出工时和费用条目也可以手动添加服务项。我常用的做法是每个里程碑完成时开一张对应金额的发票收到客户打款后在系统里把发票状态改成“已支付”。这样一来应收账款一目了然不用再用 Excel 来回统计“到底还有多少钱没回”。4.4 收入、费用与基础报表在开票收款的同时把项目过程中产生的支出也录进系统比如外包人力成本、服务器费用、差旅报销。系统里有一个基本逻辑收入减去费用等于利润这是项目维度的利润核算。它在报表里会按组织、项目、客户等维度做汇总管理层看经营状况时不用等财务月底给 Excel。但这个模块的边界还是要说清楚它更适合“看一个项目赚不赚钱”这种管理层视角不适合做严格的财务凭证和税务申报。我的做法是ever-gauzy 承担运营层面的收入费用记录月底把汇总数据导出交给财务同事在专业软件里做合规账和报税。这样两边各司其职既不让业务人员天天去学会计科目也避免财务合规环节出问题。5. 二次开发实战加字段、加接口、加页面5.1 Monorepo 里如何找代码入口第一次接触这个仓库的人容易被目录结构吓到因为站在根目录往下看包非常多。但别慌结构上是清晰的API 相关的代码集中在后端应用目录下核心后端服务按业务模块分文件夹比如客户、项目、发票、员工等每个模块下通常会拆成 entity、dto、service、controller 这几个文件。前端应用放在另一个主要应用目录下Angular 的页面组件、服务和状态管理也按业务域分文件。我自己的经验是想改什么业务先在模块文件夹里找 controller看它暴露了哪些接口再顺着 service 找到数据操作逻辑最后去前端找对应的 service 文件一层一层跟踪下去。整个过程就是很标准的 NestJS Angular 开发模式没有额外需要学习的神秘框架。5.2 后端扩展示例实体、控制器与服务给你展示一个最典型的二次开发场景我希望给项目增加一个“是否属于战略客户项目”的标记并在接口里返回这个字段。在后端我先新增或扩展实体的字段定义类似这样import { Entity, Column, PrimaryGeneratedColumn } from typeorm; import { ApiProperty } from nestjs/swagger; Entity(project_strategy_flag) export class ProjectStrategyFlag { PrimaryGeneratedColumn(uuid) id: string; ApiProperty() Column({ length: 100 }) projectId: string; ApiProperty() Column({ default: false }) isStrategic: boolean; }然后写一个简单的 service负责转发数据库操作import { Injectable } from nestjs/common; import { InjectRepository } from nestjs/typeorm; import { Repository } from typeorm; import { ProjectStrategyFlag } from ./project-strategy-flag.entity; Injectable() export class ProjectStrategyFlagService { constructor( InjectRepository(ProjectStrategyFlag) private readonly repository: RepositoryProjectStrategyFlag, ) {} async findByProject(projectId: string): PromiseProjectStrategyFlag[] { return this.repository.find({ where: { projectId } }); } async save(payload: ProjectStrategyFlag): PromiseProjectStrategyFlag { return this.repository.save(payload); } }最后在 controller 里注册成 APIUseGuards(JwtAuthGuard) Controller(project-strategy-flags) export class ProjectStrategyFlagController { constructor(private readonly service: ProjectStrategyFlagService) {} Get(project/:projectId) async findByProject(Param(projectId) projectId: string) { return this.service.findByProject(projectId); } Post() async create(Body() body: ProjectStrategyFlag) { return this.service.save(body); } }这里我只是演示最小闭环真实项目里你还得考虑数据校验、事务、权限、租户过滤等细节。不要只在本地改完就完事了务必在 controller 上加上对应的权限装饰器否则等所有人能访问你的自定义接口时数据安全就成了大问题。5.3 前端对接与页面展示前端的对接也比较常规。先在 Angular 服务里写一个请求方法getStrategyFlags(projectId: string): ObservableProjectStrategyFlag[] { return this.http.getProjectStrategyFlag[]( /api/project-strategy-flags/project/${projectId} ); }然后在项目详情页或列表页调用这个服务把返回的字段渲染到表格里。Angular 开发体验整体还是顺畅的但要注意响应式数据的不可变更新习惯尤其是表单绑定时尽量把 API 返回的数据复制一份再修改避免因为对象引用问题引起页面不刷新。如果是更复杂的扩展比如要新增一个独立的一级菜单那还涉及路由注册和菜单权限配置。这个也不难但要在前端路由里注册页面同时去后端的权限配置里给对应角色开放菜单。很多时候你发现菜单没显示不是因为组件写错了而是当前登录用户没有这个菜单的权限。5.4 不要把权限和租户隔离忽略掉这一点我想单独拎出来说因为我看过太多二次开发只顾着“功能通了”的例子。ever-gauzy 的多租户机制决定了你在新增数据表时几乎都要考虑租户隔离这张表是否应该按租户隔离还是全局共享如果不加租户字段也不在查询里做过滤那么 A 公司的客户数据出现在 B 公司的界面上只是时间问题。正确做法是所有业务实体都继承或显式包含租户标识字段查询时从 JWT 里解析出当前用户所属租户在 service 层强制加过滤条件。后端框架通常会有全局的能力帮你做一部分但自定义表不会自动具备你想要的隔离逻辑必须自己补。另外如果你扩展了接口记得给接口加上权限控制。一套系统里管理员能做的事和普通员工能做的不应该完全相同。宁可在开始时把权限收紧再慢慢放开也不要默认全开放。6. 我实际部署中踩过的坑内存、权限、国际化与构建6.1 全栈同时启动导致内存爆掉第一次我在本地把 API、前端、数据库、Redis 全开起来16G 内存的笔记本直接卡到鼠标都飘。原因很简单Angular 开发服务器本身就吃内存NestJS 进程也不小再加上数据库和多个 Node 工具链内存自然紧张。解决办法分几个层面。开发阶段不用的模块不要全部启动按需启动对应服务前端构建时可以加内存参数限制 Node 堆大小避免进程无上限增长。真正到了生产环境用构建好的静态文件托管前端不用跑 Angular dev server内存问题会缓解很多。API 侧如果启动了很多 Worker也要看哪些任务真的需要不需要的异步 Worker 可以关掉。6.2 升级后权限数据没迁移干净这是我最想提醒大家的一个坑。ever-gauzy 迭代速度不慢偶尔会调整内置角色的默认权限比如新增某个菜单权限或者在某个角色模板里增加按钮。正常升级时数据库迁移会帮你加字段但已经存在的角色数据不一定被同步更新结果就是升级后用户角色缺少新菜单的权限页面看起来像“丢了一部分菜单”。遇到这个问题不要去生产数据库手动改几十条记录正确做法是升级前把权限相关的表先备份升级后对比变更日志或发布说明看有没有权限模板调整必要时用脚本批量补齐。我在踩过这个坑之后把备份命令加到了每个发布流程里再也没有乱过。权限表相关的典型备份命令大概是pg_dump postgres://gauzy:change_melocalhost:5432/gauzy \ -t roles -t role_permissions -t permissions \ -F c -f gauzy_permissions_$(date %F).dump6.3 界面冒出翻译 key 而不是中文ever-gauzy 官方界面支持多种语言中文翻译其实已经覆盖了不少。但二次开发新增的字段、菜单以及一些比较偏门的模块界面很容易显示成类似menu.help_center这样的翻译 key而不是正常的文字。那些多数情况是前端资源配置里缺少对应语言的词条。解法简单直接找到前端国际化资源文件按 key 补上目标语言的翻译。不要试图去改打包后在 node_modules 里的文件因为下次安装依赖就没了。正确姿势是把自定义翻译放到项目的扩展资源位置或者提给上游社区。顺便说一句如果你把系统交给不会英文的同事用这一项一定要在上线前检查一遍否则业务人员会觉得系统是坏的其实只是缺翻译。6.4 前端构建慢、产物大怎么处理Angular 项目构建慢是普遍问题ever-gauzy 的前端模块很多首次构建等几分钟很正常。优化方案我验证过几个构建时开启生产模式关闭 source map代码压缩效果非常明显把变化不频繁的第三方库分离出来利用 Angular 的缓存机制静态资源放在 CDN 或 Nginx 层做缓存让客户端不要每次请求都拿全量文件在后端和反向代理层同时开启 gzip 或 brotli 压缩前端包体积可以被压掉 60% 以上。发布之后记得检查浏览器控制台有没有报资源 404确认构建产物路径和 Nginx 静态目录匹配。这个坑我踩过好几次本地正常线上白屏最后发现是路径前缀不对。7. 生产环境部署与团队落地的一些实在建议7.1 用 Docker Compose 快速搭生产环境生产环境我不建议手动在裸机上一点点装依赖太容易漏。用 Docker Compose 做编排是比较稳妥的路子。下面是一个精简的编排示例给你参考services: postgres: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_DB: gauzy POSTGRES_USER: gauzy POSTGRES_PASSWORD: change_me volumes: - pgdata:/var/lib/postgresql/data redis: image: redis:7-alpine restart: unless-stopped api: build: ./apps/api restart: unless-stopped env_file: .env.production depends_on: - postgres - redis ports: - 3000:3000 ui: build: ./apps/gauzy restart: unless-stopped depends_on: - api ports: - 8080:80 volumes: pgdata:实际使用时要根据你拉取的仓库构建目录和镜像名调整但思路是不变的数据库和 Redis 做数据卷持久化API 和前端通过环境变量传参服务挂了能自动重启。强烈建议不要在生产环境把数据库密码写在 compose 文件里用环境变量或密钥管理工具注入。7.2 网关与 HTTPS、静态资源缓存前端构建产物放在 Nginx 里做静态托管API 走反向代理转发到后端端口这样对外只暴露 80/443。我在 Nginx 里最常用的一段配置大概是server { listen 80; server_name your-domain.example; client_max_body_size 20m; location /api/ { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; } location / { root /opt/gauzy/www; try_files $uri $uri/ /index.html; } }上线必须配 HTTPS这是底线不是可选项。用 Lets Encrypt 就能免费申请证书之后在 Nginx 里加上 443 的 server 块并做 80 跳转。文件上传大小也要调一下默认的 1M 限制对附件场景实在太小我一般调到 20M 以上具体看你业务需要传什么文件。7.3 备份与恢复的底线操作很多人装好系统第一件事是加员工加数据却把备份忘在脑后。我的习惯是数据库备份走定时任务每天凌晨执行一次同时保留最近 14 天的备份文件并且每两周做一次恢复演练。备份不只是把数据库文件复制出来就能安心你要定期验证“用这份备份能不能恢复出一个可用的系统”。恢复不出来的备份等于没有备份。典型备份命令前面提到过生产环境还要把上传文件目录一起备份。如果使用 S3 或对象存储保存文件就更省心至少数据不会跟单台服务器的磁盘一起消失。7.4 团队落地时别犯的流程错误技术层面没问题团队落地反而容易翻车。我见得太多次“系统功能全但业务不用”的情况。犯的错基本是同一个想一次性把所有历史数据、所有历史流程都搬进去结果导入数据格式对不上业务人员觉得系统麻烦最后弃用。正确的推进方式是分阶段走第一个月只启用客户、项目和发票把这三个模块用熟同时把新项目的流程全部在系统里走第二个月开始启用工时和报表要求项目成员每周在系统里补工时第三个月再启用考勤和招聘流程给人事部门适应时间。历史数据不要追求一次全量迁移把未完成的项目和在途发票放进去就够了历史归档数据放在系统外反而安全。等团队真正形成使用习惯再考虑逐步导入更多历史数据也不迟。最后再分享一点个人体会ever-gauzy 不是一个装完就万事大吉的产品它更像一个基本功扎实的半成品平台需要你根据自己的业务去打磨。我这个过程中最大收获其实是把“客户—项目—工时—发票”这条链路彻底理顺了业务部门不用再反复用 Excel 传文件管理层也能随时看到项目有没有超支、钱有没有收回。如果你决定入坑建议从本地环境搭建开始先在测试环境里完整跑一遍业务全流程再决定要不要上生产。另外因为上游社区更新频率不低建议配置好 upstream 仓库定期关注新版本的特性和数据库迁移说明避免大版本跳着升级造成不必要的兼容问题。最后还是要叮嘱一句任何自托管的系统安全责任都在你自己身上默认密码、密钥管理、备份恢复这三件事宁可多做不能不做。