【至尊智能家居】
主要内容
- 项目开发过程简介
- 项目功能分析
- 数据建模&UML 建模
- 基础环境搭建
一、 项目开发过程简介
1 项目的核心是什么?
项目不是简单的 CRUD。不是数据的增删改查。一个项目的实质是业务的实现。不同 的行业、环境、项目,会有不同的业务需求,根据具体的业务需求,完成对应的应用开发, 称为项目开发。
数据的变化是项目的底层支撑,非常重要。但是数据的操作基本是固定的。只要有一定 的开发经验,对数据的 CRUD 操作,就没有太大的问题。
业务实现是项目的精华核心。每个业务,都和行业、环境、情景、时间有关,任何一个 条件的变化,都会导致业务的实现变化。对于程序员来说,积累项目经验,就是积累业务经 验。如:银行、电信、电商、交友社区等不同的行业,登录业务差别的非常大。银行要求必 须有足够的安全,网银需要有 U 盾、电话银行必须绑定手机号、ATM 必须使用卡片或手机预约、柜台必须持有个人有效证件。电信只要手机号是对的就行。电商要求身份和凭证有效, 在支付的时候需要提供额外的支付凭证。交友平台只看身份和凭证,甚至可以游客访问,可 以游客交流,可以三方登录等。
2 项目的开发过程有哪些?
2.1 需求过程
由销售、售前、架构、项目经理、项目组长、开发代表、测试经理、测试组长、测试代 表等参与完成。
确认一个项目都要做什么功能实现的环节。
如:Ting 域主持人管理平台中需求如下:
用户登录: 用户登录、菜单查询
成员管理: 主持人管理-新增主持人、修改主持人信息、修改主持人权限、修改主持人 帐号状态、删除主持人、分页查询、多条件分页查询;婚庆公司管理-新增婚庆公司、修改 公司信息、修改公司状态、分页查询、多条件分页查询、删除公司;新人管理-分页查询、 多条件分页查询。
2.2 分析过程
参与人员:架构、项目经理、项目组长、开发代表(可能是全体开发人员)
就是分析实现一个具体的功能,需要如何去做。需要什么数据、需要如何操作数据、需 要如何管理事务、需要如何控制逻辑、需要根据什么确定返回结果等。
需要明确一个需求,在实现的过程中,都经过哪些接口、类型、方法的处理;传入参数 是什么,有什么标准;输出参数是什么,有什么标准;异常处理应该如何实现,是自定义异常还是使用 JDK 提供的异常;控制器返回结果的时候,是返回 AJAX 异步处理结果 (ResponseBody)还是一个视图结果,当发生异常的时候,要进入哪一个错误提示页面等。
2.3 开发过程
用代码技术实现需求的过程。
二、 项目功能分析
1 模块划分
所谓的模块,就是当前工程中,可以绑定开发的多个功能。如:用户管理模块包含新增 用户、修改用户、删除用户、查询用户等。
1.1 用户登录模块
用户登录。 传入参数:用户名(身份)、密码(凭证)、记住我(rememberMe) 输出参数:显示主页面(返回视图地址)、实现权限查询、身份数据记录(session 记 录)
1.1.1 用例图
1.1.2 时序图
1.1.3 类图
1.2 系统公告管理(CMS 系统)
Content Manager(Management) System 内容管理系统。用户管理平台编辑内容。
客户平台可以查看内容。类似百度中的新闻。
1.2.1 公告类别管理
新增公告类别:输入-公告类别名称和公告类别描述;输出-显示分页查询公告类别页面
删除公告类别:输入-公告类别的主键;输出-显示分页查询公告类别页面
修改公告类别:输入-公告类别的主键;输出-显示修改公告类别详情表单页面;二次输 入-修改后的公告类别数据;输出-分页查询公告类别页面
分页查询公告类别:输入-页码和每页显示数据数量; 输出-显示逻辑
1.2.2 公告管理
新增公告;修改公告;删除公告;分页查询公告;名称模糊分页查询。
三、 数据建模
一般都是 DBA 数据库管理员进行设计的。就是数据库的库表设计。
用户数据如下:
四、 基础环境搭建
使用一个脚手架实现快速的开发。 使用的脚手架是若依开发平台。是一个使用 SpringBoot+Shiro 实现的一个后台统一 管理模板。在若依脚手架中,提供了若干统一管理能力。如:用户管理、权限管理、菜单管 理、角色管理等。
1 创建数据库
2 导入数据脚本
在软件目录中,有 02 若依子目录,其中有连个 sql 脚本。ry_20191122.sql、quartz.sql。 依次执行连个数据库脚本,初始化表格内容。
3 创建工程
创建一个 Maven 工程,根据已有的工程进行导入创建。已有工程命名为 Ting。工程 所在位置在软件目录/02 若依/Ting
4 启动
启动 com.ruoyi.RuoYiApplication。
访问 http://localhost/。
默认提供的超级管理员 帐户:
用户名-admin;
密码-admin123
主要内容
- CMS 内容管理实现
一、 CMS 内容管理实现
1 登录页面视图修改
1.1 找到登录页面
1.2 修改内容
2 主页面视图修改
2.1 找到主页面视图
2.2 修改内容
修改 32 行显示内容:
注释 78~168 行视图代码
注释 184~185 行视图代码
3 修改工作台首页选项卡内容
3.1 找到首页视图
3.2 修改内容
注释全部显示内容,添加自定义内容。
4 菜单管理操作
添加需要的权限。并提供对应角色和用户。
4.1 进入菜单管理页面
4.2 新增需要的菜单
4.2.1 点击新增按钮
4.2.2 编辑目录内容
4.2.3 为目录添加菜单
4.2.4 编辑菜单内容
4.2.5 为菜单添加按钮权限
4.2.6 编辑按钮内容
5 角色管理操作
5.1 进入角色管理页面
5.2 新增需要的角色
5.2.1 点击新增按钮
5.2.2 编辑角色内容
6 用户管理操作
6.1 进入用户管理页面
6.2 新增需要的用户
6.2.1 点击新增按钮
6.2.2 编辑用户内容
7 CMS 分类管理
7.1 公告分类分页查询
7.1.1 视图开发
创建 html 视图文件:src/main/resources/templates/cms/type/type.html
具体内容如下:
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header('公告类型列表')" />
</head>
<body class="gray-bg">
<div class="container-div">
<div class="row">
<div class="col-sm-12 search-collapse">
<form id="post-form">
<div class="select-list">
<ul>
<li>
类型名称:<input type="text" name="typeName"/>
</li>
<li>
<a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i> 搜索</a>
<a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i> 重置</a>
</li>
</ul>
</div>
</form>
</div>
<div class="btn-group-sm" id="toolbar" role="group">
<a class="btn btn-success" onclick="$.operate.add()" shiro:hasPermission="cms:type:add">
<i class="fa fa-plus"></i> 新增
</a>
<a class="btn btn-primary single disabled" onclick="$.operate.edit()" shiro:hasPermission="cms:type:edit">
<i class="fa fa-edit"></i> 修改
</a>
<a class="btn btn-danger multiple disabled" onclick="$.operate.removeAll()" shiro:hasPermission="cms:type:remove">
<i class="fa fa-remove"></i> 删除
</a>
</div>
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
<th:block th:include="include :: footer" />
<script th:inline="javascript">
// @permission.hasPermi('cms:type:edit') 调用作用域中的Shiro提供的内置对象,做权限校验。
var editFlag = [[${@permission.hasPermi('cms:type:edit')}]];
var removeFlag = [[${@permission.hasPermi('cms:type:remove')}]];
var datas = [[${@dict.getType('sys_normal_disable')}]];
var prefix = ctx + "cms/type";
$(function() {
var options = {
url: prefix + "/list", // 查询路径地址。就是分页查询
createUrl: prefix + "/add", // 新增路径地址、 显示新增页面和提交新增数据同地址。不同请求方式。 get请求显示新增视图, post请求提交新增数据
updateUrl: prefix + "/edit/{id}", // 修改路径地址。 根据主键查询,或提交修改内容
removeUrl: prefix + "/remove", // 删除路径地址。
// exportUrl: prefix + "/export", // 到处表格路径地址
sortName: "typeSort", // 定义短名称
modalName: "公告类型",
columns: [{
checkbox: true // 复选框
},
{
field: 'typeId',
title: '公告类型编号',
sortable: true
},
{
field: 'typeName', // 提供要显示的数据的属性名称。
title: '公告类型名称',
sortable: true
},
{
field: 'remark',
title: '公告类型描述',
sortable: true
},
{
field: 'typeSort',
title: '显示顺序',
sortable: true
},
{
field: 'status',
title: '状态',
align: 'center',
formatter: function(value, row, index) {
return $.table.selectDictLabel(datas, value);
}
},
{
title: '操作',
align: 'center',
formatter: function(value, row, index) {
var actions = [];
actions.push('<a class="btn btn-success btn-xs ' + editFlag + '" href="javascript:void(0)" οnclick="$.operate.edit(\'' + row.typeId + '\')"><i class="fa fa-edit"></i>编辑</a> ');
actions.push('<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" οnclick="$.operate.remove(\'' + row.typeId + '\')"><i class="fa fa-remove"></i>删除</a>');
return actions.join('');
}
}]
};
$.table.init(options);
});
</script>
</body>
</html>
7.1.1.1视图中的注意点
th:include - 导入其他 html 内容。下述’图 1’内容代表导入一个命名为 include.html 的视图中的 header 部分内容,且在导入的同时,传入参数’公告类型列表’。'图 2’是 include.html 文件内容片段
搜索函数和表单重置:'图 3’内容是 type.html 中的代码片段,用于触发条件分页查询 和表单重置。其中的
.
t
a
b
l
e
和
.table 和
.table和.form 都是若依脚手架提供的 JavaScript 函数。函数定义 在 src/main/resources/static/ruoyi/js/ry-ui.js 文件中。'图 4’和’图 5’为 ry-ui.js 代码片 段截图。
7.1.2 常量接口
package com.ruoyi.project.cms.type.commons;
public interface CMSTypeConst {
// 返回视图的前缀。相当于src/resources/templates/目录名称
String PREFIX = "cms/type";
// 所有公告类型相关控制器的访问路径前缀。
String PATH_PREFIX = "/cms/type";
// 公告类型名称唯一
String TYPE_NAME_UNIQUE = "0";
// 公告类型名称不唯一
String TYPE_NAME_NOT_UNIQUE = "1";
}
7.1.3 实体类型
package com.ruoyi.project.cms.type.domain;
import com.ruoyi.framework.web.domain.BaseEntity;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
public class CMSType extends BaseEntity {
private static final long serialVersionUID = 1L;
private Long typeId;
private String typeName;
private String typeSort;
private String status;
public Long getTypeId() {
return typeId;
}
public void setTypeId(Long typeId) {
this.typeId = typeId;
}
@NotBlank(message = "公告类型名称不能为空")
@Size(min = 0, max = 50, message = "公告类型名称长度不能超过50个字符")
public String getTypeName() {
return typeName;
}
public void setTypeName(String typeName) {
this.typeName = typeName;
}
@NotBlank(message = "显示顺序不能为空")
public String getTypeSort() {
return typeSort;
}
public void setTypeSort(String typeSort) {
this.typeSort = typeSort;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
7.1.4 控制器开发
package com.ruoyi.project.cms.type.controller;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.page.TableDataInfo;
import com.ruoyi.project.cms.type.commons.CMSTypeConst;
import com.ruoyi.project.cms.type.domain.CMSType;
import com.ruoyi.project.cms.type.service.ICMSTypeService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.List;
/**
* @author 老金
*
* @version 1.0
*
* 分页查询公告类型控制器
*/
@Controller
@RequestMapping(CMSTypeConst.PATH_PREFIX)
public class CMSTypeController extends BaseController {
@Autowired
private ICMSTypeService typeService;
@RequiresPermissions("cms:type:view")
@GetMapping
public String view(){
return CMSTypeConst.PREFIX + "/type";
}
@RequiresPermissions("cms:type:list")
@PostMapping("/list")
@ResponseBody
public TableDataInfo list(CMSType type){
// 分页处理
startPage();
// 查询, 通过服务代码,调用Mapper逻辑,查询数据库中的数据。
List<CMSType> list = typeService.selectCMSTypeList(type);
return getDataTable(list);
}
}
7.1.5 服务接口开发
package com.ruoyi.project.cms.type.service;
import com.ruoyi.project.cms.type.domain.CMSType;
import java.util.List;
/**
* @author 老金
* @version 1.0
* 公告类型管理服务接口 - 处理分页查询公告类型
*/
public interface ICMSTypeService {
/**
* 查询公告类型信息集合 - 分页查询,使用PageHelper插件实现分页。
*
* @param type 公告类型信息 - 查询条件
* @return 公告类型信息集合
*/
public List<CMSType> selectCMSTypeList(CMSType type);
}
7.1.6 服务实现开发
package com.ruoyi.project.cms.type.service;
import com.ruoyi.project.cms.type.domain.CMSType;
import com.ruoyi.project.cms.type.mapper.CMSTypeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author 老金
* @version 1.0
* 公告类型管理服务实现 - 处理分页查询公告类型
*/
@Service
public class CMSTypeServiceImpl implements ICMSTypeService {
// 公告类型数据访问接口
@Autowired
private CMSTypeMapper typeMapper;
@Override
public List<CMSType> selectCMSTypeList(CMSType type) {
return typeMapper.selectCMSTypeList(type);
}
}
7.1.7 数据访问接口开发
package com.ruoyi.project.cms.type.mapper;
import com.ruoyi.project.cms.type.domain.CMSType;
import java.util.List;
/**
* @author 老金
* @version 1.0
* 公告类型数据访问接口 - 专门处理查询公告类型数据
*/
public interface CMSTypeMapper {
/**
* 查询公告类型数据集合
*
* @param type 公告类型信息
* @return 公告类型数据集合
*/
List<CMSType> selectCMSTypeList(CMSType type);
}
7.1.8 SQL 映射文件
在 src/main/resources/mybatis/cms 目录中编写 XML 文件:CMSTypeMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.project.cms.type.mapper.CMSTypeMapper">
<!-- 查询结果resultSet和实体类型CMSType之间的映射关系 -->
<resultMap type="CMSType" id="CMSTypeResult">
<id property="typeId" column="type_id" />
<result property="typeName" column="type_name" />
<result property="typeSort" column="type_sort" />
<result property="status" column="status" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
</resultMap>
<!-- 条件查询 -->
<select id="selectCMSTypeList" parameterType="CMSType" resultMap="CMSTypeResult">
select type_id, type_name, type_sort, status, create_by, create_time, update_by, update_time, remark
from cms_type
<where>
<if test="typeName != null and typeName != ''">
<!-- concat是MySQL数据库中的函数,用来实现字符串拼接。
concat(string...)
-->
AND type_name like concat('%', #{typeName}, '%')
</if>
</where>
</select>
</mapper>
7.1.9 实现结果
7.2 新增公告分类
7.2.1 视图开发
创建 html 视图文件:src/main/resources/templates/cms/type/add.html
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
<th:block th:include="include :: header('新增公告类型')" />
</head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
<form class="form-horizontal m" id="form-post-add">
<div class="form-group">
<label class="col-sm-3 control-label">公告类型名称:</label>
<div class="col-sm-8">
<input class="form-control" type="text" name="typeName" id="typeName" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">显示顺序:</label>
<div class="col-sm-8">
<input class="form-control" type="text" name="typeSort" id="typeSort" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">状态:</label>
<div class="col-sm-8">
<div class="radio-box" th:each="dict : ${@dict.getType('sys_normal_disable')}">
<input type="radio" th:id="${dict.dictCode}" name="status" th:value="${dict.dictValue}" th:checked="${dict.default}">
<label th:for="${dict.dictCode}" th:text="${dict.dictLabel}"></label>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">备注:</label>
<div class="col-sm-8">
<textarea id="remark" name="remark" class="form-control"></textarea>
</div>
</div>
</form>
</div>
<th:block th:include="include :: footer" />
<script type="text/javascript">
var prefix = ctx + "cms/type";
$("#form-post-add").validate({
onkeyup: false,
rules:{
typeName:{
remote: {
url: prefix + "/checkTypeNameUnique",
type: "post",
dataType: "json",
data: {
"typeName" : function() {
return $.common.trim($("#typeName").val());
}
},
dataFilter: function(data, type) {
return $.validate.unique(data);
}
}
},
typeSort:{
digits:true
},
},
messages: {
"typeName": {
remote: "公告类型名称已经存在"
}
},
focusCleanup: true
});
function submitHandler() { // 提交新增公告类型请求。
if ($.validate.form()) {
$.operate.save(prefix + "/add", $('#form-post-add').serialize());
}
}
</script>
</body>
</html>
7.2.2 控制器开发
package com.ruoyi.project.cms.type.controller;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.project.cms.type.commons.CMSTypeConst;
import com.ruoyi.project.cms.type.domain.CMSType;
import com.ruoyi.project.cms.type.service.ICMSTypeAddService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author 老金
*
* @version 1.0
*
* 新增公告类型控制器
*/
@Controller
@RequestMapping(CMSTypeConst.PATH_PREFIX)
public class CMSTypeAddController extends BaseController {
@Autowired
private ICMSTypeAddService icmsTypeAddService;
/**
* 新增公告类型到数据库, 要求必须有权限cms:type:add才可访问当前方法。
* 注解Validated - 用于校验请求参数。需要配合实体类型中get/set方法上定义的注解实现校验。
* @param type 要新增的公告类型
* @return
*/
@RequiresPermissions("cms:type:add")
@PostMapping("/add")
@ResponseBody
public AjaxResult add(@Validated CMSType type){
if(CMSTypeConst.TYPE_NAME_NOT_UNIQUE.equals(icmsTypeAddService.checkTypeNameUnique(type))){
// 公告类型名称不可用。
return error("新增公告类型'" + type.getTypeName() + "'失败,公告类型名称已存在");
}
// 调用服务逻辑,新增数据到数据库。
// toAjax是父类型BaseController中定义的方法。 根据新增行数判断是否成功。新增数据行数大于0,成功。反之失败。
return toAjax(icmsTypeAddService.addCMSType(type));
}
/**
* 新增公告类型页面跳转
*/
@GetMapping("/add")
public String toAdd(){
return CMSTypeConst.PREFIX + "/add";
}
/**
* 检查公告类型名称是否唯一
* @return
* 字符串0 - 唯一数据,可以使用
* 字符串1 - 非唯一数据,不可使用
*/
@PostMapping("/checkTypeNameUnique")
@ResponseBody
public String checkTypeNameUnique(CMSType type){
return icmsTypeAddService.checkTypeNameUnique(type);
}
}
7.2.3 服务接口开发
package com.ruoyi.project.cms.type.service;
import com.ruoyi.project.cms.type.domain.CMSType;
/**
* @author 老金
*
* @version 1.0
*
* 公告类型服务接口 - 处理新增相关业务
*/
public interface ICMSTypeAddService {
/**
* 判断公告类型名称是否唯一
* @param type 查询参数
* @return 0 - 唯一可用。 1 - 不唯一不可用。
*/
String checkTypeNameUnique(CMSType type);
/**
* 新增数据到数据库
* @param type
* @return
*/
int addCMSType(CMSType type);
}
7.2.4 服务实现开发
package com.ruoyi.project.cms.type.service;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.security.ShiroUtils;
import com.ruoyi.project.cms.type.commons.CMSTypeConst;
import com.ruoyi.project.cms.type.domain.CMSType;
import com.ruoyi.project.cms.type.mapper.CMSTypeAddMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author 老金
* @version 1.0
* 新增公告类型服务实现
*/
@Service
public class CMSTypeAddServiceImpl implements ICMSTypeAddService {
@Autowired
private CMSTypeAddMapper typeAddMapper;
@Override
public String checkTypeNameUnique(CMSType type) {
// 当前可能是在修改公告类型,也可能在新增公告类型。 如果是修改,则需要判定主键。
Long typeId = StringUtils.isNull(type.getTypeId()) ? -1L : type.getTypeId();
CMSType info = typeAddMapper.checkTypeNameUnique(type.getTypeName());
if (StringUtils.isNotNull(info) && info.getTypeId().longValue() != typeId.longValue())
{
return CMSTypeConst.TYPE_NAME_NOT_UNIQUE;
}
return CMSTypeConst.TYPE_NAME_UNIQUE;
}
@Override
public int addCMSType(CMSType type) {
// 将当前登录用户的登录名,赋值到createBy属性中。
type.setCreateBy(ShiroUtils.getLoginName());
return typeAddMapper.insertCMSType(type);
}
}
7.2.5 数据访问接口开发
package com.ruoyi.project.cms.type.mapper;
import com.ruoyi.project.cms.type.domain.CMSType;
/**
* 新增公告类型数据访问接口
*/
public interface CMSTypeAddMapper {
/**
* 根据公告类型名称查询公告类型对象
* @param typeName 查询条件
* @return
*/
CMSType checkTypeNameUnique(String typeName);
/**
* 新增公告类型
* @param type 要新增的数据
* @return
*/
int insertCMSType(CMSType type);
}
7.2.6 SQL 映射文件
在 src/main/resources/mybatis/cms 目 录 中 编 写 XML 文 件 : CMSTypeAddMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.project.cms.type.mapper.CMSTypeAddMapper">
<!--新增公告类型-->
<insert id="insertCMSType" parameterType="CMSType" useGeneratedKeys="true" keyProperty="typeId">
insert into cms_type(
<if test="typeId != null and typeId != 0">type_id,</if>
<if test="typeName != null and typeName != ''">type_name,</if>
<if test="typeSort != null and typeSort != ''">type_sort,</if>
<if test="status != null and status != ''">status,</if>
<if test="remark != null and remark != ''">remark,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
create_time
) values (
<if test="typeId != null and typeId != 0">#{typeId},</if>
<if test="typeName != null and typeName != ''">#{typeName},</if>
<if test="typeSort != null and typeSort != ''">#{typeSort},</if>
<if test="status != null and status != ''">#{status},</if>
<if test="remark != null and remark != ''">#{remark},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
sysdate()
)
</insert>
<!-- 根据公告类型名称查询公告类型对象 -->
<select id="checkTypeNameUnique" parameterType="String"
resultMap="com.ruoyi.project.cms.type.mapper.CMSTypeMapper.CMSTypeResult">
select type_id, type_name, type_sort, status, create_by, create_time, update_by, update_time, remark
from cms_type
where type_name = #{typeName}
</select>
</mapper>
7.2.7 实现结果
'图 6’为新增公告类型“名称重复”情况发生时的提示视图;'图 7’为正常新增公告类 型数据视图。
7.3 修改公告分类
7.3.1 视图开发
创建 html 视图文件:src/main/resources/templates/cms/type/edit.html
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
<th:block th:include="include :: header('修改公告类型')" />
</head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
<form class="form-horizontal m" id="form-post-edit" th:object="${type}">
<input id="typeId" name="typeId" type="hidden" th:field="*{typeId}"/>
<div class="form-group">
<label class="col-sm-3 control-label">公告类型名称:</label>
<div class="col-sm-8">
<input class="form-control" type="text" name="typeName" id="typeName" th:field="*{typeName}" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">显示顺序:</label>
<div class="col-sm-8">
<input class="form-control" type="text" name="typeSort" id="typeSort" th:field="*{typeSort}" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">状态:</label>
<div class="col-sm-8">
<div class="radio-box" th:each="dict : ${@dict.getType('sys_normal_disable')}">
<input type="radio" th:id="${dict.dictCode}" name="status" th:value="${dict.dictValue}" th:field="*{status}">
<label th:for="${dict.dictCode}" th:text="${dict.dictLabel}"></label>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">备注:</label>
<div class="col-sm-8">
<textarea id="remark" name="remark" class="form-control">[[*{remark}]]</textarea>
</div>
</div>
</form>
</div>
<th:block th:include="include :: footer" />
<script type="text/javascript">
var prefix = ctx + "cms/type";
$("#form-post-edit").validate({
onkeyup: false,
rules:{
typeName:{
remote: {
url: prefix + "/checkTypeNameUnique",
type: "post",
dataType: "json",
data: {
"typeId": function() {
return $("input[name='typeId']").val();
},
"typeName" : function() {
return $.common.trim($("#typeName").val());
}
},
dataFilter: function(data, type) {
return $.validate.unique(data);
}
}
},
typeSort:{
digits:true
},
},
messages: {
"typeName": {
remote: "公告类型名称已经存在"
}
},
focusCleanup: true
});
function submitHandler() {
if ($.validate.form()) {
$.operate.save(prefix + "/edit", $('#form-post-edit').serialize());
}
}
</script>
</body>
</html>
7.3.2 控制器开发
package com.ruoyi.project.cms.type.controller;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.project.cms.type.commons.CMSTypeConst;
import com.ruoyi.project.cms.type.domain.CMSType;
import com.ruoyi.project.cms.type.service.ICMSTypeAddService;
import com.ruoyi.project.cms.type.service.ICMSTypeEditService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping(CMSTypeConst.PATH_PREFIX)
public class CMSTypeEditController extends BaseController {
@Autowired
private ICMSTypeEditService editService;
@Autowired
private ICMSTypeAddService addService;
/**
* 修改公告类型数据。就是要根据主键更新其他字段。
* @param type
* @return
*/
@RequiresPermissions("cms:type:edit")
@PostMapping("/edit")
@ResponseBody
public AjaxResult edit(@Validated CMSType type){
// 判断公告类型的名称是否可用。
if(CMSTypeConst.TYPE_NAME_NOT_UNIQUE.equals(addService.checkTypeNameUnique(type))){
// 公告类型名称不可用
return error();
}
// 更新数据。并返回结果。更新行数大于0,代表更新成功。反之失败。
return toAjax(editService.editCMSType(type));
}
/**
* 根据主键查询公告类型
* @param id 公告类型主键
* @return 视图
*/
@GetMapping("/edit/{id}")
public String toEdit(@PathVariable("id") Long id, Model model){
CMSType type = editService.selectCMSTypeByPK(id);
model.addAttribute("type", type);
return CMSTypeConst.PREFIX + "/edit";
}
}
7.3.3 服务接口开发
package com.ruoyi.project.cms.type.service;
import com.ruoyi.project.cms.type.domain.CMSType;
/**
* @author 老金
* @version 1.0
* 修改公告类型服务接口
*/
public interface ICMSTypeEditService {
/**
* 根据主键查询公告类型
* @param id
* @return
*/
CMSType selectCMSTypeByPK(Long id);
/**
* 根据主键更新公告类型。
* @param type
* @return
*/
int editCMSType(CMSType type);
}
7.3.4 服务实现开发
package com.ruoyi.project.cms.type.service;
import com.ruoyi.common.utils.security.ShiroUtils;
import com.ruoyi.project.cms.type.domain.CMSType;
import com.ruoyi.project.cms.type.mapper.CMSTypeEditMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author 老金
* @version 1.0
* 修改公告类型服务实现
*/
@Service
public class CMSTypeEditServiceImpl implements ICMSTypeEditService {
@Autowired
private CMSTypeEditMapper editMapper;
@Override
public CMSType selectCMSTypeByPK(Long id) {
return editMapper.selectByPK(id);
}
@Override
public int editCMSType(CMSType type) {
// 设置更新者名称
type.setUpdateBy(ShiroUtils.getLoginName());
return editMapper.updateCMSType(type);
}
}
7.3.5 数据访问接口开发
package com.ruoyi.project.cms.type.mapper;
import com.ruoyi.project.cms.type.domain.CMSType;
/**
* @author 老金
* @version 1.0
* 修改公告类型数据访问接口
*/
public interface CMSTypeEditMapper {
/**
* 主键查询公告类型
* @param id
* @return
*/
CMSType selectByPK(Long id);
/**
* 根据主键更新公告类型
* @param type
* @return
*/
int updateCMSType(CMSType type);
}
7.3.6 SQL 映射文件
在 src/main/resources/mybatis/cms 目 录 中 编 写 XML 文 件 : CMSTypeEditMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.project.cms.type.mapper.CMSTypeEditMapper">
<!-- 根据主键更新公告类型 -->
<update id="updateCMSType" parameterType="CMSType">
update cms_type
<set>
<if test="typeName != null and typeName != ''">type_name = #{typeName},</if>
<if test="typeSort != null and typeSort != ''">type_sort = #{typeSort},</if>
<if test="status != null and status != ''">status = #{status},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
update_time = sysdate()
</set>
where type_id = #{typeId}
</update>
<!-- 主键查询公告类型 -->
<select id="selectByPK" parameterType="long"
resultMap="com.ruoyi.project.cms.type.mapper.CMSTypeMapper.CMSTypeResult">
select type_id, type_name, type_sort, status, create_by, create_time, update_by, update_time, remark
from cms_type
where type_id = #{id}
</select>
</mapper>
7.3.7 实现结果
'图 8’和’图 9’为两种进入编辑公告类型的方式。
'图 10’为修改公告类型“公告名称重复”情况发生时的提示视图。
'图 11’是正常修改公告类型数据视图。
7.4 删除公告分类
7.4.1 控制器开发
package com.ruoyi.project.cms.type.controller;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.project.cms.type.commons.CMSTypeConst;
import com.ruoyi.project.cms.type.service.ICMSTypeRemoveService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author 老金
* @version 1.0
* 删除公告类型控制器
*/
@Controller
@RequestMapping(CMSTypeConst.PATH_PREFIX)
public class CMSTypeRemoveController extends BaseController {
@Autowired
private ICMSTypeRemoveService removeService;
/**
* 删除公告类型
* @param ids 要删除的公告类型的主键,多个主键使用逗号分隔
* @return
*/
@PostMapping("/remove")
@RequiresPermissions("cms:type:remove")
@ResponseBody
public AjaxResult remove(String ids){
try{
return toAjax(removeService.removeCMSTypeByIds(ids));
}catch(Exception e){
return error(e.getMessage());
}
}
}
7.4.2 服务接口开发
package com.ruoyi.project.cms.type.service;
/**
* @author 老金
* @version 1.0
* 删除公告类型服务接口
*/
public interface ICMSTypeRemoveService {
/**
* 根据主键删除公告类型。
* @param ids 要删除的公告类型主键。如果是批量删除,多个主键之间使用逗号分隔。
* @return
*/
int removeCMSTypeByIds(String ids);
}
7.4.3 服务实现开发
package com.ruoyi.project.cms.type.service;
import com.ruoyi.common.utils.text.Convert;
import com.ruoyi.project.cms.type.mapper.CMSTypeRemoveMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author 老金
* @version 1.0
* 删除公告类型服务实现
*/
@Service
public class CMSTypeRemoveServiceImpl implements ICMSTypeRemoveService {
@Autowired
private CMSTypeRemoveMapper removeMapper;
/**
* 删除公告类型
* @param ids 要删除的公告类型主键。如果是批量删除,多个主键之间使用逗号分隔。
* @return
*/
@Override
public int removeCMSTypeByIds(String ids) {
Long[] typeIds = Convert.toLongArray(ids);
return removeMapper.deleteCMSTypeByIds(typeIds);
}
}
7.4.4 数据访问接口开发
package com.ruoyi.project.cms.type.mapper;
import org.apache.ibatis.annotations.Param;
/**
* @author 老金
* @version 1.0
* 删除公告类型数据访问接口
*/
public interface CMSTypeRemoveMapper {
/**
* 主键删除公告类型。 此方法支持批量删除
* @param typeIds
* @return
*/
int deleteCMSTypeByIds(@Param("ids") Long[] typeIds);
}
7.4.5 SQL 映射文件
在 src/main/resources/mybatis/cms 目 录 中 编 写 XML 文 件 : CMSTypeRemoveMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.project.cms.type.mapper.CMSTypeRemoveMapper">
<delete id="deleteCMSTypeByIds" parameterType="Long">
delete from cms_type where type_id in
<foreach collection="ids" item="typeId" open="(" separator="," close=")">
#{typeId}
</foreach>
</delete>
</mapper>
7.4.6 实现结果
'图 12’为删除单一公告类型方式。
'图 13’为删除单一公告类型确认框提示。
'图 14’为批量删除公告类型方式。
'图 15’为批量删除公告类型确认框提示。
项目练习源码:https://gitee.com/cutelili/Ting