2021暑期学习第20天

前言

  • 时间:2021.8.6—8.21

  • 内容:

    • 关于映射会出的几点问题。(映射1)
    • 小众网前端:
      • 1 点击列表的某项书跳转至该书的详情页面。(联表查询)
      • 2 可指定地址进入注册页面。(映射2)
      • 3 注册页面有验证码。
      • 4 md5加密。(用到了盐值)
      • 5 图书状态(想看/已看)、点赞、短评
  • 备注:

    • 有些是8月21日才补充的笔记所以这篇看起来就乱了很多…前半部分自己的思考还是蛮有用滴~~(真是拖延癌晚期患者了…

    • 另外关于这个小项目,暂时不更新啦,代码已贴GitHub~~~~

      https://github.com/Hyidol/demo-xzw

1 详情页面(联表查询)

  • 这里就直接贴代码带过吧,就是前面写无数遍ssm的基础操作,难点就是一个联表查询

  • 思考结果:

    • 一般来说,我们是可以在控制层任意调用业务的,控制层的分类只是为了看起来清晰、更好理解一些。(提高可读性

    • 初学时的惯有思维,一个dao对应一个domain,一个service对应一个dao,但实际应用上~前者没啥问题,dao现在继承于baomidou的各种sql方法,不用自己写了;后者却不是,就比如联表查询的时候就会出现,一个service对应多个dao。

      @Service
      @Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly = true)
      public class EvaluationServiceImpl implements EvaluationService {
          @Autowired EvaluationDao evaluationDao;
          //对会员和书下手,需要注入这两个
          @Autowired BookDao bookDao;
          @Autowired MemberDao memberDao;
      
          @Override
          public List<Evaluation> selectEvaluationList(Long bookId) {
              QueryWrapper<Evaluation> queryWrapper = new QueryWrapper<>();
              queryWrapper.eq("bookId",bookId);
              queryWrapper.eq("statu","enable");
              queryWrapper.orderByDesc("createTime");
              //查出所有评论,针对这本书的
              List<Evaluation> evList = evaluationDao.selectList(queryWrapper);
              //书有了
              Book book = bookDao.selectById(bookId);
              for(Evaluation evaluation : evList){
                  //会员有了
                  Member member = memberDao.selectById(evaluation.getMemberId());
                  evaluation.setBook(book);
                  evaluation.setMember(member);
              }
              return evList;
          }
      }
      
    • 对应该业务同名包要加东西,感觉逻辑不太对,但总结一下来看好像也没啥不对hhh:评论表的数据库里是只有用户id没有用户信息的,那页面想要获取用户信息怎么办呢?发一个评论,没有发评论的名字总不对劲吧!所以怎么办呢?通俗说就是我们的联表查询,但这个联表查询是java后端方面的,不像以前攻一攻sql就能解决。

    • 太多废话了,步骤如下:

      • domain页面,类别里增加一个属性!!!

        @TableName
        public class Evaluation {
            @TableId(type = IdType.AUTO)
            @TableField("evaluationId")
            private Long evaluationId;
            private Long bookId;
            private Long memberId;
            private String content;
            private Integer score;
            private Date createTime;
            private Integer enjoy;
            private String statu;//状态 审核状态,enable 有效,disable 无效
            private Date disableTime;
            private String disableReason;
        
            //我们需要在这里,增加一些和表不对应的属性
            @TableField(exist = false)//表示 数据库表格中 不存在对应的字段,不会参与到sql的自动生成
            private Book book;//这个对象是由我们手动来查的
        
            @TableField(exist = false)
            private Member member;
        

2 映射

2-1 映射1

突然提起映射的问题,主要是遇上蛮多次ssm和某某视图view不匹配的情况,前面的总结提过,现在再补充个小结~

  1. 有可能是dao的不匹配:dao需要同名对应一个.java一个.xml,并且xml内的mapper空间需要写对应的java包路径。
  2. 有可能是applicationContext.xml里包问题:要么是扫描时候范围太小了,推荐扫com.pro,可以扫所有的@Controller和@Service;要么是类似我这种懒癌cv选手,在复制粘贴包的的时候,过于智能的idea帮我把路径改了(我该感谢它么??万里代码找不同…找着这个配置文件多了个com,ctrl+f去查找替换才解决)
  3. 有可能是控制器里的路径映射不到对应的页面:详见映射2

2-2 映射2

  • 思考过程:

    • 之前一直以为不加@ResponseBody这种搞地址的方式时,mapping里的参数要和方法名一样,今天突然发现是可以不一样的!
      • @GetMapping里的地址只是单纯用于地址栏的输入(以及前端找这个方法时候要喊的名字),随便写啥都行。
      • 方法名可以和@GetMapping的名字不一样,就像一个人除了自己的名字之外可以有外号一样,方法也是可以有专属自己的外号的,区别在于这个外号只能有一个(总不能在上头加两个GetMapping?(好像也不是不行…?(困惑(狗头
    • 另外关于GetMapping和PostMapping突然有些迷糊了,如果是直接在地址栏输入地址的,那是不是两种方式都可以呢?毕竟也没传数据过来,所以…get和post是不是只影响了“从前端ajax传数据过来”这种情况,对于“地址栏直接喊名字访问”这种情况是不影响的呢?(困惑)
    • 以及关于map,这个熟悉的陌生人,一直不太明明白它的机制,等后面慢慢理清了再小结下噢!!!!!!!
  • 以“在地址栏输入指定地址跳转至注册页面”为例:

    • 方法①

      @GetMapping("/a")
      public String b(){
          return "reg";
      }
      
    • 方法②

      @GetMapping("/reg.html")
      public ModelAndView toReg() {
          ModelAndView mav = new ModelAndView("reg");
          return mav;
      }
      
    • 方法③ 返回页面名字

      ModelMap mm = new ModelMap();
      
    • 方法④ 返回页面名字(这里写的返回参数应该是String,搞不太清,重新写笔记只分的清前两种惹…)

      public ModelAndView displayBookDetail(@PathVariable('bookId')Long bookId,Mode model){
          Book book = bookService.getBookId(bookId);
          model.addAttribute("book",book);
      }
      

3 验证码

3-1 验证码(显示)

pom.xml
  • maven里,只有这一个版本的噢!
<!--验证码-->
<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
</dependency>
applicationContext.xml
  • 增加一个bean

    <!--配置kaptcha-->
    <bean id="defaultKaptcha" class="com.google.code.kaptcha.impl.DefaultKaptcha">
        <property name="config">
            <bean class="com.google.code.kaptcha.util.Config">
                <constructor-arg><!--构造函数-->
                    <props>
                        <!--边框-->
                        <prop key="kaptcha.border">no</prop>
                        <!--宽度120px-->
                        <prop key="kaptcha.image.width">120</prop>
                        <!--颜色-->
                        <prop key="kaptcha.textproducer.font.color">pink</prop>
                        <!--大小-->
                        <prop key="kaptcha.textproducer.font.size">40</prop>
                        <!--几个字符-->
                        <prop key="kaptcha.textproducer.char.length">4</prop>
                    </props>
                </constructor-arg>
            </bean>
        </property>
    </bean>
    
KaptchaController.java
@Controller
public class KaptchaController {
    @Autowired
    protected Producer defaultKaptcha;

    @GetMapping("/verifyCode")
    public void createVerifyCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setDateHeader("Expires",0);//响应立即过期
        //不缓存图片
        response.setHeader("Cache-Control", "no-store,no-cache,must-revalidate");
        response.setHeader("Cache-Control", "post-check=0,pre-check=0");
        response.setHeader("Pragma", "no-cache");
        response.setContentType("image/png");

        //生成验证码文本
        String verifyCode = defaultKaptcha.createText();
        request.getSession().setAttribute("verifyCode", verifyCode);
        //这里打印出来,一会试验时候,看看对比一下,是否一致
        System.out.println(request.getSession().getAttribute("verifyCode"));

        //变魔术,变成图片
        BufferedImage image = defaultKaptcha.createImage(verifyCode);
        //输出流,从服务器到客户端,架了一个管道,将那个图片验证码,发送到你的页面中
        ServletOutputStream outputStream = response.getOutputStream();
        //写,把图通过管道,写到你的客户端
        ImageIO.write(image, "png", outputStream);
        outputStream.flush();
        outputStream.close();
        //io流。什么是流,有哪些流?
    }
}
  • 这里的Producer不是自己写的类,是kaptcha里的一个类

    package com.google.code.kaptcha;
    
    import java.awt.image.BufferedImage;
    
    public interface Producer {
        BufferedImage createImage(String var1);
    
        String createText();
    }
    

3-2 验证码(验证)

  • 主要是前端的操作啦,因为做笔记和写代码时间隔的有丢丢久

  • 登录的验证

    <script>
        //点击验证码图片刷新验证码
        $("#imgVerifyCode").click(function () {
            reloadVerifyCode();
        });
        //重新发送请求,刷新验证码
        function reloadVerifyCode(){
            //这里实现刷新验证码,请求中设置时间差。
            $('#imgVerifyCode').attr("src","verifyCode?tp="+new Date().getTime());
        }
        //提交表单的操作
        $(function () {
            $('#btnSubmit').click(function () {
                $.ajax({
                    url:'/xzw/checkLogin',
                    type:'post',
                    dataType:'json',
                    data:$('#frmLogin').serialize(),
                    success:function (data) {
                        if(data.code=="ok_vc"){
                            alert(data.msg);
                            //去首页
                            window.location="/xzw/index.html?tp="+new Date().getTime();
                        }else {
                            //刷新页面,重新登录
                            alert(data.msg);
                            $('#password').val("")
                            $('#nickname').val("")
                            $('#verifyCode').val("")
                            reloadVerifyCode();
                        }
                    }
                });
    
            });
        });
    </script>
    
  • 注册的验证

    <script>
        //点击验证码图片刷新验证码
        $("#imgVerifyCode").click(function () {
            reloadVerifyCode();
        });
        //重新发送请求,刷新验证码
        function reloadVerifyCode(){
            //请在这里实现刷新验证码,请求中设置时间错。
            $('#imgVerifyCode').attr("src","verifyCode?tp="+new Date().getTime());
        }
        //提交表单的操作
        $("#btnSubmit").click(function () {
            //表单校验
            var username = $.trim($("#username").val());
            var regex = /^.{6,10}$/;
            if (!regex.test(username)) {
                alert("用户名请输入正确格式(6-10位)");
                return;
            }
    
            var password = $.trim($("#password").val());
    
            if (!regex.test(password)) {
                alert("密码请输入正确格式(6-10位)");
                return;
            }
    
            $btnReg = $(this);
    
            $btnReg.text("正在处理...");
            $btnReg.attr("disabled", "disabled");
    
            //发送ajax请求
            $.ajax({
                url: "regist",
                type: "post",
                dataType: "json",
                data: $("#frmLogin").serialize(),
                success: function (data) {
                    //结果处理,根据服务器返回code判断服务器处理状态
                    //服务器要求返回JSON格式:
                    //{"code":"0","msg":"处理消息"}
                    console.info("服务器响应:" , data);
    
                    if (data.code == "ok_vc") {
                        //显示注册成功对话框
                        alert(data.msg)
                        $('#username').val("")
                        $('#password').val("")
                        $('#nickname').val("")
                        $('#verifyCode').val("")
                        reloadVerifyCode();
                    } else {
                        //服务器校验异常,提示错误信息
                        alert(data.msg);
                        //错误,则重新生成验证码
                        reloadVerifyCode();
                    }
                }
            });
            return false;
        });
    </script>
    

4 md5加密

  • 这个加密技术是用来干啥的呢?我们可以想一下噢,如果我们注册账号的时候,填的密码是明明白白进入数据库的,那我们的账号密码岂不是被数据库管理员看光光惹?所以,为了用户和开发彼此的信任,就出现了这种加密的中间环节:用户上传密码时候会给密码加密,所以管理员端收到的用户密码是一串很长很长的加密过的字符串,而用户登录账号去和后台匹配密码时候,也会用同样的方式加密密码再匹配。

  • 这个地方有个小小bug,比如说我的密码是123456,再申请了个别的账号密码也是123456,那么记录进数据库的两条密码也是相同的字符串。。。

  • 代码如下:

    package com.pro.util;
    
    import org.apache.commons.codec.digest.DigestUtils;
    
    /**
     * @author Yuhua
     * @since 21.8.5 11:20
     */
    public class MD5Util {
        /**
         *
         * @param source 准备加密梳理的
         * @param salt
         * @return
         */
        public static String md5Digest(String source,Integer salt){
            char[] ca = source.toCharArray();
            /*for (int i = 0; i < ca.length; i++) {
                ca[i] = (char) (ca[i]+salt);
            }*/
            //字符数组-->字符串
            String target = new String(ca);
            String md5 = DigestUtils.md5Hex(target);
            //abcd-->adfadsfasdfadfsa
            return md5;
        }
    }
    
  • MemberServiceImpl.java

    //注册
    @Override
    public Member createMember(String username, String password, String nickname){
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("username",username);
    
        List<Member> memberList = memberDao.selectList(queryWrapper);
        //根据username,如果查到了,说明已经被注册了
        if(memberList.size()>0){
            //不能注册
            throw new ServiceException("801","该用户名已被注册了!");
        }
        //
        Member member = new Member();
        member.setUsername(username);
        member.setNickName(nickname);
        //密码域
        int salt = new Random().nextInt(1000) + 1000;
        String md5 = MD5Util.md5Digest(password,salt);
        member.setPassword(md5);
        member.setSalt(salt);
        member.setCreateTime(new Date());
        System.out.println(member.toString());
        //添加
        memberDao.insert(member);
        return member;
    }
    
    //登录,查用户及密码,是否正确
    @Override
    public Member checkLogin(String username, String password){
        QueryWrapper<Member> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username",username);
    
        Member member = memberDao.selectOne(queryWrapper);
        if(member==null){
            throw new ServiceException("802","用户不存在");
        }
        //可以站到这里,说明表中有这个用户
        String md5 = MD5Util.md5Digest(password, member.getSalt());
        if(!md5.equalsIgnoreCase(member.getPassword())){
            throw new ServiceException("803", "输入密码不对");
        }
        //如果代码可以走到这里,说明都正确
        return member;
    }
    

5 图书状态、点赞、短评

  • 这部分直接上代码叭。。不全的直接去GitHub上搞来康康。。。地址贴在文章开头惹。。。

    <script>
        $.fn.raty.defaults.path = '/xzw/resources/raty/lib/images';
        $(function () {
            $(".stars").raty({readOnly: true});
        })
    
        $(function () {
            //注意,注释
            <!-- 状态不为空,则设置高亮,将查出的阅读状态对应的元素,加为高亮 ??这俩问号表示不为null-->
            <#if memberReadState ??>/*如果状态存在*/
                $('[data-read-state="${memberReadState.readState}"]').addClass('highlight');
            </#if>
    
            //如果没有登录,则阅读状态,写评论,点赞,都不能操作
            <#if !loginMember ??>
                $('[data-read-state],#btnEvaluation,[data-evaluation-id]').click(function () {
                    alert("请先登录,才能操作!")
                })
            </#if>
    
            <#if loginMember ??>
            $('[data-read-state]').click(function () {
                var readState = $(this).data('read-state');
                //post是4个参数
                $.post('/xzw/updateReadState',{
                    "memberId":${loginMember.memberId},
                    "bookId":${book.bookId},
                    "readState":readState
                },function () {
                    if(data.code=="ok_mrs"){
                        $('[data-read-state]').removeClass('highlight');//清除高亮
                        $('[data-read-state="'+readState+'"]').addClass('highlight');
                    }
                },'json');
                window.location.reload(true);
            });
    
            //点击短评按钮
            $("#btnEvaluation").click(function () {
                $('#score').raty({});//将span转为星形组件
                //显示写短评div
                $('#dlgEvaluation').modal('show');
            })
    
            //提交点评
            $("#btnSubmit").click(function () {
                var score = $('#score').raty("score");
                var content = $('#content').val();
                //没有内容,不提交
                if(score==0||$.trim(content)==""){
                    return;
                }
                $.post("/xzw/evaluate",{
                    "score":score,
                    "bookId": ${book.bookId},
                    "memberId":${loginMember.memberId},
                    "content":content
                },function (data) {
                    if(data.code=="ok_ev"){
                        //重新加载当前页,得到最新的评论
                        window.location.reload();
                    }
                },'json')
            });
            $('[data-evaluation-id]').click(function(){
                var evaluationId = $(this).data("evaluation-id");
                $.post('/xzw/enjoy',{"evaluationId":evaluationId},function (data) {
                    if(data.code=="ok_en"){
                        window.location.reload();//这句话好像没啥用?!!
                        $('[data-evaluation-id="'+evaluationId+'"] span').text(data.evaluation.enjoy)
                    }
                },'json');
            });
            </#if>
        });
    
    </script>
    

前端的一个东西

display:none和jQuery的show是对应的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值