《5K入门级项目实战:好来屋在线影院》之第 9 战 —— 电影信息管理

OK,这一节讲解后台管理的最后一个功能:电影信息管理。主要对“添加电影信息”的列表管理。

我们先在 html 目录下,创建一个movieManage.html文件

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>电影管理页面</title>
<link rel="stylesheet" type="text/css" href="/static/jquery-easyui-1.7.0/themes/default/easyui.css"></link>
<link rel="stylesheet" type="text/css" href="/static/jquery-easyui-1.7.0/themes/icon.css"></link>
<script type="text/javascript" src="/static/jquery-easyui-1.7.0/jquery.min.js"></script>
<script type="text/javascript" src="/static/jquery-easyui-1.7.0/jquery.easyui.min.js"></script>
<script type="text/javascript" src="/static/jquery-easyui-1.7.0/locale/easyui-lang-zh_CN.js"></script>

<script type="text/javascript">
    //删除电影信息
    function deleteMovie(){
        var selectedRows=$("#dg").datagrid("getSelections");
        if(selectedRows.length == 0){
            $.messager.alert("系统提示","请选择要删除的数据!");
            return;
        }
        var idArr = [];
        for(var i = 0;i < selectedRows.length;i++){
            idArr.push(selectedRows[i].id);
        }
        var ids = idArr.join(",");
        $.messager.confirm("系统提示","您确定要删除这<font color=red>"+selectedRows.length+"</font>条数据吗?",function(r){
            if(r){
                $.post("/admin/movie/delete",{ids:ids},function(result){
                    if(result.flag){
                        $.messager.alert("系统提示","数据已成功删除!");
                    }else{
                        $.messager.alert("系统提示",result.errorMsg);
                    }
                    $("#dg").datagrid("reload");//重新加载网格数据
                },"json");
            }
        });
    }

    //打开修改电影信息的 tab
    function openMovieModifyTab(){
        var selectedRows = $("#dg").datagrid("getSelections");
        if(selectedRows.length != 1){
            $.messager.alert("系统提示","请选择一个要修改的电影!");
            return;
        }
        var row = selectedRows[0];
        window.parent.openTab('修改电影信息','modifyMovie.html?id='+row.id,'icon-edit');
    }

    //格式化电影图片,src 的路径是一个虚拟的路径,WebConfig 类会映射本地磁盘的真实路径。前面有讲解过
    function formatImageName(val,row){
        return "<img width=100 height=100 src='/movieImages/"+val+"'>";
    }

    //格式化是否热门
    function formatHot(val,row){
        if(val == 1){
            return "是";
        }else{
            return "否";
        }
    }

    //查询电影信息
    function searchMovie(){
        $("#dg").datagrid('load',{
            queryParams: {
                'movieName': $("#nameId").val()
            }
        });
    }

    //格式化发布日期
    function formatCreateTime(val,row) {
        if (val != null) {
            var date = new Date(val);
            return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate();
        }
    }

</script>
</head>
<body style="margin: 1px">
<table id="dg" title="电影管理" class="easyui-datagrid"
       fitColumns="true" pagination="true" rownumbers="true"
       url="/admin/movie/list" fit="true" toolbar="#tb">
    <thead>
    <tr>
        <th field="cb" checkbox="true" align="center"></th>
        <th field="id" width="20" align="center">编号</th>
        <th field="imageName" width="50" align="center" formatter="formatImageName">电影主图</th>
        <th field="movieName" width="100" align="center">电影名称</th>
        <th field="movieTitle" width="200" align="center">电影标题</th>
        <th field="hot" width="30" align="center" formatter="formatHot">是否热门</th>
        <th field="createTime" width="150" align="center" formatter="formatCreateTime">发布日期</th>
    </tr>
    </thead>
</table>
<div id="tb">
    <div>
        <a href="javascript:openMovieModifyTab()" class="easyui-linkbutton" iconCls="icon-edit" plain="true">修改</a>
        <a href="javascript:deleteMovie()" class="easyui-linkbutton" iconCls="icon-remove" plain="true">删除</a>
    </div>
    <div>
        &nbsp;电影名称:&nbsp;<input type="text" id="nameId" size="20" onkeydown="if(event.keyCode==13) searchMovie()"/>
        <a href="javascript:searchMovie()" class="easyui-linkbutton" iconCls="icon-search" plain="true">搜索</a>
    </div>
</div>

</body>
</html>

接下来,我们开发 API 接口,用来查询电影信息的列表。 

跟上一篇博客讲解的差不多,直接上代码,这部分功能写在 MovieInfoController即可:

    //分页查询电影信息
    @ResponseBody
    @RequestMapping("/list")
    public Map<String, Object> list(SearchInfo info, @RequestParam(value = "page", required = false) Integer page, @RequestParam(value = "rows", required = false) Integer rows) {
        Map<String, Object> map = movieInfoManage.getMovieList(info, page, rows);
        return map;
    }

MovieInfoManage接口增加代码:

    //分页查询电影信息
    Map<String, Object> getMovieList(SearchInfo info, int page, int rows);

MovieInfoManageImpl实现类增加代码:

    //分页查询电影信息
    @Override
    public Map<String,Object> getMovieList(SearchInfo info, int page, int rows){
        Map<String,Object> map = new HashMap<>();
        try{
            PageQuery pageQuery = SearchInfoUtil.getPageQuery(info,page,rows);
            List<MovieInfoEntity> list = movieInfoService.findBy(pageQuery);
            map = SearchInfoUtil.getResult(pageQuery,list);//封装结果
        }catch (Exception e){
            e.printStackTrace();
        }
        return map;
    }

MovieInfoService类增加代码:

    //分页查询
    @Transactional(readOnly = true)
    public List<MovieInfoEntity> findBy(PageQuery pageQuery){
        return movieInfoDao.findBy(pageQuery);
    }

MovieInfoDao接口增加代码:

    //分页查询
    List<MovieInfoEntity> findBy(PageQuery pageQuery);

MovieInfoEntityMapper.xml 文件增加代码(根据主键 id 倒序):

    <!-- 分页查询 -->
    <select id="findBy" resultMap="BaseResultMap">
        select *
        from movie_info
        <where>
            <if test=" movieName != null and movieName != '' ">
                and movie_name like CONCAT('%',#{movieName,jdbcType=VARCHAR}, '%')
            </if>
        </where>
        order by id desc
    </select>

OK,重启服务,看下效果:

老铁们,没毛病!

接下来,测试查询功能,搜索框输入电影名称,支持模糊查询:

OK,搞定。

接下来,我们开发删除电影信息的 API 接口。在删除信息前,我们先弄一些测试数据,这里教一个方法:

insert into movie_info
(select null,movie_name,movie_title,hot,image_name,movie_content,create_time
from movie_info where id = 1);

多执行几次,就可以把电影信息表的 id=1 的信息新增到电影表中,做一些测试。

OK,开发删除接口,需要注意请求的 URL 路径,以及返回的 key。

 

MovieInfoController类增加代码:

    //删除电影信息
    @ResponseBody
    @RequestMapping("/delete")
    public Map<String,Object> delete(@RequestParam(value="ids")String ids){
        Map<String, Object> map = new HashMap<>();
        boolean flag = movieInfoManage.delete(ids);
        map.put("flag", flag);
        return map;
    }

MovieInfoManage接口增加函数: 

    //删除电影信息
    boolean delete(String ids);

MovieInfoManageImpl实现类增加代码:

    //删除电影信息
    @Override
    public boolean delete(String ids) {
        boolean flag = false;
        try {
            //用逗号分隔前端传递的 id 集合
            String[] idsArr = ids.split(",");
            for(String id : idsArr){
                movieInfoService.delete(Integer.parseInt(id));
            }
            flag = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }

MovieInfoService增加代码: 

    //删除电影信息
    @Transactional(readOnly = false)
    public int delete(Integer id){
        return movieInfoDao.delete(id);
    }

MovieInfoDao增加代码: 

    //删除电影信息
    int delete(Integer id);

MovieInfoEntityMapper.xml增加代码(之前已经有了无需增加):

    <!-- 根据主键 id 删除 -->
  <delete id="delete" parameterType="java.lang.Integer">
    delete from movie_info
    where id = #{id,jdbcType=INTEGER}
  </delete>

OK,重启服务,测试:

OK,搞定!

还有一个功能,就是“修改”电影信息。

我们以一个新的 tab 页打开,把电影的主键 id 带过去。

OK,我们新建 modifyMovie.html 文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>修改电影信息页面</title>
    <link rel="stylesheet" type="text/css" href="/static/jquery-easyui-1.7.0/themes/default/easyui.css"></link>
    <link rel="stylesheet" type="text/css" href="/static/jquery-easyui-1.7.0/themes/icon.css"></link>
    <script type="text/javascript" src="/static/jquery-easyui-1.7.0/jquery.min.js"></script>
    <script type="text/javascript" src="/static/jquery-easyui-1.7.0/jquery.easyui.min.js"></script>
    <script type="text/javascript" src="/static/jquery-easyui-1.7.0/locale/easyui-lang-zh_CN.js"></script>
    <script type="text/javascript" src="/static/ckeditor/ckeditor.js"></script>
</head>
<script type="text/javascript">
    //提交修改
    function submitData(){
        $("#fm").form("submit",{
            url:"/admin/movie/save", //仍然使用 save 方法,差别在于修改的时候有主键 id
            onSubmit:function(){
                var content= CKEDITOR.instances.content.getData()
                if(content == ""){
                    $.messager.alert("系统提示","内容不能为空!");
                    return false;
                }
                return $(this).form("validate");
            },
            success:function(r){
                var result = eval('('+r+')');
                if(result.flag){
                    $.messager.alert("系统提示","保存成功!");
                    resetValue();
                }else{
                    $.messager.alert("系统提示","保存失败!");
                    return;
                }
            }
        });
    }

    //重置数据
    function resetValue(){
        $("#movieName").val("");
        $("#movieTitle").val("");
        $("#imageFile").val("");
        $("#no").prop("checked", true);
        CKEDITOR.instances.content.setData("");
    }

</script>
</head>
<body style="margin: 10px">
<div id="p" class="easyui-panel" title="修改电影信息" style="padding: 10px">
    <form id="fm" method="post" enctype="multipart/form-data">
        <table cellspacing="20px">
            <tr>
                <td width="80px">电影名称:</td>
                <td><input type="text" id="movieName" name="movieName" style="width: 400px;" class="easyui-validatebox" required="true"/></td>
            </tr>
            <tr>
                <td width="80px">电影标题:</td>
                <td><input type="text" id="movieTitle" name="movieTitle" style="width: 400px;" class="easyui-validatebox" required="true"/></td>
            </tr>
            <tr>
                <td width="80px" valign="top">电影主图:</td>
                <td>
                    <input type="hidden" id="imageName" name="imageName" />
                    <input type="file" id="imageFile" name="imageFile" />
                </td>
            </tr>
            <tr>
                <td width="80px">是否热门:</td>
                <td><input type="radio" id="yes" value="1" name="hot" ><label for="yes">是&nbsp;</label>
                    <input type="radio" id="no" value="0" checked="checked" name="hot" /><label for="no">否&nbsp;</label></td>
            </tr>
            <tr>
                <td valign="top">电影详情:</td>
                <td>
                    <!-- 在引入 ckeditor.js 的文件里,textarea id="content" 会自动变成富文本编辑器 -->
                    <textarea id="content" name="movieContent" rows="20" cols="100"></textarea>
                </td>
            </tr>
            <tr>
                <td></td>
                <td>
                    <input type="hidden" id="id" name="id" />
                    <a href="javascript:submitData()" class="easyui-linkbutton" data-options="iconCls:'icon-submit'">发布信息</a>
                </td>
            </tr>
        </table>
    </form>
</div>

<script>
    CKEDITOR.replace( 'content' );

    //获取从上一个页面的路径传递的 id 值
    function getQueryString(name) {
        var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
        var url = decodeURI(decodeURI(window.location.search))
        var r = url.substr(1).match(reg);
        if (r != null) return unescape(r[2]); return null;
    }

    var id = getQueryString("id");
    console.log("id="+id);//打印到控制台
    //根据电影信息 id 查询数据
    $.post("/admin/movie/selectById",{id:id},function(result){
        $("#id").val(result.id);
        $("#movieName").val(result.movieName);
        $("#movieTitle").val(result.movieTitle);
        $("#imageName").val(result.imageName);
        if(result.hot == 1){
            $("#yes").prop("checked", true);
        }else{
            $("#no").prop("checked", true);
        }
        CKEDITOR.instances.content.setData(result.movieContent);//电影详情
    },"json");

</script>
</body>
</html>

说明:

1、修改电影信息的请求 API 我们仍然使用 save 方法,不过需要做一点小变动:判断是否有主键 id,有则修改,无则新增。稍后说。

2、我们把电影主图也带出来了,只是不显示(hidden),而定义一个 type = "file" 的控件,重新上传。如果不选择文件,则主图不变。后台 API 有判断 file 是否是空的。

3、获取上一个页面传递的 id,以及调用 selectById 方法查询后台返回的电影信息,并设置到对应的位置中。

 

OK,我们看下后台 API 如何改造。

MovieInfoController类新增代码:

    //根据 id 查询电影信息
    @ResponseBody
    @RequestMapping("/selectById")
    public MovieInfoEntity selectById(@RequestParam(value="id")Integer id){
        MovieInfoEntity entity = movieInfoManage.selectById(id);
        return entity;
    }

MovieInfoManage接口新增代码:

    //根据 id 查询电影信息
    MovieInfoEntity selectById(Integer id);

MovieInfoManageImpl实现类新增代码,同时修改 save 方法的判断逻辑:判断是否有主键 id

    //根据 id 查询电影信息
    @Override
    public MovieInfoEntity selectById(Integer id){
        MovieInfoEntity infoEntity = null;
        try{
            infoEntity = movieInfoService.selectById(id);
            //将获取到的电影信息进行解码,save 方法里有提到过
            if(null != infoEntity){
                //解码只需一个方法即可,解码后返回给前端,跟传递进来的时候是一样的。
                String movieContent = HtmlUtils.htmlUnescape(infoEntity.getMovieContent());
                infoEntity.setMovieContent(movieContent);//重新设置属性值
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return infoEntity;
    }
//新增电影信息
    @Override
    public boolean save(MovieInfoEntity entity, MultipartFile file) {
        boolean flag = false;
        try {
            if (!file.isEmpty()) {
                String originalFilename = file.getOriginalFilename();//获取文件名
                String suffixName = originalFilename.substring(originalFilename.lastIndexOf("."));//获取文件后缀
                String newFileName = System.currentTimeMillis() + suffixName;//新的文件名,避免文件名重复
                FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(imageFilePath + newFileName));
                entity.setImageName(newFileName);//设置到实体中
            }

            //将富文本的 html 代码转换特殊字符,防止对网站进行xss跨站攻击
            //方案 1:转成十进制
            String movieContent = HtmlUtils.htmlEscapeDecimal(entity.getMovieContent());
            //方案 2:转成十六进制
            //String movieContent = HtmlUtils.htmlEscapeHex(entity.getMovieContent());

            entity.setMovieContent(movieContent);

            int count = 0;
            if(null != entity.getId()){//有主键 id,是修改
                count = movieInfoService.update(entity);
            }else{//没有主键id,是新增
                count = movieInfoService.insert(entity);
            }
            if (count > 0) {
                flag = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }

MovieInfoService新增代码:

    //根据 id 查询电影信息
    @Transactional(readOnly = true)
    public MovieInfoEntity selectById(Integer id){
        return movieInfoDao.selectById(id);
    }

    //更新电影信息
    public int update(MovieInfoEntity entity){
        return movieInfoDao.update(entity);
    }

MovieInfoDao新增代码:

    //根据 id 查询电影信息
    MovieInfoEntity selectById(Integer id);

    //更新电影信息
    int update(MovieInfoEntity entity);

MovieInfoEntityMapper.xml新增代码(之前已经有的话可以不新增):

  <!-- 更新 -->
  <update id="update" parameterType="com.movie.entity.MovieInfoEntity">
    update movie_info
    <set>
        <if test=" movieName != null and movieName != '' ">
            movie_name = #{movieName,jdbcType=VARCHAR},
        </if>
        <if test=" movieTitle != null and movieTitle != '' ">
            movie_title = #{movieTitle,jdbcType=VARCHAR},
        </if>
        <if test=" hot != null and hot != '' ">
            hot = #{hot,jdbcType=CHAR},
        </if>
        <if test=" imageName != null and imageName != '' ">
            image_name = #{imageName,jdbcType=VARCHAR},
        </if>
        <if test=" movieContent != null and movieContent != '' ">
            movie_content = #{movieContent,jdbcType=LONGVARCHAR}
        </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>

    <!-- 根据主键 id 查询 -->
  <select id="selectById" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select id, movie_name, movie_title, hot, image_name, movie_content,create_time
    from movie_info
    where id = #{id,jdbcType=INTEGER}
  </select>

OK,我们做测试:

点击“修改”,打开新的 tab 页面

 

然后我们回到列表页面,点击分页下面的 刷新 按钮,即可查看到最新数据。

 

 

OK,到这里,我们的后台管理已经全部搞定。后面继续讲解用户端的使用页面。

 

 


版权声明:本文为BiandanLoveyou原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。