增加demo模块。优化提示词文档

This commit is contained in:
lpd
2026-01-22 17:56:09 +08:00
parent 2863c963ca
commit cd5093d602
24 changed files with 2899 additions and 28 deletions

248
doc/README.md Normal file
View File

@@ -0,0 +1,248 @@
# Lyzsys 项目文档导航
欢迎使用 Lyzsys 企业级后台管理系统!本文档导航将帮助您快速找到所需的文档资源。
---
## 📚 文档目录
### 🚀 快速开始
| 文档 | 说明 | 适用人群 |
|-----|------|---------|
| [项目介绍](./项目介绍.md) | 项目概述、技术栈、核心特性 | 所有人 |
| [部署指南](./部署指南.md) | 环境搭建、安装部署、配置说明 | 运维、开发 |
| [常见问题](./常见问题.md) | FAQ、故障排查、解决方案 | 所有人 |
### 🏗️ 架构设计
| 文档 | 说明 | 适用人群 |
|-----|------|---------|
| [架构设计](./架构设计.md) | 系统架构、技术选型、设计思路 | 架构师、开发 |
| [数据库设计](./数据库设计.md) | 数据库表结构、字段说明、ER图 | 开发、DBA |
### 💻 开发文档
| 文档 | 说明 | 适用人群 | 重要程度 |
|-----|------|---------|---------|
| [后端开发文档](./后端开发文档.md) | 后端开发指南、核心功能、代码示例 | 后端开发 | ⭐⭐⭐⭐⭐ |
| [前端开发文档](./前端开发文档.md) | 前端开发指南、组件使用、最佳实践 | 前端开发 | ⭐⭐⭐⭐⭐ |
| [编码规范](./编码规范.md) | 命名规范、代码规范、最佳实践 | 所有开发 | ⭐⭐⭐⭐⭐ |
| [新模块开发指南](./新模块开发指南.md) | 新模块开发完整教程(含 Demo 示例) | 所有开发 | ⭐⭐⭐⭐⭐ |
---
## 🎯 按场景查找文档
### 场景 1我是新加入的开发人员
**推荐阅读顺序**
1. [项目介绍](./项目介绍.md) - 了解项目概况
2. [部署指南](./部署指南.md) - 搭建开发环境
3. [编码规范](./编码规范.md) - 熟悉编码规范 ⚠️ **必读**
4. [后端开发文档](./后端开发文档.md) 或 [前端开发文档](./前端开发文档.md) - 根据岗位选择
5. [新模块开发指南](./新模块开发指南.md) - 学习如何开发新功能
### 场景 2我要开发一个新的业务模块
**推荐步骤**
1. **必读** → [编码规范](./编码规范.md) - 了解命名规范和菜单结构规范
2. **必读** → [新模块开发指南](./新模块开发指南.md) - 按照步骤创建模块
3. **参考** → Demo 模块代码(`lyzsys-module-demo`- 作为实现参考
4. **参考** → [后端开发文档](./后端开发文档.md) - 查阅具体技术实现
5. **参考** → [前端开发文档](./前端开发文档.md) - 前端页面开发
### 场景 3我遇到了问题
**查找方式**
1. [常见问题](./常见问题.md) - 先查看 FAQ
2. [后端开发文档](./后端开发文档.md) - 查看附录的常见问题
3. GitHub Issues - 搜索或提交问题
### 场景 4我要了解系统架构
**推荐阅读**
1. [项目介绍](./项目介绍.md) - 快速了解
2. [架构设计](./架构设计.md) - 详细架构说明
3. [数据库设计](./数据库设计.md) - 数据模型
4. [后端开发文档](./后端开发文档.md) - 核心功能实现
---
## 📖 重点文档说明
### 🔥 编码规范(必读)
**文件**[编码规范.md](./编码规范.md)
**核心内容**
- ⚠️ **驼峰命名规范**:第一个单词不能只有一个字母(重要!)
- 📋 **菜单结构规范**:业务菜单优先,系统管理放最后
- 🏗️ **代码结构规范**Controller-Service-DAL 三层架构
- 💾 **数据库设计规范**:表名、字段名、索引规范
- 🔌 **API 接口规范**RESTful 风格、统一响应格式
- 🎨 **前端开发规范**:组件规范、权限控制
**为什么重要**
- 避免 MyBatis Plus 字段映射问题
- 提高代码可读性和可维护性
- 统一团队开发风格
### 🚀 新模块开发指南(必读)
**文件**[新模块开发指南.md](./新模块开发指南.md)
**核心内容**
- ✅ 完整的开发流程(从数据库设计到前端页面)
- 📝 逐步操作指南(包含完整代码示例)
- 🎯 以 **Demo 模块**为参考案例
- ✔️ 开发检查清单
**包含内容**
1. 创建后端模块Maven 配置、实体类、Service、Controller
2. 创建前端页面API 接口、列表页、表单组件)
3. 配置菜单权限(一级菜单、二级菜单、按钮权限)
4. 常见问题解决
### 📦 Demo 示例模块
**模块位置**
- 后端:`lyzsys_backend/lyzsys-module-demo`
- 前端:`lyzsys-ui-admin/src/views/demo/project`
- SQL`lyzsys_backend/sql/mysql/demo_*.sql`
**功能展示**
- ✅ 项目管理的完整 CRUD 功能
- ✅ 分页查询、批量删除、Excel 导出
- ✅ 遵循所有编码规范的标准实现
- ✅ 前后端完整联调示例
**如何使用**
1. 查看 Demo 模块的代码结构
2. 参考其命名方式和代码风格
3. 复制并修改为自己的业务模块
4. 详细步骤见 [新模块开发指南](./新模块开发指南.md)
---
## 📂 文档结构
```
doc/
├── README.md # 📖 本文档(文档导航)
├── 编码规范.md # ⭐⭐⭐⭐⭐ 命名规范、开发规范
├── 新模块开发指南.md # ⭐⭐⭐⭐⭐ 新模块开发教程
├── 后端开发文档.md # 后端开发指南
├── 前端开发文档.md # 前端开发指南
├── 项目介绍.md # 项目概述
├── 架构设计.md # 架构设计文档
├── 数据库设计.md # 数据库设计文档
├── 部署指南.md # 部署运维文档
└── 常见问题.md # FAQ 文档
```
---
## 🔑 关键规范速查
### 驼峰命名规范
⚠️ **核心原则**:第一个单词不能只有一个字母
```java
// ✅ 正确
projectId, projectName, establishDate
// ❌ 错误
pId, pName, eDate
```
**详细说明**[编码规范 - 命名规范](./编码规范.md#1-命名规范)
### 菜单结构规范
```
1-90. 业务菜单(优先展示)
98. 系统管理
99. 基础设施
```
**详细说明**[编码规范 - 菜单结构规范](./编码规范.md#2-菜单结构规范)
### 权限标识格式
```
{模块名}:{业务}:{操作}
示例:
demo:project:query
demo:project:create
demo:project:update
```
**详细说明**[编码规范 - 菜单配置规范](./编码规范.md#23-权限标识规范)
---
## 🛠️ 开发工具推荐
### 后端开发
- **IDE**IntelliJ IDEA推荐
- **JDK**1.8+
- **Maven**3.6+
- **数据库工具**Navicat、DataGrip
### 前端开发
- **IDE**VSCode、WebStorm
- **Node.js**16+
- **包管理器**npm、pnpm
### 版本管理
- **Git 客户端**Git、SourceTree、GitKraken
- **代码托管**GitHub、GitLab
---
## 📞 获取帮助
### 文档问题
- 查看 [常见问题](./常见问题.md)
- 搜索 GitHub Issues
### 技术支持
- 提交 GitHub Issue
- 联系开发团队
### 参与贡献
- Fork 项目
- 提交 Pull Request
- 完善文档
---
## 📝 文档更新记录
| 日期 | 版本 | 更新内容 | 作者 |
|-----|------|---------|------|
| 2024-01 | v1.0 | 创建文档导航,新增编码规范和新模块开发指南 | 开发团队 |
| 2024-01 | v1.0 | 添加 Demo 示例模块说明 | 开发团队 |
---
## 🎉 开始使用
如果您是第一次使用 Lyzsys建议按以下顺序阅读文档
1. ✅ [项目介绍](./项目介绍.md) - 5分钟了解项目
2. ✅ [部署指南](./部署指南.md) - 搭建开发环境
3. ✅ [编码规范](./编码规范.md) - **必读**,熟悉编码规范
4. ✅ [新模块开发指南](./新模块开发指南.md) - 开发第一个模块
5. ✅ [后端开发文档](./后端开发文档.md) / [前端开发文档](./前端开发文档.md) - 深入学习
祝您使用愉快!🚀
---
**文档维护**Lyzsys 开发团队
**最后更新**2024年
**文档版本**v1.0

43
doc/demo_menu.sql Normal file
View File

@@ -0,0 +1,43 @@
-- ----------------------------
-- 菜单配置 SQL - 项目管理模块
-- ----------------------------
-- 一级菜单:项目管理(放在最前面)
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
VALUES (3000, '项目管理', '', 1, 1, 0, '/demo', 'ep:files', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
-- 二级菜单:项目列表
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
VALUES (3001, '项目列表', '', 2, 1, 3000, 'project', 'ep:document', 'demo/project/index', 'DemoProject', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
-- 按钮权限:查询
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
VALUES (3002, '项目查询', 'demo:project:query', 3, 1, 3001, '', '', '', NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
-- 按钮权限:新增
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
VALUES (3003, '项目新增', 'demo:project:create', 3, 2, 3001, '', '', '', NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
-- 按钮权限:修改
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
VALUES (3004, '项目修改', 'demo:project:update', 3, 3, 3001, '', '', '', NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
-- 按钮权限:删除
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
VALUES (3005, '项目删除', 'demo:project:delete', 3, 4, 3001, '', '', '', NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
-- 按钮权限:导出
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
VALUES (3006, '项目导出', 'demo:project:export', 3, 5, 3001, '', '', '', NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
-- ----------------------------
-- 调整系统管理和基础设施菜单的排序(放到最后)
-- 注意:这里假设系统管理菜单 id=1基础设施菜单 id=2
-- 如果 id 不同,请根据实际情况修改
-- ----------------------------
-- 将系统管理菜单 sort 调整为 98
UPDATE `system_menu` SET `sort` = 98, `updater` = '1', `update_time` = NOW() WHERE `id` = 1 AND `deleted` = b'0';
-- 将基础设施菜单 sort 调整为 99
UPDATE `system_menu` SET `sort` = 99, `updater` = '1', `update_time` = NOW() WHERE `id` = 2 AND `deleted` = b'0';

25
doc/demo_project.sql Normal file
View File

@@ -0,0 +1,25 @@
-- ----------------------------
-- Table structure for demo_project
-- ----------------------------
DROP TABLE IF EXISTS `demo_project`;
CREATE TABLE `demo_project` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '项目ID',
`project_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '项目名称',
`project_code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '项目编号',
`establish_date` datetime NULL DEFAULT NULL COMMENT '立项时间',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '项目管理表';
-- ----------------------------
-- Records of demo_project (示例数据)
-- ----------------------------
BEGIN;
INSERT INTO `demo_project` (`id`, `project_name`, `project_code`, `establish_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`)
VALUES (1, '示例项目一', 'PROJ-2024-001', '2024-01-01 00:00:00', '1', '2024-01-01 10:00:00', '1', '2024-01-01 10:00:00', b'0', 1);
COMMIT;

259
doc/demo_部署说明.md Normal file
View File

@@ -0,0 +1,259 @@
# 项目管理模块部署说明
## 一、概述
本文档说明如何部署 lyzsys-module-demo 项目管理模块。
### 模块信息
- **模块名称**: lyzsys-module-demo
- **功能**: 项目管理(增删改查导出)
- **数据库表**: demo_project
- **菜单结构**:
- 一级菜单:项目管理
- 二级菜单:项目列表
### 命名规范遵循
✅ 所有字段名遵循驼峰命名规范,第一个单词不能只有一个字母
- `projectName`(而非 `pName`
- `projectCode`(而非 `pCode`
- `establishDate`(而非 `eDate`
## 二、部署步骤
### 1. 数据库部署
按顺序执行以下 SQL 脚本:
```bash
# 1. 创建项目管理表
mysql> source e:/workspace/lyzsys/lyzsys_backend/sql/mysql/demo_project.sql
# 2. 创建菜单配置
mysql> source e:/workspace/lyzsys/lyzsys_backend/sql/mysql/demo_menu.sql
```
**说明**
- `demo_project.sql` - 创建项目管理表,包含示例数据
- `demo_menu.sql` - 创建菜单配置,包含一级菜单、二级菜单和按钮权限
### 2. 后端部署
后端代码已自动集成到项目中,无需额外配置。
**后端文件清单**
```
lyzsys_backend/lyzsys-module-demo/
├── pom.xml # Maven 配置
└── src/main/java/cn/iocoder/lyzsys/module/demo/
├── controller/admin/project/
│ ├── ProjectController.java # 控制器
│ └── vo/
│ ├── ProjectPageReqVO.java # 分页查询请求
│ ├── ProjectRespVO.java # 响应 VO
│ └── ProjectSaveReqVO.java # 保存请求 VO
├── service/project/
│ ├── ProjectService.java # 服务接口
│ └── ProjectServiceImpl.java # 服务实现
├── dal/
│ ├── dataobject/project/
│ │ └── ProjectDO.java # 数据对象
│ └── mysql/project/
│ └── ProjectMapper.java # Mapper 接口
└── enums/
└── ErrorCodeConstants.java # 错误码常量
```
**验证后端部署**
1. Maven 重新导入项目依赖
2. 确认 `lyzsys-server/pom.xml` 包含 `lyzsys-module-demo` 依赖
3. 启动后端项目,查看日志确认模块加载成功
### 3. 前端部署
前端代码已生成,无需额外配置。
**前端文件清单**
```
lyzsys-ui-admin/src/
├── api/demo/project/
│ └── index.ts # API 接口定义
└── views/demo/project/
├── index.vue # 项目列表页面
└── ProjectForm.vue # 项目表单组件
```
**验证前端部署**
1. 重新启动前端项目(如果正在运行)
2. 使用管理员账号登录系统
3. 在左侧菜单栏查看"项目管理"菜单
## 三、功能验证
### 1. 菜单验证
登录后台管理系统,应该看到以下菜单结构:
```
项目管理(一级菜单)
└── 项目列表(二级菜单)
系统管理(已调整到最后)
基础设施(已调整到最后)
```
### 2. 权限验证
项目列表页面应包含以下操作按钮:
- ✅ 搜索
- ✅ 重置
- ✅ 新增
- ✅ 导出
- ✅ 批量删除
- ✅ 修改(每行)
- ✅ 删除(每行)
### 3. 功能验证
测试以下功能:
#### 查询功能
- [ ] 按项目名称模糊查询
- [ ] 按项目编号模糊查询
- [ ] 按立项时间范围查询
- [ ] 分页查询
#### 新增功能
- [ ] 点击"新增"按钮打开表单
- [ ] 必填字段验证(项目名称、项目编号)
- [ ] 项目编号唯一性校验
- [ ] 保存成功后刷新列表
#### 修改功能
- [ ] 点击"修改"按钮打开表单
- [ ] 回显原有数据
- [ ] 修改后保存成功
#### 删除功能
- [ ] 单个删除确认提示
- [ ] 删除成功后刷新列表
- [ ] 批量删除功能
#### 导出功能
- [ ] 点击"导出"按钮
- [ ] 下载 Excel 文件
- [ ] 验证导出数据正确
## 四、接口文档
### API 端点
**Base URL**: `/demo/project`
| 接口 | 方法 | 说明 | 权限 |
|-----|------|------|------|
| `/create` | POST | 创建项目 | demo:project:create |
| `/update` | PUT | 修改项目 | demo:project:update |
| `/delete` | DELETE | 删除项目 | demo:project:delete |
| `/delete-list` | DELETE | 批量删除 | demo:project:delete |
| `/page` | GET | 分页查询 | demo:project:query |
| `/get` | GET | 查询详情 | demo:project:query |
| `/export-excel` | GET | 导出Excel | demo:project:export |
### 请求示例
**创建项目**
```json
POST /demo/project/create
{
"projectName": "示例项目",
"projectCode": "PROJ-2024-001",
"establishDate": "2024-01-01 00:00:00"
}
```
**查询列表**
```
GET /demo/project/page?pageNo=1&pageSize=10&projectName=示例
```
## 五、常见问题
### 1. 菜单不显示
**原因**:可能是权限问题
**解决**
1. 确认已执行 `demo_menu.sql`
2. 检查当前登录用户的角色权限
3. 在"系统管理 > 菜单管理"中确认菜单已创建
4. 在"系统管理 > 角色管理"中给当前角色分配菜单权限
### 2. 后端接口 404
**原因**:模块未启动或路由配置错误
**解决**
1. 检查 `lyzsys-server/pom.xml` 是否包含 demo 模块依赖
2. 重新编译启动项目
3. 查看启动日志确认模块加载
### 3. 前端页面报错
**原因**API 接口路径不匹配
**解决**
1. 检查 `src/api/demo/project/index.ts` 中的接口路径
2. 确认后端 Controller 的 `@RequestMapping` 路径
3. 查看浏览器控制台错误信息
### 4. 项目编号重复错误
**原因**:唯一性校验生效
**解决**:这是正常的业务校验,请使用不同的项目编号
## 六、扩展开发
### 添加新字段
1. **修改数据库表**
```sql
ALTER TABLE demo_project ADD COLUMN new_field VARCHAR(100) COMMENT '新字段';
```
2. **修改后端代码**
-`ProjectDO.java` 中添加字段
-`ProjectRespVO.java``ProjectSaveReqVO.java` 中添加字段
- 根据需要修改 `ProjectMapper.java` 查询条件
3. **修改前端代码**
-`api/demo/project/index.ts` 中添加字段定义
-`ProjectForm.vue` 中添加表单项
-`index.vue` 中添加表格列
### 添加新功能
参考现有的增删改查实现,遵循以下开发流程:
1. 在 Service 层添加业务方法
2. 在 Controller 层添加接口
3. 在前端 API 文件中添加接口调用
4. 在前端页面中实现 UI 交互
## 七、技术栈
### 后端
- Spring Boot 2.7.18
- MyBatis Plus 3.5.15
- MySQL 8.0+
### 前端
- Vue 3.5.12
- Element Plus 2.11.1
- TypeScript 5.3.3
- Vite 5.1.4
## 八、联系方式
如有问题,请联系开发团队或查阅项目文档。
---
**生成时间**: 2024年
**版本**: v1.0

View File

@@ -828,16 +828,224 @@ lyzsys:
---
## Demo 示例模块
### 模块简介
**lyzsys-module-demo** 是一个完整的业务模块示例,展示了如何在 Lyzsys 系统中快速创建一个新的业务模块。该模块实现了项目管理的完整 CRUD 功能,包括增删改查和 Excel 导出。
### 功能特性
- ✅ 完整的 CRUD 接口(增删改查)
- ✅ 分页查询支持
- ✅ 批量删除功能
- ✅ Excel 导出功能
- ✅ 业务字段唯一性校验
- ✅ 权限控制
- ✅ 前后端完整实现
### 模块结构
```
lyzsys-module-demo/
├── pom.xml # Maven 配置
└── src/main/java/cn/iocoder/lyzsys/module/demo/
├── controller/admin/project/ # 控制器层
│ ├── ProjectController.java # 项目控制器
│ └── vo/ # VO 对象
│ ├── ProjectPageReqVO.java # 分页查询请求
│ ├── ProjectRespVO.java # 响应 VO
│ └── ProjectSaveReqVO.java # 保存请求 VO
├── service/project/ # 服务层
│ ├── ProjectService.java # 服务接口
│ └── ProjectServiceImpl.java # 服务实现
├── dal/ # 数据访问层
│ ├── dataobject/project/ # 数据对象
│ │ └── ProjectDO.java # 项目实体类
│ └── mysql/project/ # Mapper 接口
│ └── ProjectMapper.java # 项目 Mapper
└── enums/ # 枚举类
└── ErrorCodeConstants.java # 错误码常量
```
### 核心代码示例
#### 实体类ProjectDO
```java
@TableName("demo_project")
@Data
@EqualsAndHashCode(callSuper = true)
public class ProjectDO extends BaseDO {
@TableId
private Long id;
// 遵循命名规范:第一个单词不能只有一个字母
private String projectName; // ✅ 正确(而非 pName
private String projectCode; // ✅ 正确(而非 pCode
private LocalDateTime establishDate; // ✅ 正确(而非 eDate
}
```
#### Controller 示例
```java
@Tag(name = "管理后台 - 项目管理")
@RestController
@RequestMapping("/demo/project")
public class ProjectController {
@PostMapping("/create")
@Operation(summary = "创建项目")
@PreAuthorize("@ss.hasPermission('demo:project:create')")
public CommonResult<Long> createProject(@Valid @RequestBody ProjectSaveReqVO createReqVO) {
return success(projectService.createProject(createReqVO));
}
// 其他 CRUD 方法...
}
```
### 数据库设计
```sql
CREATE TABLE `demo_project` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '项目ID',
`project_name` varchar(100) NOT NULL COMMENT '项目名称',
`project_code` varchar(50) NOT NULL COMMENT '项目编号',
`establish_date` datetime NULL COMMENT '立项时间',
-- 标准字段
`creator` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='项目管理表';
```
### 菜单配置
Demo 模块的菜单结构:
```
项目管理一级菜单sort: 1
└── 项目列表二级菜单sort: 1
├── 项目查询demo:project:query
├── 项目新增demo:project:create
├── 项目修改demo:project:update
├── 项目删除demo:project:delete
└── 项目导出demo:project:export
```
### 前端实现
前端代码位置:
- API 接口:`src/api/demo/project/index.ts`
- 列表页面:`src/views/demo/project/index.vue`
- 表单组件:`src/views/demo/project/ProjectForm.vue`
### 作为参考模板
Demo 模块可以作为新业务模块开发的参考模板,包含了:
1. **标准的代码结构** - 遵循项目分层架构
2. **完整的业务逻辑** - 包括校验、异常处理等
3. **规范的命名方式** - 遵循驼峰命名规范
4. **权限控制示例** - 展示如何配置权限
5. **前后端联调** - 完整的前后端交互流程
**详细开发指南请参考**
- [新模块开发指南](./新模块开发指南.md) - 基于 demo 模块的完整开发教程
- [编码规范](./编码规范.md) - 命名规范、菜单结构规范等
---
## 开发规范
为确保代码质量和团队协作效率Lyzsys 项目制定了一系列开发规范。
### 核心规范文档
1. **[编码规范](./编码规范.md)** - 必读 ⭐⭐⭐⭐⭐
- 驼峰命名规范(重要:第一个单词不能只有一个字母)
- 菜单结构规范
- 代码结构规范
- 数据库设计规范
- API 接口规范
- 前端开发规范
2. **[新模块开发指南](./新模块开发指南.md)** - 实战教程 ⭐⭐⭐⭐⭐
- 快速开始
- 创建后端模块(完整步骤)
- 创建前端页面
- 配置菜单权限
- 以 Demo 模块为参考示例
### 关键规范要点
#### 命名规范核心原则
⚠️ **重要**:所有字段名、类名的驼峰命名中,**第一个单词不能只有一个字母**
```java
// ✅ 正确示例
private String projectId; // 而非 pId
private String projectName; // 而非 pName
private String establishDate; // 而非 eDate
// ❌ 错误示例
private String pId; // 单字母开头,可能导致 MyBatis Plus 映射问题
private String mType; // 单字母开头
```
#### 菜单结构规范
业务功能菜单优先展示,系统管理功能放在最后:
```
1-90. 业务菜单(项目管理、客户管理等)
98. 系统管理
99. 基础设施
```
#### 权限标识格式
```
格式:{模块名}:{业务}:{操作}
示例:
demo:project:query # 查询
demo:project:create # 新增
demo:project:update # 修改
demo:project:delete # 删除
demo:project:export # 导出
```
### 开发流程建议
1. **阅读规范文档** → 了解编码规范和开发流程
2. **参考 Demo 模块** → 理解标准的实现方式
3. **按照开发指南** → 逐步创建新模块
4. **代码评审** → 确保符合规范要求
---
## 附录
### 常见问题
#### Q1: 如何添加新的业务模块?
1.`lyzsys-module-xxx` 目录下创建新模块
2. 按照标准模块结构创建包和类
3.`lyzsys-server/pom.xml` 中添加模块依赖
4. `application.yaml` 中配置模块扫描路径
**推荐方式**:参考 [新模块开发指南](./新模块开发指南.md),以 Demo 模块为模板创建新模块
**快速步骤**
1. 创建 Maven 模块和目录结构
2. 编写数据库表(遵循命名规范)
3. 按照 Demo 模块的结构创建后端代码
4. 创建前端页面和 API 接口
5. 配置菜单和权限
#### Q2: 如何自定义异常?

View File

@@ -0,0 +1,931 @@
# Lyzsys 新模块开发指南
## 目录
- [1. 快速开始](#1-快速开始)
- [2. 创建后端模块](#2-创建后端模块)
- [3. 创建前端页面](#3-创建前端页面)
- [4. 配置菜单权限](#4-配置菜单权限)
- [5. 完整示例](#5-完整示例)
- [6. 常见问题](#6-常见问题)
---
## 1. 快速开始
本指南以 **lyzsys-module-demo** 项目管理模块为参考,演示如何快速创建一个新的业务模块。
### 1.1 开发流程
```
1. 设计数据库表
2. 创建后端模块
├── 创建 Maven 模块
├── 编写实体类DO
├── 编写 Mapper 接口
├── 编写 VO 类
├── 编写 Service 层
└── 编写 Controller 层
3. 创建前端页面
├── 编写 API 接口
├── 编写列表页面
└── 编写表单组件
4. 配置菜单权限
├── 创建一级菜单
├── 创建二级菜单
└── 创建按钮权限
5. 测试验证
```
### 1.2 前置准备
- [ ] 确认数据库表设计
- [ ] 确认菜单结构(一级、二级菜单名称)
- [ ] 确认业务字段和验证规则
- [ ] 准备好模块名称和权限标识
---
## 2. 创建后端模块
### 2.1 创建 Maven 模块
#### 步骤 1创建模块目录
`lyzsys_backend` 下创建新模块:
```
lyzsys_backend/
└── lyzsys-module-{模块名}/
├── pom.xml
└── src/main/java/cn/iocoder/lyzsys/module/{模块名}/
```
#### 步骤 2编写 pom.xml
参考 `lyzsys-module-demo/pom.xml`
```xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>lyzsys-module-{模块名}</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
{模块说明}
</description>
<dependencies>
<!-- 依赖 infra 模块 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys-module-infra</artifactId>
<version>${revision}</version>
</dependency>
<!-- 业务组件 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys-spring-boot-starter-biz-tenant</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys-spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys-spring-boot-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys-spring-boot-starter-redis</artifactId>
</dependency>
<!-- Test 测试相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys-spring-boot-starter-excel</artifactId>
</dependency>
</dependencies>
</project>
```
#### 步骤 3添加模块到父 pom.xml
`lyzsys_backend/pom.xml` 中添加:
```xml
<modules>
<!-- 已有模块 -->
<module>lyzsys-module-system</module>
<module>lyzsys-module-infra</module>
<!-- 新增模块 -->
<module>lyzsys-module-{模块名}</module>
</modules>
```
#### 步骤 4添加模块到 server
`lyzsys-server/pom.xml` 中添加依赖:
```xml
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys-module-{模块名}</artifactId>
<version>${revision}</version>
</dependency>
```
### 2.2 创建数据库表
参考 `sql/mysql/demo_project.sql`
```sql
DROP TABLE IF EXISTS `{表名}`;
CREATE TABLE `{表名}` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
-- 业务字段(注意:遵循驼峰命名规范,第一个单词不能只有一个字母)
`business_name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务名称',
`business_code` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务编号',
-- 标准字段(所有表必备)
`creator` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '业务表';
```
### 2.3 创建实体类DO
文件位置:`src/main/java/cn/iocoder/lyzsys/module/{模块名}/dal/dataobject/{业务}/{业务}DO.java`
参考 `ProjectDO.java`
```java
package cn.iocoder.lyzsys.module.{模块名}.dal.dataobject.{业务};
import cn.iocoder.lyzsys.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* {业务名称} DO
*
* @author {作者}
*/
@TableName("{表名}")
@KeySequence("{表名}_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增
@Data
@EqualsAndHashCode(callSuper = true)
public class {业务}DO extends BaseDO {
/**
* 主键ID
*/
@TableId
private Long id;
/**
* 业务字段(注意驼峰命名)
*/
private String businessName;
private String businessCode;
private LocalDateTime businessDate;
}
```
### 2.4 创建 Mapper 接口
文件位置:`src/main/java/cn/iocoder/lyzsys/module/{模块名}/dal/mysql/{业务}/{业务}Mapper.java`
参考 `ProjectMapper.java`
```java
package cn.iocoder.lyzsys.module.{模块名}.dal.mysql.{业务};
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
import cn.iocoder.lyzsys.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.lyzsys.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.lyzsys.module.{模块名}.controller.admin.{业务}.vo.{业务}PageReqVO;
import cn.iocoder.lyzsys.module.{模块名}.dal.dataobject.{业务}.{业务}DO;
import org.apache.ibatis.annotations.Mapper;
/**
* {业务名称} Mapper
*
* @author {作者}
*/
@Mapper
public interface {业务}Mapper extends BaseMapperX<{业务}DO> {
default PageResult<{业务}DO> selectPage({业务}PageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<{业务}DO>()
.likeIfPresent({业务}DO::getBusinessName, reqVO.getBusinessName())
.likeIfPresent({业务}DO::getBusinessCode, reqVO.getBusinessCode())
.betweenIfPresent({业务}DO::getCreateTime, reqVO.getCreateTime())
.orderByDesc({业务}DO::getId));
}
default {业务}DO selectByBusinessCode(String businessCode) {
return selectOne({业务}DO::getBusinessCode, businessCode);
}
}
```
### 2.5 创建 VO 类
#### 分页查询请求 VO
文件位置:`src/main/java/cn/iocoder/lyzsys/module/{模块名}/controller/admin/{业务}/vo/{业务}PageReqVO.java`
```java
package cn.iocoder.lyzsys.module.{模块名}.controller.admin.{业务}.vo;
import cn.iocoder.lyzsys.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.lyzsys.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - {业务名称}分页列表 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class {业务}PageReqVO extends PageParam {
@Schema(description = "业务名称,模糊匹配", example = "示例")
private String businessName;
@Schema(description = "业务编号,模糊匹配", example = "CODE-001")
private String businessCode;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "创建时间")
private LocalDateTime[] createTime;
}
```
#### 响应 VO
文件位置:`src/main/java/cn/iocoder/lyzsys/module/{模块名}/controller/admin/{业务}/vo/{业务}RespVO.java`
```java
package cn.iocoder.lyzsys.module.{模块名}.controller.admin.{业务}.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - {业务名称}信息 Response VO")
@Data
@ExcelIgnoreUnannotated
public class {业务}RespVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("ID")
private Long id;
@Schema(description = "业务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "示例")
@ExcelProperty("业务名称")
private String businessName;
@Schema(description = "业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "CODE-001")
@ExcelProperty("业务编号")
private String businessCode;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}
```
#### 保存请求 VO
文件位置:`src/main/java/cn/iocoder/lyzsys/module/{模块名}/controller/admin/{业务}/vo/{业务}SaveReqVO.java`
```java
package cn.iocoder.lyzsys.module.{模块名}.controller.admin.{业务}.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - {业务名称}创建/修改 Request VO")
@Data
public class {业务}SaveReqVO {
@Schema(description = "主键ID", example = "1")
private Long id;
@Schema(description = "业务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "示例")
@NotBlank(message = "业务名称不能为空")
@Size(max = 100, message = "业务名称长度不能超过100个字符")
private String businessName;
@Schema(description = "业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "CODE-001")
@NotBlank(message = "业务编号不能为空")
@Size(max = 50, message = "业务编号长度不能超过50个字符")
private String businessCode;
}
```
### 2.6 创建 Service 层
#### Service 接口
文件位置:`src/main/java/cn/iocoder/lyzsys/module/{模块名}/service/{业务}/{业务}Service.java`
```java
package cn.iocoder.lyzsys.module.{模块名}.service.{业务};
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
import cn.iocoder.lyzsys.module.{模块名}.controller.admin.{业务}.vo.{业务}PageReqVO;
import cn.iocoder.lyzsys.module.{模块名}.controller.admin.{业务}.vo.{业务}SaveReqVO;
import cn.iocoder.lyzsys.module.{模块名}.dal.dataobject.{业务}.{业务}DO;
import java.util.List;
/**
* {业务名称} Service 接口
*
* @author {作者}
*/
public interface {业务}Service {
/**
* 创建{业务}
*/
Long create{业务}({业务}SaveReqVO createReqVO);
/**
* 更新{业务}
*/
void update{业务}({业务}SaveReqVO updateReqVO);
/**
* 删除{业务}
*/
void delete{业务}(Long id);
/**
* 批量删除{业务}
*/
void delete{业务}List(List<Long> ids);
/**
* 获得{业务}分页列表
*/
PageResult<{业务}DO> get{业务}Page({业务}PageReqVO pageReqVO);
/**
* 获得{业务}详情
*/
{业务}DO get{业务}(Long id);
}
```
#### Service 实现类
文件位置:`src/main/java/cn/iocoder/lyzsys/module/{模块名}/service/{业务}/{业务}ServiceImpl.java`
```java
package cn.iocoder.lyzsys.module.{模块名}.service.{业务};
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils;
import cn.iocoder.lyzsys.module.{模块名}.controller.admin.{业务}.vo.{业务}PageReqVO;
import cn.iocoder.lyzsys.module.{模块名}.controller.admin.{业务}.vo.{业务}SaveReqVO;
import cn.iocoder.lyzsys.module.{模块名}.dal.dataobject.{业务}.{业务}DO;
import cn.iocoder.lyzsys.module.{模块名}.dal.mysql.{业务}.{业务}Mapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.List;
import static cn.iocoder.lyzsys.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.lyzsys.module.{模块名}.enums.ErrorCodeConstants.*;
/**
* {业务名称} Service 实现类
*
* @author {作者}
*/
@Service
@Validated
public class {业务}ServiceImpl implements {业务}Service {
@Resource
private {业务}Mapper {业务}Mapper;
@Override
public Long create{业务}({业务}SaveReqVO createReqVO) {
// 校验业务编号的唯一性
validateBusinessCodeUnique(null, createReqVO.getBusinessCode());
// 插入
{业务}DO {业务} = BeanUtils.toBean(createReqVO, {业务}DO.class);
{业务}Mapper.insert({业务});
return {业务}.getId();
}
@Override
public void update{业务}({业务}SaveReqVO updateReqVO) {
// 校验存在
validate{业务}Exists(updateReqVO.getId());
// 校验业务编号的唯一性
validateBusinessCodeUnique(updateReqVO.getId(), updateReqVO.getBusinessCode());
// 更新
{业务}DO updateObj = BeanUtils.toBean(updateReqVO, {业务}DO.class);
{业务}Mapper.updateById(updateObj);
}
@Override
public void delete{业务}(Long id) {
// 校验存在
validate{业务}Exists(id);
// 删除
{业务}Mapper.deleteById(id);
}
@Override
public void delete{业务}List(List<Long> ids) {
// 校验存在
ids.forEach(this::validate{业务}Exists);
// 批量删除
{业务}Mapper.deleteBatchIds(ids);
}
@Override
public PageResult<{业务}DO> get{业务}Page({业务}PageReqVO pageReqVO) {
return {业务}Mapper.selectPage(pageReqVO);
}
@Override
public {业务}DO get{业务}(Long id) {
return {业务}Mapper.selectById(id);
}
// ==================== 校验方法 ====================
private void validate{业务}Exists(Long id) {
if ({业务}Mapper.selectById(id) == null) {
throw exception({业务大写}_NOT_EXISTS);
}
}
private void validateBusinessCodeUnique(Long id, String businessCode) {
{业务}DO {业务} = {业务}Mapper.selectByBusinessCode(businessCode);
if ({业务} == null) {
return;
}
if (id == null) {
throw exception({业务大写}_CODE_DUPLICATE);
}
if (!{业务}.getId().equals(id)) {
throw exception({业务大写}_CODE_DUPLICATE);
}
}
}
```
### 2.7 创建 Controller 层
文件位置:`src/main/java/cn/iocoder/lyzsys/module/{模块名}/controller/admin/{业务}/{业务}Controller.java`
参考 `ProjectController.java`
```java
package cn.iocoder.lyzsys.module.{模块名}.controller.admin.{业务};
import cn.iocoder.lyzsys.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.lyzsys.framework.common.pojo.CommonResult;
import cn.iocoder.lyzsys.framework.common.pojo.PageParam;
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils;
import cn.iocoder.lyzsys.framework.excel.core.util.ExcelUtils;
import cn.iocoder.lyzsys.module.{模块名}.controller.admin.{业务}.vo.{业务}PageReqVO;
import cn.iocoder.lyzsys.module.{模块名}.controller.admin.{业务}.vo.{业务}RespVO;
import cn.iocoder.lyzsys.module.{模块名}.controller.admin.{业务}.vo.{业务}SaveReqVO;
import cn.iocoder.lyzsys.module.{模块名}.dal.dataobject.{业务}.{业务}DO;
import cn.iocoder.lyzsys.module.{模块名}.service.{业务}.{业务}Service;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import static cn.iocoder.lyzsys.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.lyzsys.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - {业务名称}管理")
@RestController
@RequestMapping("/{模块名}/{业务}")
@Validated
public class {业务}Controller {
@Resource
private {业务}Service {业务}Service;
@PostMapping("/create")
@Operation(summary = "创建{业务}")
@PreAuthorize("@ss.hasPermission('{模块名}:{业务}:create')")
public CommonResult<Long> create{业务}(@Valid @RequestBody {业务}SaveReqVO createReqVO) {
Long id = {业务}Service.create{业务}(createReqVO);
return success(id);
}
@PutMapping("/update")
@Operation(summary = "修改{业务}")
@PreAuthorize("@ss.hasPermission('{模块名}:{业务}:update')")
public CommonResult<Boolean> update{业务}(@Valid @RequestBody {业务}SaveReqVO updateReqVO) {
{业务}Service.update{业务}(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除{业务}")
@Parameter(name = "id", description = "编号", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('{模块名}:{业务}:delete')")
public CommonResult<Boolean> delete{业务}(@RequestParam("id") Long id) {
{业务}Service.delete{业务}(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Operation(summary = "批量删除{业务}")
@Parameter(name = "ids", description = "编号列表", required = true)
@PreAuthorize("@ss.hasPermission('{模块名}:{业务}:delete')")
public CommonResult<Boolean> delete{业务}List(@RequestParam("ids") List<Long> ids) {
{业务}Service.delete{业务}List(ids);
return success(true);
}
@GetMapping("/page")
@Operation(summary = "获得{业务}分页列表")
@PreAuthorize("@ss.hasPermission('{模块名}:{业务}:query')")
public CommonResult<PageResult<{业务}RespVO>> get{业务}Page(@Valid {业务}PageReqVO pageReqVO) {
PageResult<{业务}DO> pageResult = {业务}Service.get{业务}Page(pageReqVO);
return success(BeanUtils.toBean(pageResult, {业务}RespVO.class));
}
@GetMapping("/get")
@Operation(summary = "获得{业务}详情")
@Parameter(name = "id", description = "编号", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('{模块名}:{业务}:query')")
public CommonResult<{业务}RespVO> get{业务}(@RequestParam("id") Long id) {
{业务}DO {业务} = {业务}Service.get{业务}(id);
return success(BeanUtils.toBean({业务}, {业务}RespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出{业务}")
@PreAuthorize("@ss.hasPermission('{模块名}:{业务}:export')")
@ApiAccessLog(operateType = EXPORT)
public void export{业务}(HttpServletResponse response, @Valid {业务}PageReqVO exportReqVO) throws IOException {
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<{业务}DO> list = {业务}Service.get{业务}Page(exportReqVO).getList();
ExcelUtils.write(response, "{业务名称}.xls", "数据", {业务}RespVO.class,
BeanUtils.toBean(list, {业务}RespVO.class));
}
}
```
### 2.8 创建错误码常量
文件位置:`src/main/java/cn/iocoder/lyzsys/module/{模块名}/enums/ErrorCodeConstants.java`
```java
package cn.iocoder.lyzsys.module.{模块名}.enums;
import cn.iocoder.lyzsys.framework.common.exception.ErrorCode;
/**
* {模块名称} 错误码枚举类
*
* {模块名} 模块,使用 1-0XX-000-000 段
*/
public interface ErrorCodeConstants {
// ========== {业务名称}模块 1-0XX-001-000 ==========
ErrorCode {业务大写}_NOT_EXISTS = new ErrorCode(1_0XX_001_000, "{业务}不存在");
ErrorCode {业务大写}_CODE_DUPLICATE = new ErrorCode(1_0XX_001_001, "已经存在该{业务}编号");
}
```
---
## 3. 创建前端页面
### 3.1 创建 API 接口
文件位置:`src/api/{模块名}/{业务}/index.ts`
参考 `src/api/demo/project/index.ts`
```typescript
import request from '@/config/axios'
export interface {业务}VO {
id: number
businessName: string
businessCode: string
createTime: Date
}
export interface {业务}PageReqVO extends PageParam {
businessName?: string
businessCode?: string
createTime?: Date[]
}
// 查询分页
export const get{业务}Page = (params: {业务}PageReqVO) => {
return request.get({ url: '/{模块名}/{业务}/page', params })
}
// 查询详情
export const get{业务} = (id: number) => {
return request.get({ url: '/{模块名}/{业务}/get', params: { id } })
}
// 新增
export const create{业务} = (data: {业务}VO) => {
return request.post({ url: '/{模块名}/{业务}/create', data })
}
// 修改
export const update{业务} = (data: {业务}VO) => {
return request.put({ url: '/{模块名}/{业务}/update', data })
}
// 删除
export const delete{业务} = (id: number) => {
return request.delete({ url: '/{模块名}/{业务}/delete', params: { id } })
}
// 批量删除
export const delete{业务}List = (ids: number[]) => {
return request.delete({ url: '/{模块名}/{业务}/delete-list', params: { ids } })
}
// 导出
export const export{业务} = (params) => {
return request.download({ url: '/{模块名}/{业务}/export-excel', params })
}
```
### 3.2 创建列表页面
文件位置:`src/views/{模块名}/{业务}/index.vue`
参考 `src/views/demo/project/index.vue`(详细代码见 demo 模块)
**关键点**
- 使用 `defineOptions({ name: '{模块}{业务}' })`
- 搜索表单 + 列表表格 + 分页组件
- 权限控制:`v-hasPermi="['{模块名}:{业务}:操作']"`
### 3.3 创建表单组件
文件位置:`src/views/{模块名}/{业务}/{业务}Form.vue`
参考 `src/views/demo/project/ProjectForm.vue`(详细代码见 demo 模块)
**关键点**
- 使用 `defineOptions({ name: '{模块}{业务}Form' })`
- 使用 `defineExpose({ open })` 暴露打开方法
- 表单验证规则
- 提交成功后 `emit('success')`
---
## 4. 配置菜单权限
### 4.1 创建菜单 SQL
文件位置:`sql/mysql/{模块名}_menu.sql`
参考 `sql/mysql/demo_menu.sql`
```sql
-- 一级菜单
INSERT INTO `system_menu` (...) VALUES (
{ID}, '{一级菜单名}', '', 1, {排序}, 0,
'/{模块名}', '{图标}', NULL, NULL,
0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'
);
-- 二级菜单
INSERT INTO `system_menu` (...) VALUES (
{ID+1}, '{二级菜单名}', '', 2, 1, {一级菜单ID},
'{业务}', '{图标}', '{模块名}/{业务}/index', '{模块}{业务}',
0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'
);
-- 按钮权限:查询、新增、修改、删除、导出
INSERT INTO `system_menu` (...) VALUES
({ID+2}, '{业务}查询', '{模块名}:{业务}:query', 3, 1, {二级菜单ID}, ...), ({ID+3}, '{业务}新增', '{模块名}:{业务}:create', 3, 2, {二级菜单ID}, ...),
({ID+4}, '{业务}修改', '{模块名}:{业务}:update', 3, 3, {二级菜单ID}, ...),
({ID+5}, '{业务}删除', '{模块名}:{业务}:delete', 3, 4, {二级菜单ID}, ...),
({ID+6}, '{业务}导出', '{模块名}:{业务}:export', 3, 5, {二级菜单ID}, ...);
-- 调整系统管理和基础设施菜单排序
UPDATE `system_menu` SET `sort` = 98, `updater` = '1', `update_time` = NOW() WHERE `id` = 1 AND `deleted` = b'0';
UPDATE `system_menu` SET `sort` = 99, `updater` = '1', `update_time` = NOW() WHERE `id` = 2 AND `deleted` = b'0';
```
---
## 5. 完整示例
### 5.1 Demo 模块文件清单
以下是 `lyzsys-module-demo` 的完整文件结构,可作为新模块开发的参考:
```
lyzsys-module-demo/
├── pom.xml
└── src/main/java/cn/iocoder/lyzsys/module/demo/
├── controller/admin/project/
│ ├── ProjectController.java
│ └── vo/
│ ├── ProjectPageReqVO.java
│ ├── ProjectRespVO.java
│ └── ProjectSaveReqVO.java
├── service/project/
│ ├── ProjectService.java
│ └── ProjectServiceImpl.java
├── dal/
│ ├── dataobject/project/
│ │ └── ProjectDO.java
│ └── mysql/project/
│ └── ProjectMapper.java
└── enums/
└── ErrorCodeConstants.java
```
### 5.2 前端文件清单
```
src/
├── api/demo/project/
│ └── index.ts
└── views/demo/project/
├── index.vue
└── ProjectForm.vue
```
### 5.3 SQL 文件清单
```
sql/mysql/
├── demo_project.sql # 数据库表
└── demo_menu.sql # 菜单配置
```
---
## 6. 常见问题
### 6.1 Maven 编译错误
**问题**:找不到父 POM
**解决**:检查 parent 的 groupId、artifactId、version 是否正确
### 6.2 菜单不显示
**问题**:前端菜单不显示
**解决**
1. 确认已执行菜单 SQL
2. 检查当前用户角色权限
3. 清除浏览器缓存
### 6.3 接口 404
**问题**:前端调用接口返回 404
**解决**
1. 检查 Controller 的 @RequestMapping 路径
2. 确认模块已添加到 lyzsys-server/pom.xml
3. 重启后端服务
### 6.4 MyBatis Plus 字段映射错误
**问题**:字段映射错误,查询结果为 null
**解决**:检查字段命名是否遵循驼峰规范,确保第一个单词不是单字母
### 6.5 权限验证失败
**问题**:操作提示无权限
**解决**
1. 检查 @PreAuthorize 权限标识是否正确
2. 确认菜单 SQL 中的权限标识与代码一致
3. 给当前角色分配权限
---
## 7. 检查清单
开发完成后,请检查:
**后端**
- [ ] Maven 模块配置正确
- [ ] 数据库表创建成功
- [ ] 实体类字段遵循命名规范
- [ ] Mapper 接口方法正确
- [ ] VO 类字段完整
- [ ] Service 业务逻辑正确
- [ ] Controller 权限配置正确
- [ ] 错误码定义完整
**前端**
- [ ] API 接口定义正确
- [ ] 列表页面功能完整
- [ ] 表单组件验证规则正确
- [ ] 权限控制配置正确
**菜单**
- [ ] 一级菜单创建成功
- [ ] 二级菜单创建成功
- [ ] 按钮权限创建成功
- [ ] 菜单排序正确
**测试**
- [ ] 列表查询正常
- [ ] 新增功能正常
- [ ] 修改功能正常
- [ ] 删除功能正常
- [ ] 导出功能正常
- [ ] 权限控制正常
---
**文档版本**v1.0
**参考模块**lyzsys-module-demo
**最后更新**2024年
**维护者**:开发团队

647
doc/编码规范.md Normal file
View File

@@ -0,0 +1,647 @@
# Lyzsys 项目编码规范
## 目录
- [1. 命名规范](#1-命名规范)
- [2. 菜单结构规范](#2-菜单结构规范)
- [3. 代码结构规范](#3-代码结构规范)
- [4. 数据库设计规范](#4-数据库设计规范)
- [5. API 接口规范](#5-api-接口规范)
- [6. 前端开发规范](#6-前端开发规范)
---
## 1. 命名规范
### 1.1 驼峰命名规范
**核心原则**:所有字段名、类名、文件名、变量名的驼峰命名中,**第一个单词不能只有一个字母**。
#### 为什么要遵循这个规范?
1. **MyBatis Plus 兼容性**MyBatis Plus 在映射单字母开头的驼峰字段时可能出现问题
2. **代码可读性**:完整单词更具可读性,便于理解业务含义
3. **避免错误**:减少字段读取错误和调试困难
#### 正确示例 ✅
```java
// 字段命名
private String projectId; // 而不是 pId
private String projectName; // 而不是 pName
private String projectCode; // 而不是 pCode
private LocalDateTime establishDate; // 而不是 eDate
// 特殊情况:如果首字母缩写必须使用,请用完整单词
private String majorType; // 而不是 mType
private BigDecimal leaderRatio; // 而不是 lRatio
private Double kOneCoefficient; // 而不是 k1Coefficientk1 是业务术语)
```
#### 错误示例 ❌
```java
// 错误:单字母开头
private String pId; // ❌ 应该是 projectId
private String mType; // ❌ 应该是 majorType
private String lRatio; // ❌ 应该是 leaderRatio
private String k1Coefficient; // ❌ 应该是 kOneCoefficient
```
### 1.2 Java 类命名规范
#### 实体类DO
```java
// 格式:{业务名称}DO
ProjectDO.java // 项目实体
UserDO.java // 用户实体
OrderDO.java // 订单实体
```
#### VO 类
```java
// 分页查询请求
{业务名称}PageReqVO.java
ProjectPageReqVO.java
// 响应 VO
{业务名称}RespVO.java
ProjectRespVO.java
// 保存请求 VO新增和修改共用
{业务名称}SaveReqVO.java
ProjectSaveReqVO.java
// 简单响应 VO下拉选项等
{业务名称}SimpleRespVO.java
ProjectSimpleRespVO.java
```
#### Mapper 接口
```java
// 格式:{业务名称}Mapper
ProjectMapper.java
```
#### Service 层
```java
// 接口:{业务名称}Service
ProjectService.java
// 实现:{业务名称}ServiceImpl
ProjectServiceImpl.java
```
#### Controller 层
```java
// 格式:{业务名称}Controller
ProjectController.java
```
### 1.3 数据库命名规范
#### 表名
```sql
-- 格式:{模块前缀}_{业务名称}(全小写,下划线分隔)
demo_project -- demo 模块的项目表
system_user -- system 模块的用户表
infra_config -- infra 模块的配置表
```
#### 字段名
```sql
-- 使用下划线分隔的小写单词
project_name -- 项目名称(对应 Java 中的 projectName
project_code -- 项目编号
establish_date -- 立项时间
-- 标准字段(所有表必备)
id -- 主键
creator -- 创建者
create_time -- 创建时间
updater -- 更新者
update_time -- 更新时间
deleted -- 删除标记
tenant_id -- 租户ID多租户表必备
```
### 1.4 包名规范
```
cn.iocoder.lyzsys.module.{模块名}.{分层}
示例:
cn.iocoder.lyzsys.module.demo.controller.admin.project
cn.iocoder.lyzsys.module.demo.service.project
cn.iocoder.lyzsys.module.demo.dal.dataobject.project
cn.iocoder.lyzsys.module.demo.dal.mysql.project
```
### 1.5 前端命名规范
#### Vue 组件名
```javascript
// 页面组件defineOptions name
DemoProject // 项目列表页
DemoProjectForm // 项目表单组件
// 文件名kebab-case
index.vue // 列表页
ProjectForm.vue // 表单组件
```
#### API 文件
```typescript
// 文件路径
src/api/demo/project/index.ts
// 接口命名(驼峰)
export const getProjectPage = () => {}
export const createProject = () => {}
export const updateProject = () => {}
export const deleteProject = () => {}
```
---
## 2. 菜单结构规范
### 2.1 菜单层级规范
**原则**:业务功能菜单优先展示,系统管理功能放在最后。
#### 推荐的菜单顺序
```
1. 业务菜单sort: 1-90
├── 项目管理一级菜单sort: 1
│ └── 项目列表二级菜单sort: 1
├── 客户管理一级菜单sort: 2
│ └── 客户列表二级菜单sort: 1
└── ...
98. 系统管理sort: 98
├── 用户管理
├── 角色管理
├── 菜单管理
└── ...
99. 基础设施sort: 99
├── 代码生成
├── 文件管理
└── ...
```
### 2.2 菜单配置规范
#### 一级菜单(目录)
```sql
INSERT INTO `system_menu` (
`id`, `name`, `permission`, `type`, `sort`, `parent_id`,
`path`, `icon`, `component`, `component_name`,
`status`, `visible`, `keep_alive`, `always_show`,
`creator`, `create_time`, `updater`, `update_time`, `deleted`
) VALUES (
3000, -- ID建议按模块分配段如 demo: 3000-3999
'项目管理', -- 名称
'', -- 权限标识(一级菜单为空)
1, -- 类型1=目录
1, -- 排序(业务菜单建议 1-90
0, -- 父级ID0表示顶级
'/demo', -- 路由路径
'ep:files', -- 图标
NULL, -- 组件路径目录为NULL
NULL, -- 组件名称目录为NULL
0, -- 状态0=正常
b'1', -- 是否可见
b'1', -- 是否缓存
b'1', -- 是否总是显示
'1', -- 创建者
NOW(), -- 创建时间
'1', -- 更新者
NOW(), -- 更新时间
b'0' -- 是否删除
);
```
#### 二级菜单(菜单页面)
```sql
INSERT INTO `system_menu` (...) VALUES (
3001, -- ID
'项目列表', -- 名称
'', -- 权限标识(菜单页面为空)
2, -- 类型2=菜单
1, -- 排序
3000, -- 父级ID对应一级菜单ID
'project', -- 路由路径
'ep:document', -- 图标
'demo/project/index', -- 组件路径
'DemoProject', -- 组件名称(对应 Vue defineOptions name
0, b'1', b'1', b'1',
'1', NOW(), '1', NOW(), b'0'
);
```
#### 三级菜单(按钮权限)
```sql
-- 查询权限
INSERT INTO `system_menu` (...) VALUES (
3002, '项目查询', 'demo:project:query', 3, 1, 3001,
'', '', '', NULL, 0, b'1', b'1', b'1',
'1', NOW(), '1', NOW(), b'0'
);
-- 新增权限
INSERT INTO `system_menu` (...) VALUES (
3003, '项目新增', 'demo:project:create', 3, 2, 3001,
'', '', '', NULL, 0, b'1', b'1', b'1',
'1', NOW(), '1', NOW(), b'0'
);
-- 修改权限
INSERT INTO `system_menu` (...) VALUES (
3004, '项目修改', 'demo:project:update', 3, 3, 3001,
'', '', '', NULL, 0, b'1', b'1', b'1',
'1', NOW(), '1', NOW(), b'0'
);
-- 删除权限
INSERT INTO `system_menu` (...) VALUES (
3005, '项目删除', 'demo:project:delete', 3, 4, 3001,
'', '', '', NULL, 0, b'1', b'1', b'1',
'1', NOW(), '1', NOW(), b'0'
);
-- 导出权限
INSERT INTO `system_menu` (...) VALUES (
3006, '项目导出', 'demo:project:export', 3, 5, 3001,
'', '', '', NULL, 0, b'1', b'1', b'1',
'1', NOW(), '1', NOW(), b'0'
);
```
### 2.3 权限标识规范
```
格式:{模块名}:{业务}:{操作}
示例:
demo:project:query -- demo模块项目业务查询操作
demo:project:create -- demo模块项目业务新增操作
demo:project:update -- demo模块项目业务修改操作
demo:project:delete -- demo模块项目业务删除操作
demo:project:export -- demo模块项目业务导出操作
```
### 2.4 菜单 ID 分配规范
为避免 ID 冲突,建议按模块分配 ID 段:
| 模块 | ID 范围 | 说明 |
|-----|---------|------|
| system | 1-999 | 系统核心模块 |
| infra | 1000-1999 | 基础设施模块 |
| member | 2000-2999 | 会员模块 |
| demo | 3000-3999 | Demo 示例模块 |
| bpm | 4000-4999 | 工作流模块 |
| mall | 5000-5999 | 商城模块 |
| ... | ... | 其他业务模块 |
---
## 3. 代码结构规范
### 3.1 模块目录结构
```
lyzsys-module-{模块名}/
├── pom.xml # Maven 配置
└── src/main/java/cn/iocoder/lyzsys/module/{模块名}/
├── controller/ # 控制器层
│ └── admin/ # 管理后台接口
│ └── {业务}/ # 业务模块
│ ├── {业务}Controller.java # 控制器
│ └── vo/ # VO 对象
│ ├── {业务}PageReqVO.java # 分页请求
│ ├── {业务}RespVO.java # 响应 VO
│ └── {业务}SaveReqVO.java # 保存请求
├── service/ # 服务层
│ └── {业务}/
│ ├── {业务}Service.java # 服务接口
│ └── {业务}ServiceImpl.java # 服务实现
├── dal/ # 数据访问层
│ ├── dataobject/ # 数据对象
│ │ └── {业务}/
│ │ └── {业务}DO.java # 实体类
│ └── mysql/ # Mapper 接口
│ └── {业务}/
│ └── {业务}Mapper.java # Mapper
├── convert/ # 对象转换(可选)
│ └── {业务}Convert.java
├── enums/ # 枚举类
│ └── ErrorCodeConstants.java # 错误码
└── api/ # 对外 API可选
└── {业务}Api.java
```
### 3.2 分层职责
#### Controller 层
- 接收 HTTP 请求
- 参数校验(使用 @Valid
- 权限控制(使用 @PreAuthorize
- 调用 Service 层
- 返回统一响应
#### Service 层
- 业务逻辑处理
- 数据校验
- 事务控制
- 调用 Mapper 层
#### DAL 层
- 数据访问
- SQL 操作
- 不包含业务逻辑
---
## 4. 数据库设计规范
### 4.1 表设计规范
#### 标准字段(所有表必备)
```sql
CREATE TABLE `{表名}` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
-- 业务字段
...
-- 标准字段
`creator` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='表注释';
```
#### 多租户表额外字段
```sql
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
```
### 4.2 字段类型规范
| Java 类型 | MySQL 类型 | 说明 |
|-----------|-----------|------|
| Long | bigint | ID、数量等 |
| String | varchar | 字符串 |
| Integer | int | 状态、类型等 |
| LocalDateTime | datetime | 日期时间 |
| LocalDate | date | 日期 |
| BigDecimal | decimal | 金额、精确数值 |
| Boolean | bit(1) | 布尔值 |
### 4.3 索引规范
```sql
-- 主键索引
PRIMARY KEY (`id`)
-- 唯一索引(业务唯一字段)
UNIQUE KEY `uk_project_code` (`project_code`)
-- 普通索引(常用查询字段)
KEY `idx_project_name` (`project_name`)
-- 组合索引(多字段联合查询)
KEY `idx_tenant_status` (`tenant_id`, `status`)
```
---
## 5. API 接口规范
### 5.1 RESTful 风格
| 操作 | HTTP 方法 | URL | 说明 |
|-----|----------|-----|------|
| 查询列表 | GET | /{模块}/{业务}/page | 分页查询 |
| 查询详情 | GET | /{模块}/{业务}/get | 根据ID查询 |
| 新增 | POST | /{模块}/{业务}/create | 创建 |
| 修改 | PUT | /{模块}/{业务}/update | 更新 |
| 删除 | DELETE | /{模块}/{业务}/delete | 删除 |
| 批量删除 | DELETE | /{模块}/{业务}/delete-list | 批量删除 |
| 导出 | GET | /{模块}/{业务}/export-excel | 导出Excel |
### 5.2 统一响应格式
```java
// 成功响应
{
"code": 0,
"data": {...},
"msg": ""
}
// 失败响应
{
"code": 1001,
"data": null,
"msg": "错误信息"
}
// 分页响应
{
"code": 0,
"data": {
"list": [...],
"total": 100
},
"msg": ""
}
```
### 5.3 接口注解规范
```java
@Tag(name = "管理后台 - 项目管理")
@RestController
@RequestMapping("/demo/project")
@Validated
public class ProjectController {
@PostMapping("/create")
@Operation(summary = "创建项目")
@PreAuthorize("@ss.hasPermission('demo:project:create')")
public CommonResult<Long> createProject(@Valid @RequestBody ProjectSaveReqVO createReqVO) {
// ...
}
}
```
---
## 6. 前端开发规范
### 6.1 目录结构
```
src/
├── api/ # API 接口
│ └── {模块}/
│ └── {业务}/
│ └── index.ts # API 定义
└── views/ # 页面
└── {模块}/
└── {业务}/
├── index.vue # 列表页
└── {业务}Form.vue # 表单组件
```
### 6.2 组件规范
#### 列表页面
```vue
<template>
<!-- 搜索工作栏 -->
<ContentWrap>
<el-form>...</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table>...</el-table>
<Pagination />
</ContentWrap>
<!-- 表单弹窗 -->
<{业务}Form ref="formRef" @success="getList" />
</template>
<script lang="ts" setup>
defineOptions({ name: '{模块}{业务}' })
// ...
</script>
```
#### 表单组件
```vue
<template>
<Dialog v-model="dialogVisible" :title="dialogTitle">
<el-form>...</el-form>
<template #footer>
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
defineOptions({ name: '{模块}{业务}Form' })
defineExpose({ open })
// ...
</script>
```
### 6.3 权限控制
```vue
<!-- 按钮权限 -->
<el-button
v-hasPermi="['demo:project:create']"
type="primary"
@click="openForm('create')"
>
新增
</el-button>
<!-- 路由权限在菜单配置中控制 -->
```
---
## 7. 最佳实践
### 7.1 代码注释
```java
/**
* 项目管理 Service 接口
*
* @author lyzsys
*/
public interface ProjectService {
/**
* 创建项目
*
* @param createReqVO 项目信息
* @return 项目编号
*/
Long createProject(ProjectSaveReqVO createReqVO);
}
```
### 7.2 异常处理
```java
// 使用统一的业务异常
throw exception(PROJECT_NOT_EXISTS);
throw exception(PROJECT_CODE_DUPLICATE);
```
### 7.3 日志规范
```java
@Slf4j
public class ProjectServiceImpl implements ProjectService {
@Override
public Long createProject(ProjectSaveReqVO createReqVO) {
log.info("[createProject] 创建项目,项目编号:{}", createReqVO.getProjectCode());
// ...
}
}
```
---
## 8. 检查清单
在提交代码前,请检查:
- [ ] 所有字段命名遵循驼峰规范,无单字母开头
- [ ] 菜单配置完整(一级、二级、按钮权限)
- [ ] 业务菜单排序在系统管理之前
- [ ] 权限标识格式正确({模块}:{业务}:{操作}
- [ ] 数据库表包含标准字段
- [ ] API 接口遵循 RESTful 风格
- [ ] 代码注释完整
- [ ] 前端权限控制正确
---
**文档版本**v1.0
**最后更新**2024年
**维护者**:开发团队