1、前端页面展示
后台管理主要具有三大功能,分别为权限管理、业务审核和业务管理,其中权限管理又分为用户维护、角色维护和菜单维护,这三个维护功能在后端实现上来说,大同小异,都是CRUD,因此这边以用户维护为例进行介绍。下图是用户维护页面,页面所具备功能已在图中标注:
2、后端代码逻辑
后端代码遵循MVC模式,分别为handler,service,mapper三层,handler层负责业务逻辑,mapper层负责与数据库交互,service层连接这两层,提供接口供handler层调用。
i、数据库结构
create table t_admin(
id int not null auto_increment, # 主键
login_acct varchar(255) not null, # 登录账号
user_pswd varchar(255) not null, # 登录密码
user_name varchar(255) not null, # 昵称
email varchar(255) not null, # 邮件地址
create_time char(19), # 创建时间
primary key (id)
);
ii、用户检索
a、handler层
handler 层调用了 service 层的 getAdminPage 接口来获取查询到的用户,接着将其存入 modelMap 的属性中带入 admin-page 页面进行展示,其中 PageInfo 是 MyBatis 的一个分页插件。
@RequestMapping("/get/page.html")
public String getAdminPage(
@RequestParam(value = "keyword", defaultValue = "") String keyword,
@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "5") Integer pageSize,
ModelMap modelMap){
PageInfo<Admin> pageInfo = adminService.getAdminPage(keyword,pageNum,pageSize);
modelMap.addAttribute(CrowdConstant.ATTR_NAME_PAGE_INFO, pageInfo);
return "admin-page";
}
b、service层
其对应的 service 层接口通过 mapper 层提供的 selectAdminListByKeyword 接口获取数据,其中 PageHelper 为分页插件。
@Override
public PageInfo<Admin> getAdminPage(String keyword, Integer pageNum, Integer pageSize) {
if(pageNum > 0 && pageSize > 0) {
PageHelper.startPage(pageNum, pageSize);
}
List<Admin> list = adminMapper.selectAdminListByKeyword(keyword);
return new PageInfo<>(list);
}
c、mapper层
mapper 层用来编写 SQL 语句,下述 SQL 语句通过三个由 or 连接的模糊查询对 t_admin 表进行查询
<select id="selectAdminListByKeyword" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from
t_admin
where
login_acct like CONCAT("%",#{keyword, jdbcType=VARCHAR}, "%")
or user_name like CONCAT("%",#{keyword, jdbcType=VARCHAR}, "%")
or email like CONCAT("%",#{keyword, jdbcType=VARCHAR}, "%")
</select>
iii、用户新增
看 t_admin 表的结构可以发现,该表的 id 是自增的,而 create_time 是后端赋值,因此用户新增只需要前端上传剩下的四个参数即可。
a、handler层
handler 层的 save 方法通过与 t_admin 表对应的实体类 Admin 对象来接受参数,首先判断四个参数是否为空,接着生成参数 create_time 的值,并对上传的参数 user_pswd 进行加密,此处加密方法为 Spring Security 自带的加密方法,如果没有可以使用其他加密方法如 MD5 进行替代。Admin对象的参数都设置完毕后,就通过 service 层的 saveAdmin 方法新增用户。
@RequestMapping("/do/add.html")
public String save(Admin admin){
if("".equals(admin.getLoginAcct())
||"".equals(admin.getUserPswd())
||"".equals(admin.getUserName())
||"".equals(admin.getEmail())){
throw new AdminAddFailedException(CrowdConstant.MESSAGE_FORM_NOT_FULL);
}
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String createTime = simpleDateFormat.format(date);
admin.setCreateTime(createTime);
//String userPswd = CrowdUtil.encryptMd5(admin.getUserPswd());
String userPswd = bCryptPasswordEncoder.encode(admin.getUserPswd());
admin.setUserPswd(userPswd);
logger.info(admin.toString());
try{
adminService.saveAdmin(admin);
} catch (Exception e){
e.printStackTrace();
if(e instanceof DuplicateKeyException){
throw new AdminAddFailedException(CrowdConstant.MESSAGE_LOGINACCT_DUPLICATE);
}
throw e;
}
return "redirect:/admin/get/page.html?pageNum=" + Integer.MAX_VALUE;
}
b、service层
此处 service 层直接调用了 mapper 层的 insert 方法进行数据插入。
@Override
public void saveAdmin(Admin admin) {
adminMapper.insert(admin);
}
c、mapper层
数据插入操作在 mapper 层的 SQL 代码也很是循规蹈矩。
<insert id="insert" parameterType="com.alageek.crowd.entity.Admin">
insert into t_admin (id, login_acct, user_pswd,
user_name, email, create_time
)
values (#{id,jdbcType=INTEGER}, #{loginAcct,jdbcType=VARCHAR}, #{userPswd,jdbcType=CHAR},
#{userName,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{createTime,jdbcType=CHAR}
)
</insert>
iv、用户修改
a、handler层
用户修改与用户新增很是相似, edit 方法接受参数也是用了一个Admin对象,不过比起用户新增多接收了两个参数, pageNum 为分页页码, keyword 为用户检索的关键字,这俩参数的作用是为了执行用户修改后返回到原页面原位置。
@RequestMapping("/do/edit.html")
public String edit(Admin admin, Integer pageNum, String keyword){
if("".equals(admin.getLoginAcct())
||"".equals(admin.getUserName())
||"".equals(admin.getEmail())){
throw new AdminEditFailedException(CrowdConstant.MESSAGE_FORM_NOT_FULL);
}
logger.info(admin.toString());
try{
adminService.update(admin);
} catch (Exception e){
e.printStackTrace();
throw new AdminEditFailedException(CrowdConstant.MESSAGE_UPDATE_ERROR);
}
return "redirect:/admin/get/page.html?pageNum=" + pageNum + "&keyword=" + keyword;
}
b、service层
此处 service 层直接调用了 mapper 层的 update 方法进行数据更新。
@Override
public void update(Admin admin) {
adminMapper.updateByPrimaryKeySelective(admin);
}
c、mapper层
mapper 层选用 updateByPrimaryKeySelective 方法,是为了方便只更新一个参数的时候能够执行成功,不过显然 handler 层的对参数的非空判断,使得这个考虑并没有什么必要。
<update id="updateByPrimaryKeySelective" parameterType="com.alageek.crowd.entity.Admin">
update t_admin
<set>
<if test="loginAcct != null">
login_acct = #{loginAcct,jdbcType=VARCHAR},
</if>
<if test="userPswd != null">
user_pswd = #{userPswd,jdbcType=CHAR},
</if>
<if test="userName != null">
user_name = #{userName,jdbcType=VARCHAR},
</if>
<if test="email != null">
email = #{email,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
create_time = #{createTime,jdbcType=CHAR},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
v、用户删除
a、handler层
删除操作不需要除了 adminId 以外的其他参数,因为主键是唯一的, pageNum 与 keyword 是为了删除用户后可以回到原页面原位置。
@RequestMapping("/remove/{adminId}.html")
public String remove(
@PathVariable("adminId") Integer adminId,
@RequestParam("pageNum") Integer pageNum,
@RequestParam("keyword") String keyword){
adminService.remove(adminId);
return "redirect:/admin/get/page.html?pageNum=" + pageNum + "&keyword=" + keyword;
}
b、service层
此处 service 层直接调用了 mapper 层的 remove 方法进行数据删除。
@Override
public void remove(Integer adminId) {
adminMapper.deleteByPrimaryKey(adminId);
}
c、mapper层
数据删除操作在 mapper 层的 SQL 代码也很是循规蹈矩。
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from t_admin
where id = #{id,jdbcType=INTEGER}
</delete>
3、注意事项
CRUD 很简单,没啥好注意,唯一要注意的就是程序员职业生涯中,万万不可一直做 CRUD 工作,要尝试着接触新技术,将其融入工作中,只有这样,才能步步高升!