独立业务模块接入指南
sz-module-admin是框架官方维护的核心模块,主要承载 RBAC、系统管理、认证授权、菜单、角色、字典等基础能力。二次开发时,不建议把客户自己的业务代码直接写进sz-module-admin,否则后续同步官方版本时更容易产生冲突,也会让业务代码和框架核心耦合在一起。推荐做法是:客户自有业务新建独立
sz-module-*,由sz-service-admin或自定义启动服务按需引入;前端较完整的业务域放到src/modules/<domain>,再通过 edition 注册。这样官方核心、客户业务、派生产品组合入口都能保持清晰边界。这套方式适合审计、代码生成器、SSO、商城、CRM、工单、物联网等具备独立边界的业务域。普通系统管理页面或很小的 CRUD 页面仍可继续放在
sz-module-admin既有能力范围和前端src/views中,不需要为了模块化而强行拆分。
什么时候需要独立模块
判断是否要拆成独立模块,可以先看业务有没有“独立边界”。
适合独立模块的场景:
- 可以按需启用或关闭,例如审计、代码生成器。
- 有自己的 Controller、Service、Mapper、数据库表和初始化菜单。
- 可能被不同派生项目复用。
- 后续会由不同团队维护,或需要降低与官方核心模块的升级冲突。
- 业务域比较完整,例如 SSO、CRM、商城、工单。
不一定要拆的场景:
- 只是普通系统管理页面的小改动。
- 只新增一两个轻量 CRUD,且明确属于现有后台管理能力。
- 只是对已有 admin 页面做展示字段、查询条件或按钮调整。
官方 sz-module-audit 是一个很好的参考:它有自己的后端模块、API 前缀、表结构、菜单权限、前端页面和 HTTP client,但又不需要改动 sz-module-admin 的核心代码。
后端如何接入
模块放在哪里
审计模块位于 sz-module 下,与 sz-module-admin、sz-module-generator 并列:
sz-module/
├── sz-module-admin # 官方后台管理核心,客户业务不建议直接改这里
├── sz-module-audit # 审计日志模块,独立维护 Controller / Mapper / Changelog
│ ├── pom.xml
│ └── src/main
│ ├── java/com/sz/audit
│ │ ├── config
│ │ └── controller
│ └── resources
│ ├── mapper
│ └── db/changelog
└── sz-module-generator # 代码生成器模块自有业务也按同样思路创建 sz-module-*。模块内放自己的业务实现,跨模块共享的接口、常量、轻量 DTO、事件和配置 key 再放到 sz-module-common。
模块自己声明 API 前缀
独立模块不需要在 Controller 上手动拼全局前缀,而是通过 ApiPrefixRegister 声明模块编码、默认前缀和 Controller 扫描包。
审计模块的实际写法如下:
@Configuration
public class AuditApiPrefixConfiguration {
@Bean
public ApiPrefixRegister auditApiPrefixRegister() {
return new ApiPrefixRegister() {
@Override
public String module() {
// 对应 sz.api-prefix.modules.audit
return "audit";
}
@Override
public String prefix() {
// 模块默认前缀,可在 application.yml 中覆盖
return "/audit";
}
@Override
public String[] basePackages() {
// 只给审计模块自己的 Controller 加前缀
return new String[] {"com.sz.audit.controller"};
}
};
}
}Controller 仍然只写模块内路径:
@RestController
@RequestMapping("sys-operation-log")
public class SysOperationLogController {
}默认 server.servlet.context-path=/api 时,最终路径就是:
/api/audit/sys-operation-log这种设计可以让每个模块维护自己的 API 命名空间,也方便前端、网关和权限按模块划分。
在启动服务里启用模块
独立模块本身不负责启动,它需要被 sz-service-admin 或其他启动服务装配。审计模块在 sz-service-admin/pom.xml 中引入:
<dependency>
<groupId>com.sz</groupId>
<artifactId>sz-module-audit</artifactId>
<version>${revision}</version>
</dependency>模块前缀可以在 application.yml 中按环境覆盖:
sz:
api-prefix:
modules:
audit:
# 是否启用 audit 模块的 Controller 前缀注册
enabled: true
# 最终接口路径会叠加 server.servlet.context-path
prefix: /audit这里的 enabled: false 只是不注册该模块的 Controller 前缀,不等于完整卸载模块。如果项目明确不需要某个模块,应同时移除服务 POM 依赖、数据库 changelog、前端模块注册、菜单和代理配置。
模块维护自己的数据库脚本
独立模块建议维护自己的 Liquibase 入口。审计模块的入口是:
sz-module-audit/src/main/resources/db/changelog/module-audit-changelog.xml启动服务的总 changelog 中按模块 include:
<!-- sz-module-audit 模块,可选;如无需审计能力可注释该入口并移除模块依赖 -->
<include file="db/changelog/module-audit-changelog.xml" errorIfMissing="false"/>审计模块初始化的内容包括 sys_operation_log、sys_operation_log_detail、操作审计菜单、按钮权限和索引。自有模块也建议把表结构、菜单、按钮权限、字典来源、系统配置等初始化数据放进自己的 changelog,避免和官方 admin、audit、generator 数据混在一起。
前端如何接入
独立 HTTP client
后端拆了模块前缀后,前端也不要继续在业务 API 里手动拼模块前缀。审计模块使用独立的 auditHttp:
export const AUDIT_API_BASE = normalizeApiBase(import.meta.env.VITE_AUDIT_API_BASE, '/api/audit');
export const auditHttp = createHttp(AUDIT_API_BASE);环境变量保持一致:
VITE_AUDIT_API_BASE=/api/audit业务 API 文件只写模块内路径:
import { auditHttp } from '@/api/client';
export const getSysOperationLogListApi = (params: SysOperationLogQuery) => {
// 实际请求路径:/api/audit/sys-operation-log
return auditHttp.get<IPage<SysOperationLogRow>>('/sys-operation-log', params);
};自有模块也按同样方式增加 xxxHttp,让 API 文件只关心模块内资源路径。
页面放到 src/modules
审计前端模块位于:
src/modules/audit/
├── api/sysOperationLog.ts
├── types/sysOperationLog.ts
├── views/sysOperationLog/index.vue
├── views/sysOperationLog/components/OperationLogDetail.vue
└── register.tssrc/modules/<domain> 适合放完整业务域。普通旧页面仍可留在 src/views,不用为了迁移而一次性改造。
用 register 连接菜单和页面
后端菜单里存的是 component 字段,前端需要把这个值解析到实际 Vue 页面。审计模块通过 register.ts 做显式映射:
import { defineModule } from '@/core';
export const auditModule = defineModule({
name: 'audit',
components: {
// 后端菜单 component 保持旧值,前端映射到新的模块页面
'/system/sysOperationLog/index': () => import('./views/sysOperationLog/index.vue')
}
});显式映射适合兼容旧菜单。如果是全新的自有模块,也可以从一开始就让菜单 component 与 src/modules/<domain>/views/<rest>.vue 保持一致。
动态路由解析顺序是:
- 已注册模块的
components显式映射。 src/modules/<domain>/views/<rest>.vue约定路径。src/views/<component>.vue旧目录兜底。
在 edition 中启用模块
模块定义好之后,需要在当前产品 edition 中注册。默认后台 edition 文件位于:
src/editions/admin.ts该文件导出前端产品组合对象 ADMIN_EDITION。src/main.ts 会导入 ADMIN_EDITION 并执行 ADMIN_EDITION.setup(),模块注册通常放在这个 setup() 方法里。
默认后台 edition 已注册 auditModule:
import { moduleRegistry } from '@/core';
import { auditModule } from '@/modules/audit/register';
export const ADMIN_EDITION = {
id: 'admin',
name: 'sz-admin',
setup(): void {
moduleRegistry.register(auditModule);
}
};edition 的作用是决定当前产品启用哪些登录适配器和业务模块。派生项目可以创建自己的 edition,只注册自己需要的模块,而不必改动框架主流程。
网关与部署同步
一个独立模块上线时,后端、前端和网关要保持同一套前缀。以审计模块为例:
| 位置 | 审计模块配置 |
|---|---|
后端 sz.api-prefix.modules.audit.prefix | /audit |
前端 .env* | VITE_AUDIT_API_BASE=/api/audit |
| Vite proxy | 代理 /api/audit/** 到后端 |
| Nginx / 网关 | 转发 /api/audit/** |
| 菜单权限 | sys.operation.log.query_table、sys.operation.log.detail |
如果线上出现 404,优先检查这几处是否一致。最常见的问题是后端前缀、前端 API base、Nginx location 三者只改了一部分。
不需要模块时怎么处理
独立模块可以按需启用,也可以按需移除。
如果只是临时不想开放菜单,可以不分配菜单权限或隐藏菜单。
如果不想启用某个模块的后端能力,应移除启动服务 POM 依赖,并同步处理 changelog、前端 edition 注册、菜单初始化数据和代理配置。
如果只是关闭模块前缀注册,可以设置:
sz:
api-prefix:
modules:
audit:
enabled: false但要注意,这不是完整卸载模块,只是让该模块 Controller 不再注册对应前缀。
接入时核对
自有模块接入时,重点核对这些点即可:
- 后端模块已加入聚合 POM,并被启动服务 POM 引入。
- 模块提供
ApiPrefixRegister,module()与sz.api-prefix.modules.<module>一致。 - Controller 包名被
basePackages()覆盖,最终路径符合/api/<module>/**预期。 - Liquibase 入口已被服务 changelog include,初始化数据标明模块归属。
- 前端新增对应 HTTP client、环境变量、API 文件和页面模块。
- edition 已注册需要启用的业务模块。
- 菜单
component能命中模块注册表、src/modules约定路径或src/views兜底路径。 - Vite proxy、Nginx、Sa-Token 白名单、资源
base-url和第三方回调已同步。
更多目录结构说明可阅读 目录结构,API 前缀配置可阅读 配置 - API 前缀说明,审计模块的业务功能说明可阅读 审计日志。
