2018年12月

这阵子,一个前端的vue项目已经结束了,也自己上线了,对自己提升很大,巧逢今天和导师聊天,聊到了工作。
我们的背景就是,大专的学历和2年的学习经历,同期的2位非常好的朋友经导师推荐去朋友小厂面试;
由于这层关系,也就是需要2个”码农“,几个月整一个后端管理系统,对技术要求不高(ssm会做增删改查);
但是据说面试了一个月的应届生和培训出来的,没找到一个合适的,没有写出一个增删改查。

当时知道这个小厂对于我的2位朋友有着磨练的机会,所以今天就去了,结果3个小时之内,做到第二个小时就委婉的
让他们走了,我个人心里觉得有这样几个原因:

1.紧张(第一次这种环境下的机试)
据我所知,这两位朋友code功底蛮深的,不谈什么高大上的分布式线程熔断乱七八糟的技术,就光增删改查玩的还是很6的)
2.轻易相信的自己
我们的学校尤其是我们班都是以实战为主,2年不知道做过多少demo和项目练习,对自己的技术过于自信,没有认真准备,
导致轻视了这个机会,我相信他们一定会引以为戒

关于这个基础重要还是高大上的技术重要呢?
其实我的客观理解就是:深入java基础很重要,高大上技术是通过第三方的渠道了解到的,比如别人,视频,工作
所以为了我们这种即将春招的应届毕业生不要追求很多新潮的技术,也不要忘记了,不要轻信任何一个demo或者练习

u=2703915976,3722517054&fm=26&gp=0.jpg

品优购:

我们需要做一个功能就是完成商品的分类,需要操作的pojo是itemcat,我们的根分类的id是0,所以我们现在的需求是当我们点击哪一个的时候就要查询当前id的下级
所以我们的第一个service产生了,就是根据父类id查询子类:我们这样写:

/**

  * 根据父id查询对象
  */
 public List<TbItemCat> getItemByParentId(Long parentId) {
     TbItemCatExample example = new TbItemCatExample();
     Criteria createCriteria = example.createCriteria();
     // 匹配父ID
     createCriteria.andParentIdEqualTo(parentId);
     List<TbItemCat> selectByExample = itemCatMapper.selectByExample(example);
     return selectByExample;
 }

这样就我们能查询出来,action省略,我们来看看主要的前端代码:

初始化:ng-init="selectByParentId(0)"
传参为0,我们循环的是父级;

ng-click="findOne(entity.id)"

这样就可以在列表中点击哪个就出现哪个的分类了,我们这一部分完成了,现在的需求就是我们要完成面包屑的操作;

我们面包屑的层级是这样的,默认是父级是1,二级是2,三级是3;

然后我们定义一些方法:

1.首先是初始化层级和设置层级

//设置面包屑初始为0根目录

 $scope.grade = 1;
 //设置级别
 $scope.setGrade = function(value) {
     $scope.grade = value;
 }

2.判断当前的层级,如果层级是2,就把对应的参数传递层级2的对象中,然后通过这个对象中的id再查询

//读取列表

 $scope.selectGrade = function(p_entity) {
     //如果为一级
     if ($scope.grade === 1) {
          $scope.entity_1 = null;
          $scope.entity_2 = null;
     }
     if ($scope.grade === 2) {
          $scope.entity_1 = p_entity;
          $scope.entity_2 = null;
     }
     if($scope.grade === 3){
          $scope.entity_2 = p_entity;
     }
     
     $scope.selectByParentId(p_entity.id)
 }

然后我们的html可以这样写:

面包屑:

  • 顶级分类列表

  • {{entity_1.name}}

  • {{entity_2.name}}
  • 我们的列表的按钮:

    ng-click="setGrade(grade+1);selectGrade(entity)

    面包屑思路:当我们点击查询下一级的时候,会把层级关系加1,然后进行查询,查询的条件碰到了层级2,就会把它的对象变成entity_1,然后我们的面包屑上写的就是entity_1.name ; 所以我们就可以通过这样的方式来达到面包屑的效果。

    ----实现商品录入,在进行商品录入之前,我们需要知道2个概念:

    我们在写电商系统的时候,必须得要理解spu和sku是什么概念,具体就不阐述了,sku就是具体的商品列表,spu是商品的一些主干。
    这个商品的录入我们要结合2张表,一个是goods一个是goodsDesc,所以我们需要写一个组合表

    // 电商spu
     private TbGoods goods;
     // 商品拓展介绍
     private TbGoodsDesc goodsDesc;
     // 电商sku列表
     private List<TbItem> itemList;
    

    组合写好之后,我们需要更改mapper中,添加一个selectkey查询一下主键,这个主键的意图很简单,在添加商品成功之后,需要拿这个id做商品的拓展增加;

    所以我们的service层是这样写的:

    public void add(Goods goods) {

         // 增加商品记录
         // 设置未申请状态
         goods.getGoods().setAuditStatus("0");
         goodsMapper.insert(goods.getGoods());
         // 插入拓展信息,获取添加后的id
          goods.getGoodsDesc().setGoodsId(goods.getGoods().getId());
         // 添加
         goodsDescMapper.insert(goods.getGoodsDesc());
     }
    

    action同样省略(需要获取登陆名词并且set到里面去),我们这样就可以做好商品录入了,我们的前端进行对应的绑定即可,主要的是我们用到了一个富文本的编辑器:

    初始化,拿到editor对象,可以通过.html拿到里面的值进行添加到goodsDesc对象即可;

    FastDFS分布式文件管理服务器:

    我们开发过程中,可以只用一台服务器,这个服务器准备好了,运行开启即可,然后我们需要准备几个配置文件,一个是conf文件一个是简单封装了上传的文件
    具体的步骤我们可以看写的demo,不要忘记在springmvc中加入媒体解析器,上传后端代码:

     @RequestMapping("upload")
     public Result upload(MultipartFile file) {
         // 获取文件名
         String OriginalFilename = file.getOriginalFilename();
         // 截取文件的后缀
         String name = OriginalFilename.substring(OriginalFilename.lastIndexOf(".") + 1);
         try {
              util.FastDFSClient client = new FastDFSClient("classpath:config/fdfs_client.conf");
              String fileName = client.uploadFile(file.getBytes(), name);
              // 拼接图片url
              String url = images_server_url + fileName;
              return new Result(true, url);
         } catch (Exception e) {
              return new Result(false, "上传失败");
              // TODO: handle exception
         }
     }
    

    工具类在common中,util包中,需要加maven依赖才能引入,可以通过value注解来给images_server_url注入值,通过application.properties

    前端代码:

    this.upload = function() {

         //上传二进制文件需要的form对象
         var formData = new FormData();
         //添加文件到对象
         formData.append("file", file.files[0]); //name为file的第0个文件
         //执行上传,设置相关配置
         return $http({
              url : '../upload.do',
              method : "POST",
              //携带数据:
              data : formData,
              //设置头信息,如果不写,默认是json格式
              headers : {
                  'Content-Type' : undefined
              },
              //angular上传必须要写的:序列化
              transformRequest : angular.identity
         })
     }
    

    控制层:

    //上传图片

     $scope.upload = function() {
         $scope.imageEntity = {}
         uploadFile.upload().success(
              function(response) {
                  if (response.success) {
                       $scope.imageEntity.url = response.message
                  } else {
                       alert(response.message)
                  }
              }
         )
     }
     
     
     //定义实体结构
     $scope.entity = {goods:{},goodsDesc:{itemImages:[]}}
     //添加图片列表
     $scope.addImgList = function() {
          $scope.entity.goodsDesc.itemImages.push($scope.imageEntity)
     }
     //移出出图片列表
     $scope.deleImgList = function(index) {
          $scope.entity.goodsDesc.itemImages.splice(index, 1);
     }
    
    

    timg (1).jpg
    今天学习了这个SpringSecurity这个框架,记录一下注意的知识点和坑

    首先这个是一个安全框架,主要用途就是在登陆上,它为我们提供了很好的安全性和性能,为我们减少了很多开发需求

    我们可以花费一点点时间做一个入门小demo

    引入包和webxml加载配置我就不说了,都是复制粘贴的,最主要就是看我们的框架xml配置,我们需要了解一些功能。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans:beans xmlns="http://www.springframework.org/schema/security"

     xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd

    http://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security.xsd">

     
     
     <!-- 以下页面不拦截 -->
     <http pattern="/login.html" security="none"></http>
     <http pattern="/login_error.html" security="none"></http>
     <!-- 页面拦截规则 -->
     <http use-expressions="false">
         <intercept-url pattern="/**" access="ROLE_USER"/>
         <!-- 开启表单自动生成 -->
         <form-login default-target-url="/index.html" authentication-failure-url="/login_error.html"/>
         <csrf disabled="true"/>
     </http>
     
     <!-- 认证管理器 -->
     <authentication-manager>
         <authentication-provider>
              <user-service>
                  <user name="admin" password="admin" authorities="ROLE_USER"/>
              </user-service>
         </authentication-provider>
     </authentication-manager>

    </beans:beans>

    我们的页面拦截规则的pattern主要是不拦截资源,比如我们的一些登陆页面或者错误页面,这些页面是不需要拦截的。
    我们拦截的规则设置成跟目录下的所有资源,匹配的用户是ROLE_USER , 框架必须指定前缀是ROLE,这里要注意,我们可以开启表单自动生成,然后成功跳转到首页,错误跳转
    到错误页面。

    我们的认证管理器,主要是写死账号和密码。

    这样就可以给我们提供了一个自动生成的,包括html页面都是框架帮助我们生成好了,我们通过了一个入门小案例知道了这个框架的大体运用。

    然后开始运用到项目中,引入依赖和webxml就不敲了,我们还是来熟悉一下业务场景,首先我们不能让框架帮助我们生成丑陋的登陆页面了,我们需要指定自己的登陆页面。
    我们可以这样写:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans:beans xmlns="http://www.springframework.org/schema/security"

     xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd

    http://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security.xsd">

     <!-- 匹配不拦截的资源 -->
     <http pattern="/*.html" security="none"></http>
     <http pattern="/css/**" security="none"></http>
     <http pattern="/img/**" security="none"></http>
     <http pattern="/js/**" security="none"></http>
     <http pattern="/plugins/**" security="none"></http>
     <!-- 页面拦截规则 -->
     <http use-expressions="false">
         <intercept-url pattern="/**" access="ROLE_ADMIN" />
         <form-login login-page="/login.html" default-target-url="/admin/index.html"
              authentication-failure-url="/login.html" always-use-default-target="true" />
         <!-- 跨域拦截是否禁用 -->
         <csrf disabled="true" />
         <!--设置不拦截iframe框架内置 -->
         <headers>
              <frame-options policy="SAMEORIGIN" />
         </headers>
         <logout/>
     </http>
     <!-- 认证管理器 -->
     <authentication-manager>
         <authentication-provider>
              <user-service>
                  <user name="admin" password="123456" authorities="ROLE_ADMIN" />
              </user-service>
         </authentication-provider>
     </authentication-manager>
     

    </beans:beans>

    我们不拦截的资源就根据项目的场景来决定,我们在form中配置指定登陆页面,而且要加入一个成功默认跳转页面(很重要)
    认证管理器我们还是写死一个账号密码,让他可以登陆,这样我们就在管理员后台登陆的时候用到了这个安全框架,值得注意的是:

    前端html:action : /login 必须post方法 账号和密码的name必须是username和password

    然后我们开始做运营商的商家入驻申请其中非常关键的安全框架的查询数据库登陆+密码加密:

    查询数据库进行登陆,我们之前写的security写的是固定的admin和密码,我们如何查询数据库呢??

    我们需要自己在controller写一个包,在包下建立一个特殊的服务,我们在中配置扫描的是controller的包,所以我们这边的注入service是不起作用的通过注解
    因为这个服务是在doubbo远程注册,所以我们只能通过xml注入,我们先看看具体的查询服务:

    package com.pinyougou.service;

    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;

    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;

    import com.pinyougou.pojo.TbSeller;
    import com.pinyougou.sellergoods.service.SellerService;

    /**

    • 认证类
      *
    • @author LAOSHEN
      *
      */
      public class UserDetailsServiceImpl implements UserDetailsService {

      // 此服务注入通过dubbo远程注入
      private SellerService sellerService;

      public void setSellerService(SellerService sellerService) {

        this.sellerService = sellerService;

      }

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 添加角色集合
        List<GrantedAuthority> authorities = new ArrayList();
        authorities.add(new SimpleGrantedAuthority("ROLE_SELLER"));
        TbSeller seller = sellerService.findOne(username);
        // 判断用户是否存在
        if (seller != null) {
            // 判断用户是否通过审核
            if (seller.getStatus().equals("1")) {
                // 如果通过审核就生成改用户的正确密码与输入的进行匹配
                return new User(username, seller.getPassword(), authorities);
            } else {
                // 如果没有通过审核
                return null;
            }
        } else {
            // 如果没有此用户
            return null;
        }
    }

    }

    返回空就直接不能登陆成功,重定向到login页面中,最重要的是我们的安全框架的配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans:beans xmlns="http://www.springframework.org/schema/security"

     xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc"
     xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd

    http://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd
    http://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security.xsd">

     <!-- 匹配不拦截的资源 -->
     <http pattern="/*.html" security="none"></http>
     <http pattern="/css/**" security="none"></http>
     <http pattern="/img/**" security="none"></http>
     <http pattern="/js/**" security="none"></http>
     <http pattern="/plugins/**" security="none"></http>
     <http pattern="/seller/add.do" security="none"></http>   //把一些业务逻辑的url进行放行,否则无法进行业务逻辑的操作
     <!-- 页面拦截规则 -->
     <http use-expressions="false">
         <intercept-url pattern="/**" access="ROLE_SELLER" />
         <form-login login-page="/shoplogin.html" default-target-url="/admin/index.html"
              authentication-failure-url="/shoplogin.html" always-use-default-target="true" />
         <!-- 跨域拦截是否禁用 -->
         <csrf disabled="true" />
         <!--设置不拦截iframe框架内置 -->
         <headers>
              <frame-options policy="SAMEORIGIN" /> 
         </headers>
         <logout/> //注销选项,页面通过访问/logout 即可注销
     </http>
     <!-- 认证管理器 -->
     <authentication-manager>
         <authentication-provider user-service-ref="UserDetailsService">
              <password-encoder ref="bcryptEncoder"></password-encoder>  //依赖于加密实现类,登陆的时候可以把原密码自动加密,无需将加密后的密码解密进行匹配
         </authentication-provider>
     </authentication-manager>
     
     <beans:bean id="UserDetailsService" class="com.pinyougou.service.UserDetailsServiceImpl">
         <beans:property name="sellerService" ref="sellerService"></beans:property>  //认证类:注入service接口,从dubbo中,需要拥有set方法别忘记了
     </beans:bean>
     
     <!-- 引用dubbo 服务 ,从注册中心去寻找接口服务并且注入-->
     <dubbo:application name="pinyougou-shop-web" />
     <dubbo:registry address="zookeeper://192.168.25.151:2181"/>
     <dubbo:reference id="sellerService" interface="com.pinyougou.sellergoods.service.SellerService"></dubbo:reference>  //从dubbo注册地址找service接口的服务,并且注册给认证类
     
     
     <beans:bean class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" id="bcryptEncoder"></beans:bean>  //加密实现类

    </beans:beans>

    我们存入数据库的密码,可以通过在controller中这样定义:

    public Result add(@RequestBody TbSeller seller) {

         // 对密码进行加密存储到数据库
         BCryptPasswordEncoder passwordLock = new BCryptPasswordEncoder();
         String newPassWord = passwordLock.encode(seller.getPassword());
         seller.setPassword(newPassWord);
         try {
              sellerService.add(seller);
              return new Result(true, "增加成功");
         } catch (Exception e) {
              e.printStackTrace();
              return new Result(false, "增加失败");
         }
     }
    

    可以通过 BCrypt 类进行封装,让数据库存入加密之后的密码即可

    具体的可以看品优购的第四天的讲义。

    前段时间刚刚结束了我的18岁生日,一些很久不联系的朋友给我送上了祝福。
    简单的回忆一下最近几年发生的事情吧,很多人timg.jpg都已经知道我的经历了,高三辍学,学习编程,那个时候不到17岁,16岁可以说是,15岁左右就开始
    接触一点互联网灰色的产业,跟几个大哥哥一起写脚本赚一点收入,也算是满足了当时我一点点的虚荣心吧。
    我觉得,我长得那一瞬间是有一次放学回家,我把书房门一关上,书包中的书本取出来,电脑打开(假装写作业),然后每天看行业动态,写脚本更新
    软件,然后老妈一来我就假装写作业,然后那一天晚上,我觉得这样不是一个长久之计,就给妈妈坦白:“我想正规训练一下计算机”,就这样我就来到了西安某技校大专。
    在这之前,我觉得我的技术在同龄人已经很超前了,所以有沾沾自喜的自豪感:但是现实把我狠狠的打击一番,我几乎浪费了一年的时间,为什么这么说呢?我的这个浪费指的是没有那种渴望学习,因为没有学习路径,很迷茫很迷茫,到18年,我停了手里所有的业务线,只剩下一个博客;18年的6月份开始学习前端+Java;随身带笔记本,去图书馆查资料,看视频,看纯英文官方文档;对我的提升无疑是最大的,在短短几个月时间,我认识了非常多的好友,有跟我一样学习中的,有已经工作的程序员,他们有时候讨论的问题总是是我的下一个学习知识点,在当时我的技术栈是Vue和Java;
    转折发生了,班上应该是感觉到了快毕业了(为期2年),很多开始找工作的,测试啊,运维一大堆,都想找,我也想找,但是我有点坐井观天,想去面试看看面试官问我什么,我就写好简历(18岁,大专),去投了西安3个小厂,只有一个通知我面试:
    面试跟一个好朋友一起去的,他的原生js有正规的学习过,他21岁,当兵出来的;我就去了,那是一个比较大的小区,里面就是工作室,大概就是7个人左右的小团队,进去之后,老板不在,ok我忍,一个php的老阿姨(20出头吧)来跟我聊了一下,我就跟他说,简单的介绍了一下自己,我还以为他要问原生js的一些知识点或者vue的hook,或者通信协议之类的等等,结果什么都没问,说:“我们最近有一个项目是vue的,所以想找一个vue有经验的带带我们,我们也在学习” , 我懵了,朋友也懵了,这算什么?逗我吗?然后又问我:“会php吗?” 我说:“学过2天,但是如果需要这个的话,我可以一周上手一下框架快速学习一下” 她说:“你只会java是吧?” 我说:“是的” 她说:“nuxt框架会吗” (她当时说的是读的英文字母拼起来的) 觉得好不专业,我说我会,昨天还看了一些文档。 然后她又想了一下:“她说今天早上来了2个前端的再等通知,说简历先放在我这里,有实习机会的话就给我说” (百分之99凉了) 我觉得这次面试的尴尬有几个原因:“准备太仓促,那个时候才17岁,不能录用,简历太草率,项目经验没有写全等很多原因” 这次面试对我的帮助很大,我找到了方向,出了那个大门之后,我心中的自信伴随着复仇之火又起来了,接下来的2个月我学习了很多东西,也安排了明年和过年的计划。

    这两天解决了2天的bug,关于java的,老师也束手无策,我已经再崩溃的边缘,今天写这个文章是要警醒我自己,不能放弃任何一件事情,年龄虽小,可以用技术征服,但是心智小,是真的结束了。

    很久没更新了,最近写了很多笔记,读者可以下载印象笔记,可以分享出来大家一起研究一下。

    github地址:https://github.com/1018715564
    知乎:@胖版吴彦祖
    wechat:meng99huan