今日目标:
1、完成商家管理的安全框架控制
2、完成登录的密码加密(BCrypt加密算法)
3、商家登录登出的用户名显示
4、商品录入功能实现之商品基本信息录入
一、商家管理加入安全框架
分析:在运营商管理的中我们,我们的用户名和密码都是在spring-srcurity.xml文件中配置是写死的,那么我们在商家中,我们通过获得用户的数据库密码,来实现登录
1.首先导入相关的mavenjar包
spring-security-web spring-security-conf
2、导入web.xml中的过滤器链,以及spring-security.xml问题(放行一些不需要拦截的资源)
<!-- 以下页面不被拦截 --> <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="/shoplogin.html" security="none"></http> <http pattern="/register.html" security="none"></http> <!--不拦截方法--> <http pattern="/selller/add.do" security="none"></http>
3、配置认证服务类,完成认证,授权工作
<!-- 认证管理器 --> <authentication-manager> <!--自定义认证服务类--> <authentication-provider user-service-ref="userDetailService"> </authentication-provider> </authentication-manager> <beans:bean id="userDetailService" class="com.pinyougou.user.service.UserDetailService"></beans:bean>
4、在shop的controller层,添加userService包下的userDetailServiceImpl 继承 userDetailService
public class UserDetailService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //构建用户权限集合数据 List<GrantedAuthority> authorities = new ArrayList<>(); authorities.add(new SimpleGrantedAuthority("ROLE_SELLER")); //参数一:用户名 参数二;密码 权限集合 return new User(username,"123456",authorities); } }
注意我们在这一步先写死密码,等会我们在从数据库中查
5.编写shopindex.xml页面
6.从数据库中查询密码
分析:我们知道shop中dubbo扫描的是controller,而我们的包在user.service中,所以我们要通过配置类,来出入duboo,这样才能用reference。
在userDetailService配置set方法
private SellerService sellerService; public void setSellerService(SellerService sellerService) { this.sellerService = sellerService; }
在配置文件中配置
<!--认证服务类--> <beans:bean id="userDetailService" class="com.pinyougou.user.service.UserDetailService"> <beans:property name="sellerService" ref="sellerService"></beans:property> <!--name指的是 private SellerService sellerService; 中的SellerService 应用的是下面的dubbo服务的id为sellerService --> </beans:bean> <!-- 引用dubbo 服务 --> <dubbo:application name="pinyougou_shop_web" /> <dubbo:registry address="zookeeper://192.168.25.128:2181"/> <dubbo:reference interface="com.pinyougou.sellergoods.service.SellerService" id="sellerService" />
注意:我们要在user.service中添加判断
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { TbSeller tbSeller = sellerService.findOne(username); if (tbSeller!=null){ //必须判断状态,只有审核通过的状态,才能正常登录 if("1".equals(tbSeller.getStatus())){ //构建用户权限集合数据 List<GrantedAuthority> authorities = new ArrayList<>(); authorities.add(new SimpleGrantedAuthority("ROLE_SELLER")); //参数一:用户名 参数二;密码 权限集合 return new User(username,tbSeller.getPassword(),authorities); }else{ return null; } }else { return null; } }
7、密码的加密行为,作为注册是的密码
分析:商家入驻密码加密
MD5 哈希算法,不可逆 加密长度:32位
BCrypt 加盐 123456 加密后的字符串是60位
123456 + 加盐(随机字符串)
我们采用spring-security提供的加密算法进行加密,注意在controller层完成,因为,在controller层才有spring-security的框架配置
<!-- 认证管理器 --> <authentication-manager> <!--自定义认证服务类--> <authentication-provider user-service-ref="userDetailService"> <!--基于passwordEncoding对登录传输的明文密码进行加密--> <password-encoder ref="passwordEncoder"></password-encoder> </authentication-provider> </authentication-manager>
<!--登录需要对输入的密文及进行加密--> <beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></beans:bean>
登录的时候也通过bcryptPasswordEncoder加密
二、商家后台商品录入
商家的商品录入有点,复杂,我们一共设计到八张表
SPU: 标准生产单位 例如:iPhone6s
SKU:最小库存量单位 最小销售单元 例如:金色、联通4G、64G iphone6s
SPU = Standard Product Unit (标准产品单位)
SPU 是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述
了一个产品的特性。
通俗点讲,属性值、特性相同的商品就可以称为一个 SPU。
例如:
iphone7 就是一个 SPU,与商家,与颜色、款式、套餐都无关。
SKU=stock keeping unit( 库存量单位)
SKU 即库存进出计量的单位, 可以是以件、盒、托盘等为单位。
SKU 是物理上不可分割的最小存货单元。在使用时要根据不同业态,不同管理模式来处理。
在服装、鞋类商品中使用最多最普遍。
SPU 与SKU 是一对多关系。
SPU: iPhone6s
颜色: 金色 黑色 银色
机身内存: 64G 128G
SKU:
金色、64G iphone6s
黑色、64G iphone6s
银色、64G iphone6s
金色、128G iphone6s
黑色、128G iphone6s
银色、128G iphone6s
一对多,谁维护关联关系呢?
多方维护
商品表关系分析
商品相关表:
SPU :tb_goods tb_goods_desc(是对tb_goods表的补充描述,作用:抽取大字段内容到tb_goods_desc表,主要作用:提高数据查询性能)
SKU :tb_item
商品间接相关属性表:
模板表 与商品表关系是 一对多
分类表
品牌表
规格表
规格选项表
共8张表
tb_goods tb_goods_desc 一对一
tb_goods tb_item spu对sku 一对多
tb_brand tb_type_template 多对多
tb_specification tb_type_template 多对多
tb_item_cat tb_type_template 三级分类与模板关系 一对一
tb_specification tb_specification_option 一对多
tb_type_template tb_goods 一对多
我们在分析一下我们后台和前台要组装的数据有哪些
tb_goods
`is_delete` varchar(1) DEFAULT NULL COMMENT '是否删除',
逻辑删除: 当is_delete=1,代表删除状态
在数据库表设计时,指定一个字段记录该条数据的删除状态。
逻辑删除就是更新删除字段状态操作。
页面组装的数据:
`goods_name` varchar(100) DEFAULT NULL COMMENT 'SPU名',
`brand_id` bigint(10) DEFAULT NULL COMMENT '品牌',
`caption` varchar(100) DEFAULT NULL COMMENT '副标题',
`category1_id` bigint(20) DEFAULT NULL COMMENT '一级类目',
`category2_id` bigint(10) DEFAULT NULL COMMENT '二级类目',
`category3_id` bigint(10) DEFAULT NULL COMMENT '三级类目',
`price` decimal(10,2) DEFAULT NULL COMMENT '商城价',
`type_template_id` bigint(20) DEFAULT NULL COMMENT '分类模板ID',
`is_enable_spec` varchar(1) DEFAULT NULL COMMENT '是否启用规格',
后台组装的数据:
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`seller_id` varchar(20) DEFAULT NULL COMMENT '商家ID', 基于安全框架获取
`audit_status` varchar(2) DEFAULT NULL COMMENT '状态', 新录入商品都是未审核 0
tb_goods_desc
后台组装的数据:
`goods_id` bigint(20) NOT NULL COMMENT 'SPU_ID',
页面组装的数据:
`introduction` varchar(3000) DEFAULT NULL COMMENT '描述',
`specification_items` varchar(3000) DEFAULT NULL COMMENT '规格结果集,所有规格,包含isSelected',
`custom_attribute_items` varchar(3000) DEFAULT NULL COMMENT '自定义属性(参数结果)',
`item_images` varchar(3000) DEFAULT NULL,
`package_list` varchar(3000) DEFAULT NULL COMMENT '包装列表',
`sale_service` varchar(3000) DEFAULT NULL COMMENT '售后服务',
1.首先第一步我们要把页面的三张表的数据保存到数据库中,tb_goods,tb_goods_desc ,tb_item ,所以我们的准备工作是,复制运营商管理的三个到shop的controller中,如图:
通过拷贝相对的前端controller和service
2.那我们要通过组装数据到数据库,就要创建已给实体类,来封装这个三个表的数据
3、富文本编辑器
三个:
KindEditor http://kindeditor.net/
UEditor http://ueditor.baidu.com/website/
CKEditor http://ckeditor.com/
如何使用: 步骤:1.导入资源文件 2.编写一个文本域textarea 3.获取编辑好的hmtl标签,提交到数据库,页面展示的时候,直接通过的html标签展示,带有样式效果哦
清空的富文本编辑器
editor.html('');//清空富文本编辑器
提取文本编辑器的内容
在 goodsController.js 中的 add()方法中添加
$scope.entity.goodsDesc.introduction=editor.html();
后台代码实现:
通过controller层我们封装商家的id,通过安全框架获得,
service层我们就要分开添加,分别tbgoods和goodsDesc表,注意goodsDesc中的表的goods_id通过goods表获得,tbgoods添加商品状态为0
注意:非常重要的东西,一定要加事务,因为多张表,如果一张插入不成功,则回滚
核心代码:
@RequestMapping("/add")
public Result add(@RequestBody Goods goods){
try {
//在这个我们组装,商家的id,基于安全框架获取
String name = SecurityContextHolder.getContext().getAuthentication().getName();
goods.getTbGoods().setSellerId(name);
goodsService.add(goods);
return new Result(true, "增加成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "增加失败");
}
}
//service
/**
* 增加
*/
@Override
public void add(Goods goods) {
//获取goods表,我们在插入的时候获取插入时的id,因为这个表关联goodsDesc
TbGoods tbGoods = goods.getTbGoods();
goodsMapper.insert(tbGoods);
//获取goodsDesc表
TbGoodsDesc tbGoodsDesc = goods.getTbGoodsDesc();
tbGoodsDesc.setGoodsId(tbGoods.getId());
goodsDescMapper.insert(tbGoodsDesc);
}
前台代码实现:
我们通过先修改service -》 controller-》页面 先封装也面有的,包括获取富文本的html标签
三级联动通过 依赖angularjs监控机制实现。 $watch
//监控一级分类下的二级分类
//参数一:监控的变量值,参数二:监控变化后,需要的做的事
$scope.$watch("entity.tbGoods.category1Id",function (newValue,oldValue) {
itemCatService.findByParentId(newValue).success(function (response) {
$scope.itemCat2List=response;
})
})
//监控二级分类下的三级分类
//参数一:监控的变量值,参数二:监控变化后,需要的做的事
$scope.$watch("entity.tbGoods.category2Id",function (newValue,oldValue) {
itemCatService.findByParentId(newValue).success(function (response) {
$scope.itemCat3List=response;
//遗留的问题如何清理
})
})
//监控三级分类下的模板id
//参数一:监控的变量值,参数二:监控变化后,需要的做的事
$scope.$watch("entity.tbGoods.category3Id",function (newValue,oldValue) {
itemCatService.findOne(newValue).success(function (response) {
$scope.entity.tbGoods.typeTemplateId=response.typeId;
//遗留的问题如何清空三级的标题
})
})
//监控模板下的查询关联数据
//参数一:监控的变量值,参数二:监控变化后,需要的做的事
$scope.$watch("entity.tbGoods.typeTemplateId",function (newValue,oldValue) {
typeTemplateService.findOne(newValue).success(function (response) {
//注意一定要把品牌的json字符串变为json对象
$scope.brandList=JSON.parse(response.brandIds);
})
})
注意页面记得导入itemCatList和typeTemplaleList的前端service 的文件
<td>
<select class="form-control" ng-model="entity.tbGoods.category1Id" ng-options="item.id as item.name for item in itemCat1List">
</select>
</td>
<td>
<select class="form-control select-sm" ng-model="entity.tbGoods.category2Id" ng-options="item.id as item.name for item in itemCat2List"></select>
</td>
<td>
<select class="form-control select-sm" ng-model="entity.tbGoods.category3Id" ng-options="item.id as item.name for item in itemCat3List"></select>
</td>
<td>
模板ID:{{entity.tbGoods.typeTemplateId}}
</td>
品牌:
<div class="col-md-2 title">品牌</div>
<div class="col-md-10 data">
<select class="form-control" ng-model="entity.tbGoods.brandId" ng-options="brand.id as brand.text for brand brandLiset" ></select>
</div>
三、创建的错误
四、总结
$watch简单使用
$watch是一个scope函数,用于监听模型变化,当你的模型部分发生变化时它会通知你。
$watch(watchExpression, listener, objectEquality);
每个参数的说明如下:
-
watchExpression:监听的对象,它可以是一个angular表达式如'name',或函数如function(){return $scope.name}。
-
listener:当watchExpression变化时会被调用的函数或者表达式,它接收3个参数:newValue(新值), oldValue(旧值), scope(作用域的引用)
-
objectEquality:是否深度监听,如果设置为true,它告诉Angular检查所监控的对象中每一个属性的变化. 如果你希望监控数组的个别元素或者对象的属性而不是一个普通的值, 那么你应该使用它
-
举个例子:这是监测一个属性的变化
$scope.name = 'hello';
var watch = $scope.$watch('name',function(newValue,oldValue, scope){
console.log(newValue);
console.log(oldValue);
});
$timeout(function(){
$scope.name = "world";
},1000);
监测多个属性
贴出一个具体的demo
复制代码
<body ng-app="app" ng-controller="first">
<button ng-click="name='a'">1</button>
<button ng-click="name='b'">2</button>
<button ng-click="name='c'">3</button>
<button ng-click="type=2">4</button>
<button ng-click="type=3">5</button>
<p>{{name}}</p>
</body>
<script type="text/javascript">
var app = angular.module("app", []);
app.controller("first", function($scope) {
$scope.name = 'q';
$scope.type = 1;
function te() {
console.log($scope.name+" "+ $scope.type);
}
$scope.$watch('name+type', function(newValue, oldValue) {
te();
});
})
</script>
$watch性能问题
太多的$watch将会导致性能问题,$watch如果不再使用,我们最好将其释放掉。
$watch函数返回一个注销监听的函数,如果我们想监控一个属性,然后在稍后注销它,可以使用下面的方式:
var watch = $scope.$watch('someModel.someProperty', callback);
//...
watch();
还有2个和$watch相关的函数:
$watchGroup(watchExpressions, listener);
$watchCollection(obj, listener);