-
业务设计说明
本模块主要是实现对用户信息的管理,包括用户查询,保存,更新,禁用启用等操作,其业务分析如下图所示:图-1所示:
基于对表的设计,其数据逻辑关系的展示,如图-2所示:
用户表设计的脚本如下:
CREATE TABLE `sys_users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) DEFAULT NULL COMMENT '密码',
`salt` varchar(50) DEFAULT NULL COMMENT '盐',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`mobile` varchar(100) DEFAULT NULL COMMENT '手机号',
`valid` tinyint(4) DEFAULT NULL COMMENT '状态',
`deptId` int(11) DEFAULT NULL,
`createdTime` datetime DEFAULT NULL COMMENT '创建时间',
`modifiedTime` datetime DEFAULT NULL COMMENT '修改时间',
`createdUser` varchar(20) DEFAULT NULL COMMENT '创建用户',
`modifiedUser` varchar(20) DEFAULT NULL COMMENT '修改用户',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='系统用户';
用户与角色的关系表脚本设计如下:
CREATE TABLE `sys_user_roles` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL COMMENT '用户ID',
`role_id` int(11) DEFAULT NULL COMMENT '角色ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户与角色对应关系';
-
原型设计说明
基于用户需求,通过静态页面为用户呈现用户模块的基本需求。
当在主页点击用户管理时,呈现用户列表页面,如图-3所示。
在列表页面点击添加按钮时,呈现用户编辑页面,
如图-4所示.
在编辑页面点击所属部门时,呈现部门树结构信息,
如图-5所示。
说明:假如客户对此原型进行了确认,后续则可以基于此原型进行研发。
-
API设计说明
用户管理业务后台API分层架构及调用关系如图-6所示:
说明:分层目的主要将复杂问题简单化,实现各司其职,各尽所能。
-
用户管理列表页面呈现
-
业务时序分析
用户列表页面,其加载时序分析,如图-7所示:
-
服务端实现
-
Controller实现
- 业务描述与设计实现
基于用户管理的请求业务,在PageController中添加返回用户页面相关方法。
- 关键代码设计与实现
检查PageController中是否有返回UI页面的方法,有则无需添加。例如:
@RequestMapping("{module}/{moduleUI}")
public String doModuleUI(@PathVariable String moduleUI) {
return "sys/"+moduleUI;
}
-
客户端实现
-
首页菜单事件处理
- 业务描述与设计实现
首先准备用户列表页面(/templates/pages/sys/user_list.html),然后在
starter.html页面中点击用户管理时异步加载用户列表页面。
- 关键代码设计与实现
找到项目中的starter.html 页面,页面加载完成以后,注册用户管理项的点击事件,当点击用户管理时,执行事件处理函数。关键代码如下:
$(function(){
doLoadUI("load-user-id","user/user_list")
})
function doLoadUI(id,url){
$("#"+id).click(function(){
$("#mainContentId").load(url);
});
}
其中,load函数为jquery中的ajax异步请求函数。
-
用户列表页面
- 业务描述与设计实现
本页面呈现用户信息时要以分页形式进行呈现。
- 关键代码设计与实现:
参考sys_user.html文件内容
-
用户管理列表数据呈现
-
数据架构分析
用户列表页面加载完成,启动用户数据的异步加载操作,本次列表页面要以分页形式呈现用户信息,其数据查询时,数据的封装及传递过程,如图-8所示。
说明:本模块将从数据库查询到的用户及相关数据封装到SysUserDeptVo对象,一行记录一个SysUserDeptVo对象。
用户列表数据加载,其时序分析,如图-9下:
-
服务端关键业务及代码实现
-
Vo类实现
- 业务描述及设计实现
构建值对象(VO)封装从数据库查询到的用户以及用户对应的部门信息,一行记录映射为内存中一个的这样的对象。
- 关键代码分析及实现
定义SysUserDeptVo类,基于此类对象属性封装数据。关键代码如下:
package com.cy.pj.sys.vo;
public class SysUserDeptVo implements Serializable{
private static final long serialVersionUID = 5477389876913514595L;
private Integer id;
private String username;
private String password;//md5
private String salt;
private String email;
private String mobile;
private Integer valid=1;
private SysDept sysDept; //private Integer deptId;
private Date createdTime;
private Date modifiedTime;
private String createdUser;
private String modifiedUser;
public Integer getValid() {
return valid;
}
public void setValid(Integer valid) {
this.valid = valid;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public SysDept getSysDept() {
return sysDept;
}
public void setSysDept(SysDept sysDept) {
this.sysDept = sysDept;
}
public Date getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Date createdTime) {
this.createdTime = createdTime;
}
public Date getModifiedTime() {
return modifiedTime;
}
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
public String getCreatedUser() {
return createdUser;
}
public void setCreatedUser(String createdUser) {
this.createdUser = createdUser;
}
public String getModifiedUser() {
return modifiedUser;
}
public void setModifiedUser(String modifiedUser) {
this.modifiedUser = modifiedUser;
}
}
说明:通过此对象除了可以封装从数据库查询的数据,还可以封装客户端请求数据,实现层与层之间数据的传递。
-
Dao接口实现
- 业务描述及设计实现
通过数据层对象,基于业务层的参数数据,查询用户记录总数以及当前页面要呈现的用户基本信息。
- 关键代码分析及实现:
第一步:定义用户数据层接口对象,通过此对象实现用户业务的数据操作。代码如下:
@Mapper
public interface SysUserDao {
}
第二步:在SysUserDao接口中添加getRowCount方法用于按条件统计记录总数。代码如下:
int getRowCount( String username);
第三步:在SysUserDao接口中添加findPageObjects方法,基于此方法实现当前页记录的数据查询操作。代码如下:
List<SysUserDeptVo> findPageObjects(
String username,
Integer startIndex,
Integer pageSize);
-
Mapper文件实现
- 业务描述及设计实现
基于Dao接口创建映射文件,在此文件中通过相关元素(例如select)描述要执行的数据操作。
- 关键代码设计及实现
第一步:在映射文件的设计目录中添加SysUserMapper.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.cy.pj.sys.dao.SysUserDao">
</mapper>
第二步:在映射文件中添加sql元素实现,SQL中的共性操作,代码如下:
<sql id="queryWhereId">
from sys_users
<where>
<if test="username!=null and username!=''">
username like concat("%",#{username},"%")
</if>
</where>
</sql>
第三步:在映射文件中添加id为getRowCount元素,按条件统计记录总数,代码如下:
<select id="getRowCount" resultType="int">
select count(*)
<include refid="queryWhereId"/>
</select>
第四步:在映射文件中添加id为findPageObjects元素,实现分页查询。代码如下:
<select id="findPageObjects"
resultMap="sysUserDeptVo">
select *
<include refid="queryWhereId"/>
order by createdTime desc
limit #{startIndex},#{pageSize}
</select>
第五步:定义查询结果映射元素。代码如下:
<resultMap type="com.cy.pj.sys.vo.SysUserDeptVo"
id="sysUserDeptVo">
<!-- 一般应用于many2one或one2one做关联查询
在当前应用是基于deptId查询部门信息并将其
存储到SysUserDeptVo对象的sysDept属性中。
-->
<association property="sysDept"
column="deptId"
select="com.cy.pj.sys.dao.SysDeptDao.findById">
</association>
</resultMap>
思考:
- resultMap用户进行自定义结果映射。
- association 元素用于定义关联数据的查询。
-
Service接口及实现类
- 业务描述与设计实现
-
在用户分页查询中,业务层对象主要负责对业务数据进行校验,并借助数据层对象完成数据的分页查询操作。
- 关键代码设计及实现
-
第一步:定义用户业务接口及方法,暴露外界对用户业务数据的访问,其代码参考如下:
-
package com.cy.pj.sys.service;
public interface SysUserService {
PageObject<SysUserDeptVo> findPageObjects(
String username,Integer pageCurrent);
}
-
第二步:定义用户业务接口实现类,并添加用户业务数据分页查询操作的具体实现,其代码参考如下:
-
package com.cy.pj.sys.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@Service
public class SysUserServiceImpl implements SysUserService {
@Override
public PageObject<SysUserDeptVo> findPageObjects(
String username,Integer pageCurrent) {
//1.对参数进行校验
if(pageCurrent==null||pageCurrent<1)
throw new IllegalArgumentException("当前页码值无效");
//2.查询总记录数并进行校验
int rowCount=sysUserDao.getRowCount(username);
if(rowCount==0)
throw new ServiceException("没有找到对应记录");
//3.查询当前页记录
int pageSize=2;
int startIndex=(pageCurrent-1)*pageSize;
List<SysUserDeptVo> records=
sysUserDao.findPageObjects(username,
startIndex, pageSize);
//4.对查询结果进行封装并返回
return new PageObject<>(pageCurrent, pageSize, rowCount, records);
}
}
-
Controller类实现
- 业务描述与设计实现
-
控制层对象主要负责请求和响应数据的处理,例如,本模块通过业务层对象执行业务逻辑,再通过VO对象封装响应结果(主要对业务层数据添加状态信息),最后将响应结果转换为JSON格式的字符串响应到客户端。
- 关键代码设计与实现
-
定义Controller类,并将此类对象使用Spring框架中的@RestController注解进行标识,表示此类对象要交给Spring管理。然后基于@RequestMapping注解为此类定义根路径映射。代码参考如下:
-
package com.cy.pj.sys.controller;
@RequestMapping("/user/")
@RestController
public class SysUserController {
@Autowired
private SysUserService sysUserService;
}
-
在Controller类中添加菜单查询处理方法,代码参考如下:
-
@RequestMapping("doFindPageObjects")
public JsonResult doFindPageObjects(
String username,Integer pageCurrent) {
return new JsonResult(
sysUserService.findPageObjects(name,
pageCurrent));
}
-
客户端关键业务及代码实现
-
菜单列表信息呈现
- 业务描述与设计实现
-
角色分页页面加载完成以后,向服务端发起异步请求加载角色信息,当角色信息加载完成需要将角色信息、分页信息呈现到列表页面上。
- 关键代码设计与实现
-
异步请求处理函数,关键代码如下:
第一步:分页页面加载完成,向服务端发起异步请求,代码参考如下:
-
$(function(){
//为什么要将doGetObjects函数写到load函数对应的回调内部。
$("#pageId").load("doPageUI",function(){
doGetObjects();
});
}
-
第二步:定义异步请求处理函数,代码参考如下:
-
function doGetObjects(){
//debugger;//断点调试
//1.定义url和参数
var url="user/doFindPageObjects"
var params={"pageCurrent":1};//pageCurrent=2
//2.发起异步请求
//请问如下ajax请求的回调函数参数名可以是任意吗?可以,必须符合标识符的规范
$.getJSON(url,params,function(result){
//请问result是一个字符串还是json格式的js对象?对象
doHandleResponseResult(result);
}
);//特殊的ajax函数
}
-
第三步:定义回调函数,处理服务端的响应结果。代码如下:
-
function doHandleResponseResult (result){ //JsonResult
if(result.state==1){//ok
//更新table中tbody内部的数据
doSetTableBodyRows(result.data.records);//将数据呈现在页面上
//更新页面page.html分页数据
doSetPagination(result.data); //此方法写到page.html中
}else{
alert(result.msg);
}
}
-
第四步:将异步响应结果呈现在table的tbody位置。代码参考如下:
-
function doSetTableBodyRows(records){
//1.获取tbody对象,并清空对象
var tBody=$("#tbodyId");
tBody.empty();
//2.迭代records记录,并将其内容追加到tbody
for(var i in records){
//2.1 构建tr对象
var tr=$("<tr></tr>");
//2.2 构建tds对象
var tds=doCreateTds(records[i]);
//2.3 将tds追加到tr中
tr.append(tds);
//2.4 将tr追加到tbody中
tBody.append(tr);
}
}
-
第五步:创建每行中的td元素,并填充具体业务数据。代码参考如下:
-
function doCreateTds(row){
console.log(row);
var tds="<td><input type='radio' name='radioId' value='"+row.id+"' ></td>"+
"<td>"+row.username+"</td>"+
"<td>"+(row.sysDept?row.sysDept.name:'未分配')+"</td>"+
"<td>"+row.email+"</td>"+
"<td>"+row.mobile+"</td>"+
"<td>"+(row.valid?"启用":"禁用")+"</td>"+
"<td>"+new Date(row.createdTime).toLocaleString()+"</td>"+
"<td>"+new Date(row.modifiedTime).toLocaleString()+"</td>"+
"<td><button type='button' class='btn btn-default btn-valid'>"+(row.valid?"禁用":"启用")+"</button></td>";
return tds;
}
-
用户管理禁用操作实现
-
核心业务分析
-
基于用户在列表页面上选择的的用户记录ID,执行禁用或启用操作,后续业务中被禁用的用户不允许登陆系统。 其时序分析,如图-10下:
-
-
服务端关键业务及代码实现
-
Dao接口实现
- 业务描述及设计实现
-
基于用户id,修改用户状态信息,对此用户进行禁用或启用。
- 关键代码设计及实现:
-
在创建SysUserDao中添加修改用户状态信息的方法。关键代码如下:
-
int validById(
@Param("id")Integer id,
@Param("valid")Integer valid,
@Param("modifiedUser")String modifiedUser);
-
Mapper文件实现
- 业务描述及设计实现
-
在SysUserDao接口对应的映射文件中添加修改用户状态信息的元素,然后在元素内部定义具体的SQL实现。
- 关键代码设计与实现
-
在SysUserMapper.xml文件,添加修改用户状态的SQL元素定义,关键代码如下:
-
<update id="validById">
update sys_users
set valid=#{valid},
modifiedUser=#{modifiedUser},
modifiedTime=now()
where id=#{id}
</update>
-
Service接口及实现类
- 业务描述与设计实现
-
在用户业务层,添加用于完成禁用或启用用户状态的相关业务方法及实现。
- 关键代码设计与实现
-
第一步:在SysUserService接口中,添加修改用户装填的方法。关键代码如下:
int validById(Integer id,Integer valid)
第三步:在SysUserServiceImpl实现类中添加禁用,启用业务的具体实现。关键代码如下:
@Override
public int validById(Integer id,Integer valid) {
//1.合法性验证
if(id==null||id<=0)
throw new ServiceException("参数不合法,id="+id);
if(valid!=1&&valid!=0)
throw new ServiceException("参数不合法,valie="+valid);
if(StringUtils.isEmpty(modifiedUser))
throw new ServiceException("修改用户不能为空");
//2.执行禁用或启用操作(admin为后续登陆用户)
int rows=sysUserDao.validById(id, valid,"admin");
//3.判定结果,并返回
if(rows==0)
throw new ServiceException("此记录可能已经不存在");
return rows;
}
-
Controller类实现
- 业务描述与设计实现
在用户控制层对象中,添加用于处理禁用启用业务的控制层方法。首先在此方法中通过形参接收客户端提交的数据,然后调用业务层对象执行禁用,启用操作,最后封装执行结果,并在运行时将响应对象转换为JSON格式的字符串,响应到客户端。
- 关键代码设计与实现
第一步:在SysUserController中添加用于执行删除业务的方法。代码如下:
@RequestMapping("doValidById")
public JsonResult doValidById(Integer id,Integer valid){
sysUserService.validById(id,valid);
return new JsonResult("update ok");
}
第二步:启动tomcat进行访问测试,打开浏览器输入如下网址:
http://localhost/user/doValidById?id=10
-
客户端关键业务及代码实现
-
用户列表页面事件处理
- 业务描述及设计实现
用户在用户列表页面上点击禁用或按钮,将用户记录id异步提交到服务端,最后在服务端执行用户的禁用、启用动作。
- 关键代码设计与实现
第一步:用户列表页面加载完成以后,在删除按钮上进行点击事件注册。关键代码如下:
...
$(".input-group-btn")
.on("click",".btn-valid",doValidById)
...
第二步:定义禁用、启用操作对应的事件处理函数。关键代码如下:
function doValidById(){
//1.获取对象id,validId的值
var btn=$(this);//this执行调用dom对象
var user=btn.parents("tr").data("rowData");
var id=user.id;
var valid=user.valid;
//2.构建url,参数对象
var url="user/doValidById";
var params={"id":id,"valid":valid?0:1}
//3.发送异步请求,更新数据
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
//doGetObjects();//一种刷新方式
doEditRow(btn,valid?0:1);//一种局部刷新方式
}else{
alert(result.message);
}
})
}
第三步:定义禁用、启用操作对应的局部刷新处理函数。关键代码如下:
function doEditRow(btn,valid){
//1.修改按钮上内容
btn.html(valid?"禁用":"启用");
//2.修改td中元素内容
var tr=btn.parents("tr");
tr.find("td:eq(5)").html(valid?"启用":"禁用");
//3.修改并重新绑定数据
var sysUser=tr.data("sysUser");
sysUser.valid=valid;
tr.data("sysUser",sysUser);
}
-
用户添加页面呈现
-
业务时序分析
在用户列表页面,点击添加按钮时加载编辑页面,其加载时序分析,如图-11所示:
-
准备用户编辑页面
准备用户编辑页面(/templates/pages/sys/user_edit.html)
-
用户编辑页面呈现
- 业务描述与设计实现
在用户列表页面中点击添加按钮时,呈现用户编辑页面。
- 关键代码设计与实现
第一步:用户列表事件注册,关键代码如下:
$(document).ready(function(){
...
$(".i