From cd5093d60235b66e7b253e8dda638b34ae8df49d Mon Sep 17 00:00:00 2001 From: lpd <1337706942@qq.com> Date: Thu, 22 Jan 2026 17:56:09 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0demo=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E3=80=82=E4=BC=98=E5=8C=96=E6=8F=90=E7=A4=BA=E8=AF=8D=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/README.md | 248 +++++ doc/demo_menu.sql | 43 + doc/demo_project.sql | 25 + doc/demo_部署说明.md | 259 +++++ doc/后端开发文档.md | 216 +++- doc/新模块开发指南.md | 931 ++++++++++++++++++ doc/编码规范.md | 647 ++++++++++++ lyzsys-module-demo/pom.xml | 70 ++ .../admin/project/ProjectController.java | 102 ++ .../admin/project/vo/ProjectPageReqVO.java | 28 + .../admin/project/vo/ProjectRespVO.java | 35 + .../admin/project/vo/ProjectSaveReqVO.java | 34 + .../dal/dataobject/project/ProjectDO.java | 44 + .../demo/dal/mysql/project/ProjectMapper.java | 30 + .../module/demo/enums/ErrorCodeConstants.java | 16 + .../demo/service/project/ProjectService.java | 62 ++ .../service/project/ProjectServiceImpl.java | 102 ++ lyzsys-server/pom.xml | 5 + lyzsys-ui/lyzsys-ui-admin-uniapp/README.md | 4 - lyzsys-ui/lyzsys-ui-admin-vben/README.md | 4 - lyzsys-ui/lyzsys-ui-admin-vue2/README.md | 4 - lyzsys-ui/lyzsys-ui-admin-vue3/README.md | 4 - lyzsys-ui/lyzsys-ui-mall-uniapp/README.md | 8 - pom.xml | 6 + 24 files changed, 2899 insertions(+), 28 deletions(-) create mode 100644 doc/README.md create mode 100644 doc/demo_menu.sql create mode 100644 doc/demo_project.sql create mode 100644 doc/demo_部署说明.md create mode 100644 doc/新模块开发指南.md create mode 100644 doc/编码规范.md create mode 100644 lyzsys-module-demo/pom.xml create mode 100644 lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/controller/admin/project/ProjectController.java create mode 100644 lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/controller/admin/project/vo/ProjectPageReqVO.java create mode 100644 lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/controller/admin/project/vo/ProjectRespVO.java create mode 100644 lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/controller/admin/project/vo/ProjectSaveReqVO.java create mode 100644 lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/dal/dataobject/project/ProjectDO.java create mode 100644 lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/dal/mysql/project/ProjectMapper.java create mode 100644 lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/enums/ErrorCodeConstants.java create mode 100644 lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/service/project/ProjectService.java create mode 100644 lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/service/project/ProjectServiceImpl.java delete mode 100644 lyzsys-ui/lyzsys-ui-admin-uniapp/README.md delete mode 100644 lyzsys-ui/lyzsys-ui-admin-vben/README.md delete mode 100644 lyzsys-ui/lyzsys-ui-admin-vue2/README.md delete mode 100644 lyzsys-ui/lyzsys-ui-admin-vue3/README.md delete mode 100644 lyzsys-ui/lyzsys-ui-mall-uniapp/README.md diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..5431975 --- /dev/null +++ b/doc/README.md @@ -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 diff --git a/doc/demo_menu.sql b/doc/demo_menu.sql new file mode 100644 index 0000000..18b59b8 --- /dev/null +++ b/doc/demo_menu.sql @@ -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'; diff --git a/doc/demo_project.sql b/doc/demo_project.sql new file mode 100644 index 0000000..72478b3 --- /dev/null +++ b/doc/demo_project.sql @@ -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; diff --git a/doc/demo_部署说明.md b/doc/demo_部署说明.md new file mode 100644 index 0000000..354b5c2 --- /dev/null +++ b/doc/demo_部署说明.md @@ -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 diff --git a/doc/后端开发文档.md b/doc/后端开发文档.md index 8fe083c..fd1c3c0 100644 --- a/doc/后端开发文档.md +++ b/doc/后端开发文档.md @@ -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 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: 如何自定义异常? diff --git a/doc/新模块开发指南.md b/doc/新模块开发指南.md new file mode 100644 index 0000000..95e58ba --- /dev/null +++ b/doc/新模块开发指南.md @@ -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 + + + + cn.iocoder.boot + lyzsys + ${revision} + + 4.0.0 + lyzsys-module-{模块名} + jar + + ${project.artifactId} + + {模块说明} + + + + + + cn.iocoder.boot + lyzsys-module-infra + ${revision} + + + + + cn.iocoder.boot + lyzsys-spring-boot-starter-biz-tenant + + + + + cn.iocoder.boot + lyzsys-spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-validation + + + + + cn.iocoder.boot + lyzsys-spring-boot-starter-mybatis + + + + cn.iocoder.boot + lyzsys-spring-boot-starter-redis + + + + + cn.iocoder.boot + lyzsys-spring-boot-starter-test + test + + + + + cn.iocoder.boot + lyzsys-spring-boot-starter-excel + + + + +``` + +#### 步骤 3:添加模块到父 pom.xml + +在 `lyzsys_backend/pom.xml` 中添加: + +```xml + + + lyzsys-module-system + lyzsys-module-infra + + lyzsys-module-{模块名} + +``` + +#### 步骤 4:添加模块到 server + +在 `lyzsys-server/pom.xml` 中添加依赖: + +```xml + + cn.iocoder.boot + lyzsys-module-{模块名} + ${revision} + +``` + +### 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 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 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 create{业务}(@Valid @RequestBody {业务}SaveReqVO createReqVO) { + Long id = {业务}Service.create{业务}(createReqVO); + return success(id); + } + + @PutMapping("/update") + @Operation(summary = "修改{业务}") + @PreAuthorize("@ss.hasPermission('{模块名}:{业务}:update')") + public CommonResult 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 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 delete{业务}List(@RequestParam("ids") List ids) { + {业务}Service.delete{业务}List(ids); + return success(true); + } + + @GetMapping("/page") + @Operation(summary = "获得{业务}分页列表") + @PreAuthorize("@ss.hasPermission('{模块名}:{业务}:query')") + public CommonResult> 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年 +**维护者**:开发团队 diff --git a/doc/编码规范.md b/doc/编码规范.md new file mode 100644 index 0000000..4aa088b --- /dev/null +++ b/doc/编码规范.md @@ -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; // 而不是 k1Coefficient(k1 是业务术语) +``` + +#### 错误示例 ❌ + +```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, -- 父级ID(0表示顶级) + '/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 createProject(@Valid @RequestBody ProjectSaveReqVO createReqVO) { + // ... + } +} +``` + +--- + +## 6. 前端开发规范 + +### 6.1 目录结构 + +``` +src/ +├── api/ # API 接口 +│ └── {模块}/ +│ └── {业务}/ +│ └── index.ts # API 定义 +└── views/ # 页面 + └── {模块}/ + └── {业务}/ + ├── index.vue # 列表页 + └── {业务}Form.vue # 表单组件 +``` + +### 6.2 组件规范 + +#### 列表页面 + +```vue + + + +``` + +#### 表单组件 + +```vue + + + +``` + +### 6.3 权限控制 + +```vue + + + 新增 + + + +``` + +--- + +## 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年 +**维护者**:开发团队 diff --git a/lyzsys-module-demo/pom.xml b/lyzsys-module-demo/pom.xml new file mode 100644 index 0000000..db73a90 --- /dev/null +++ b/lyzsys-module-demo/pom.xml @@ -0,0 +1,70 @@ + + + + cn.iocoder.boot + lyzsys + ${revision} + + 4.0.0 + lyzsys-module-demo + jar + + ${project.artifactId} + + demo 模块,演示业务功能。 + 例如:项目管理等 + + + + + cn.iocoder.boot + lyzsys-module-infra + ${revision} + + + + + cn.iocoder.boot + lyzsys-spring-boot-starter-biz-tenant + + + + + cn.iocoder.boot + lyzsys-spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-validation + + + + + cn.iocoder.boot + lyzsys-spring-boot-starter-mybatis + + + + cn.iocoder.boot + lyzsys-spring-boot-starter-redis + + + + + cn.iocoder.boot + lyzsys-spring-boot-starter-test + test + + + + + cn.iocoder.boot + lyzsys-spring-boot-starter-excel + + + + + diff --git a/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/controller/admin/project/ProjectController.java b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/controller/admin/project/ProjectController.java new file mode 100644 index 0000000..c662151 --- /dev/null +++ b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/controller/admin/project/ProjectController.java @@ -0,0 +1,102 @@ +package cn.iocoder.lyzsys.module.demo.controller.admin.project; + +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.demo.controller.admin.project.vo.ProjectPageReqVO; +import cn.iocoder.lyzsys.module.demo.controller.admin.project.vo.ProjectRespVO; +import cn.iocoder.lyzsys.module.demo.controller.admin.project.vo.ProjectSaveReqVO; +import cn.iocoder.lyzsys.module.demo.dal.dataobject.project.ProjectDO; +import cn.iocoder.lyzsys.module.demo.service.project.ProjectService; +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("/demo/project") +@Validated +public class ProjectController { + + @Resource + private ProjectService projectService; + + @PostMapping("/create") + @Operation(summary = "创建项目") + @PreAuthorize("@ss.hasPermission('demo:project:create')") + public CommonResult createProject(@Valid @RequestBody ProjectSaveReqVO createReqVO) { + Long projectId = projectService.createProject(createReqVO); + return success(projectId); + } + + @PutMapping("/update") + @Operation(summary = "修改项目") + @PreAuthorize("@ss.hasPermission('demo:project:update')") + public CommonResult updateProject(@Valid @RequestBody ProjectSaveReqVO updateReqVO) { + projectService.updateProject(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除项目") + @Parameter(name = "id", description = "编号", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('demo:project:delete')") + public CommonResult deleteProject(@RequestParam("id") Long id) { + projectService.deleteProject(id); + return success(true); + } + + @DeleteMapping("/delete-list") + @Operation(summary = "批量删除项目") + @Parameter(name = "ids", description = "编号列表", required = true) + @PreAuthorize("@ss.hasPermission('demo:project:delete')") + public CommonResult deleteProjectList(@RequestParam("ids") List ids) { + projectService.deleteProjectList(ids); + return success(true); + } + + @GetMapping("/page") + @Operation(summary = "获得项目分页列表") + @PreAuthorize("@ss.hasPermission('demo:project:query')") + public CommonResult> getProjectPage(@Valid ProjectPageReqVO pageReqVO) { + PageResult pageResult = projectService.getProjectPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, ProjectRespVO.class)); + } + + @GetMapping("/get") + @Operation(summary = "获得项目详情") + @Parameter(name = "id", description = "编号", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('demo:project:query')") + public CommonResult getProject(@RequestParam("id") Long id) { + ProjectDO project = projectService.getProject(id); + return success(BeanUtils.toBean(project, ProjectRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出项目") + @PreAuthorize("@ss.hasPermission('demo:project:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportProject(HttpServletResponse response, @Valid ProjectPageReqVO exportReqVO) throws IOException { + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = projectService.getProjectPage(exportReqVO).getList(); + // 导出 + ExcelUtils.write(response, "项目管理.xls", "数据", ProjectRespVO.class, + BeanUtils.toBean(list, ProjectRespVO.class)); + } + +} diff --git a/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/controller/admin/project/vo/ProjectPageReqVO.java b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/controller/admin/project/vo/ProjectPageReqVO.java new file mode 100644 index 0000000..61430a7 --- /dev/null +++ b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/controller/admin/project/vo/ProjectPageReqVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.lyzsys.module.demo.controller.admin.project.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 ProjectPageReqVO extends PageParam { + + @Schema(description = "项目名称,模糊匹配", example = "示例项目") + private String projectName; + + @Schema(description = "项目编号,模糊匹配", example = "PROJ-2024-001") + private String projectCode; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "立项时间") + private LocalDateTime[] establishDate; + +} diff --git a/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/controller/admin/project/vo/ProjectRespVO.java b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/controller/admin/project/vo/ProjectRespVO.java new file mode 100644 index 0000000..e22de31 --- /dev/null +++ b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/controller/admin/project/vo/ProjectRespVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.lyzsys.module.demo.controller.admin.project.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 ProjectRespVO { + + @Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("项目ID") + private Long id; + + @Schema(description = "项目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "示例项目") + @ExcelProperty("项目名称") + private String projectName; + + @Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "PROJ-2024-001") + @ExcelProperty("项目编号") + private String projectCode; + + @Schema(description = "立项时间", example = "2024-01-01 00:00:00") + @ExcelProperty("立项时间") + private LocalDateTime establishDate; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} diff --git a/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/controller/admin/project/vo/ProjectSaveReqVO.java b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/controller/admin/project/vo/ProjectSaveReqVO.java new file mode 100644 index 0000000..ff7d879 --- /dev/null +++ b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/controller/admin/project/vo/ProjectSaveReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.lyzsys.module.demo.controller.admin.project.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +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 +public class ProjectSaveReqVO { + + @Schema(description = "项目ID", example = "1") + private Long id; + + @Schema(description = "项目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "示例项目") + @NotBlank(message = "项目名称不能为空") + @Size(max = 100, message = "项目名称长度不能超过100个字符") + private String projectName; + + @Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "PROJ-2024-001") + @NotBlank(message = "项目编号不能为空") + @Size(max = 50, message = "项目编号长度不能超过50个字符") + private String projectCode; + + @Schema(description = "立项时间", example = "2024-01-01 00:00:00") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime establishDate; + +} diff --git a/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/dal/dataobject/project/ProjectDO.java b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/dal/dataobject/project/ProjectDO.java new file mode 100644 index 0000000..1cb84d4 --- /dev/null +++ b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/dal/dataobject/project/ProjectDO.java @@ -0,0 +1,44 @@ +package cn.iocoder.lyzsys.module.demo.dal.dataobject.project; + +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 lyzsys + */ +@TableName("demo_project") +@KeySequence("demo_project_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class ProjectDO extends BaseDO { + + /** + * 项目ID + */ + @TableId + private Long id; + + /** + * 项目名称 + */ + private String projectName; + + /** + * 项目编号 + */ + private String projectCode; + + /** + * 立项时间 + */ + private LocalDateTime establishDate; + +} diff --git a/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/dal/mysql/project/ProjectMapper.java b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/dal/mysql/project/ProjectMapper.java new file mode 100644 index 0000000..fe5ad3f --- /dev/null +++ b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/dal/mysql/project/ProjectMapper.java @@ -0,0 +1,30 @@ +package cn.iocoder.lyzsys.module.demo.dal.mysql.project; + +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.demo.controller.admin.project.vo.ProjectPageReqVO; +import cn.iocoder.lyzsys.module.demo.dal.dataobject.project.ProjectDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 项目管理 Mapper + * + * @author lyzsys + */ +@Mapper +public interface ProjectMapper extends BaseMapperX { + + default PageResult selectPage(ProjectPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(ProjectDO::getProjectName, reqVO.getProjectName()) + .likeIfPresent(ProjectDO::getProjectCode, reqVO.getProjectCode()) + .betweenIfPresent(ProjectDO::getEstablishDate, reqVO.getEstablishDate()) + .orderByDesc(ProjectDO::getId)); + } + + default ProjectDO selectByProjectCode(String projectCode) { + return selectOne(ProjectDO::getProjectCode, projectCode); + } + +} diff --git a/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/enums/ErrorCodeConstants.java b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/enums/ErrorCodeConstants.java new file mode 100644 index 0000000..f3ef318 --- /dev/null +++ b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/enums/ErrorCodeConstants.java @@ -0,0 +1,16 @@ +package cn.iocoder.lyzsys.module.demo.enums; + +import cn.iocoder.lyzsys.framework.common.exception.ErrorCode; + +/** + * Demo 错误码枚举类 + * + * demo 模块,使用 1-010-000-000 段 + */ +public interface ErrorCodeConstants { + + // ========== 项目管理模块 1-010-001-000 ========== + ErrorCode PROJECT_NOT_EXISTS = new ErrorCode(1_010_001_000, "项目不存在"); + ErrorCode PROJECT_CODE_DUPLICATE = new ErrorCode(1_010_001_001, "已经存在该项目编号"); + +} diff --git a/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/service/project/ProjectService.java b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/service/project/ProjectService.java new file mode 100644 index 0000000..72acb6c --- /dev/null +++ b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/service/project/ProjectService.java @@ -0,0 +1,62 @@ +package cn.iocoder.lyzsys.module.demo.service.project; + +import cn.iocoder.lyzsys.framework.common.pojo.PageResult; +import cn.iocoder.lyzsys.module.demo.controller.admin.project.vo.ProjectPageReqVO; +import cn.iocoder.lyzsys.module.demo.controller.admin.project.vo.ProjectSaveReqVO; +import cn.iocoder.lyzsys.module.demo.dal.dataobject.project.ProjectDO; + +import java.util.List; + +/** + * 项目管理 Service 接口 + * + * @author lyzsys + */ +public interface ProjectService { + + /** + * 创建项目 + * + * @param createReqVO 项目信息 + * @return 项目编号 + */ + Long createProject(ProjectSaveReqVO createReqVO); + + /** + * 更新项目 + * + * @param updateReqVO 项目信息 + */ + void updateProject(ProjectSaveReqVO updateReqVO); + + /** + * 删除项目 + * + * @param id 项目编号 + */ + void deleteProject(Long id); + + /** + * 批量删除项目 + * + * @param ids 项目编号列表 + */ + void deleteProjectList(List ids); + + /** + * 获得项目分页列表 + * + * @param pageReqVO 分页请求 + * @return 项目分页列表 + */ + PageResult getProjectPage(ProjectPageReqVO pageReqVO); + + /** + * 获得项目详情 + * + * @param id 项目编号 + * @return 项目详情 + */ + ProjectDO getProject(Long id); + +} diff --git a/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/service/project/ProjectServiceImpl.java b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/service/project/ProjectServiceImpl.java new file mode 100644 index 0000000..b21f501 --- /dev/null +++ b/lyzsys-module-demo/src/main/java/cn/iocoder/lyzsys/module/demo/service/project/ProjectServiceImpl.java @@ -0,0 +1,102 @@ +package cn.iocoder.lyzsys.module.demo.service.project; + +import cn.iocoder.lyzsys.framework.common.pojo.PageResult; +import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils; +import cn.iocoder.lyzsys.module.demo.controller.admin.project.vo.ProjectPageReqVO; +import cn.iocoder.lyzsys.module.demo.controller.admin.project.vo.ProjectSaveReqVO; +import cn.iocoder.lyzsys.module.demo.dal.dataobject.project.ProjectDO; +import cn.iocoder.lyzsys.module.demo.dal.mysql.project.ProjectMapper; +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.demo.enums.ErrorCodeConstants.*; + +/** + * 项目管理 Service 实现类 + * + * @author lyzsys + */ +@Service +@Validated +public class ProjectServiceImpl implements ProjectService { + + @Resource + private ProjectMapper projectMapper; + + @Override + public Long createProject(ProjectSaveReqVO createReqVO) { + // 校验项目编号的唯一性 + validateProjectCodeUnique(null, createReqVO.getProjectCode()); + + // 插入项目 + ProjectDO project = BeanUtils.toBean(createReqVO, ProjectDO.class); + projectMapper.insert(project); + return project.getId(); + } + + @Override + public void updateProject(ProjectSaveReqVO updateReqVO) { + // 校验项目存在 + validateProjectExists(updateReqVO.getId()); + // 校验项目编号的唯一性 + validateProjectCodeUnique(updateReqVO.getId(), updateReqVO.getProjectCode()); + + // 更新项目 + ProjectDO updateObj = BeanUtils.toBean(updateReqVO, ProjectDO.class); + projectMapper.updateById(updateObj); + } + + @Override + public void deleteProject(Long id) { + // 校验项目存在 + validateProjectExists(id); + // 删除项目 + projectMapper.deleteById(id); + } + + @Override + public void deleteProjectList(List ids) { + // 校验项目存在 + ids.forEach(this::validateProjectExists); + // 批量删除 + projectMapper.deleteBatchIds(ids); + } + + @Override + public PageResult getProjectPage(ProjectPageReqVO pageReqVO) { + return projectMapper.selectPage(pageReqVO); + } + + @Override + public ProjectDO getProject(Long id) { + return projectMapper.selectById(id); + } + + // ==================== 校验方法 ==================== + + private void validateProjectExists(Long id) { + if (projectMapper.selectById(id) == null) { + throw exception(PROJECT_NOT_EXISTS); + } + } + + private void validateProjectCodeUnique(Long id, String projectCode) { + ProjectDO project = projectMapper.selectByProjectCode(projectCode); + if (project == null) { + return; + } + // 如果 id 为空,说明是新增,项目编号已存在则报错 + if (id == null) { + throw exception(PROJECT_CODE_DUPLICATE); + } + // 如果有 id,说明是修改,判断是否是自己 + if (!project.getId().equals(id)) { + throw exception(PROJECT_CODE_DUPLICATE); + } + } + +} diff --git a/lyzsys-server/pom.xml b/lyzsys-server/pom.xml index bb902a6..92ce35f 100644 --- a/lyzsys-server/pom.xml +++ b/lyzsys-server/pom.xml @@ -31,6 +31,11 @@ lyzsys-module-infra ${revision} + + cn.iocoder.boot + lyzsys-module-demo + ${revision} + diff --git a/lyzsys-ui/lyzsys-ui-admin-uniapp/README.md b/lyzsys-ui/lyzsys-ui-admin-uniapp/README.md deleted file mode 100644 index 9ba0900..0000000 --- a/lyzsys-ui/lyzsys-ui-admin-uniapp/README.md +++ /dev/null @@ -1,4 +0,0 @@ -基于 Vue + uni-app 实现的管理后台。仓库地址: - -* Gitee: -* GitHub: diff --git a/lyzsys-ui/lyzsys-ui-admin-vben/README.md b/lyzsys-ui/lyzsys-ui-admin-vben/README.md deleted file mode 100644 index 4900d63..0000000 --- a/lyzsys-ui/lyzsys-ui-admin-vben/README.md +++ /dev/null @@ -1,4 +0,0 @@ -基于 Vue3 + vben(ant-design-vue) 实现的管理后台。仓库地址: - -* Gitee: -* GitHub: diff --git a/lyzsys-ui/lyzsys-ui-admin-vue2/README.md b/lyzsys-ui/lyzsys-ui-admin-vue2/README.md deleted file mode 100644 index 7aca305..0000000 --- a/lyzsys-ui/lyzsys-ui-admin-vue2/README.md +++ /dev/null @@ -1,4 +0,0 @@ -基于 Vue2 + element-ui 实现的管理后台。仓库地址: - -* Gitee: -* GitHub: diff --git a/lyzsys-ui/lyzsys-ui-admin-vue3/README.md b/lyzsys-ui/lyzsys-ui-admin-vue3/README.md deleted file mode 100644 index 312e133..0000000 --- a/lyzsys-ui/lyzsys-ui-admin-vue3/README.md +++ /dev/null @@ -1,4 +0,0 @@ -基于 Vue3 + element-plus 实现的管理后台。仓库地址: - -* Gitee: -* GitHub: diff --git a/lyzsys-ui/lyzsys-ui-mall-uniapp/README.md b/lyzsys-ui/lyzsys-ui-mall-uniapp/README.md deleted file mode 100644 index b9c66b4..0000000 --- a/lyzsys-ui/lyzsys-ui-mall-uniapp/README.md +++ /dev/null @@ -1,8 +0,0 @@ -仓库地址: - -* Gitee: -* GitHub: - -功能列表: -* 基于 uniapp 开发,支持微信小程序、微信公众号、H5 移动端,未来会支持支付宝小程序、抖音小程序等 -* 支持 SaaS 多租户,可满足商品、订单、支付、会员、优惠券、秒杀、拼团、砍价、分销、积分等多种经营需求 diff --git a/pom.xml b/pom.xml index 0ae3f05..50791ef 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,8 @@ lyzsys-module-system lyzsys-module-infra + + lyzsys-module-demo @@ -72,6 +74,9 @@ org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin.version} + + true + @@ -80,6 +85,7 @@ maven-compiler-plugin ${maven-compiler-plugin.version} + true org.springframework.boot