电商项目笔记
【第一章】、环境搭建
【第二章】
1、AngularJS框架的使用
(1)AngularJS四大特性
[ 1 ]mvc模式
Angular 遵循软件工程的 MVC 模式,并鼓励展现,数据,和逻辑组件之间的松耦合.通过依赖注入(dependency injection),Angular 为客户端的 Web 应用带来了传统服务端的服务,例如独立于视图的控制。 因此,后端减少了许多负担,产生了更轻的 Web 应用。
[ 2 ]双向绑定
【课件解析】:AngularJS是建立在这样的信念上的:声明式编程应该用于构建用户界面以及编写软件构建,而指令式编程非常适合来表示业务逻辑。框架采用并扩展了传统的HTML,通过双向的数据绑定来适应动态内容,双向的数据绑定允许模型和视图之间的自动同步。因此,AngularJS使得对DOM的操作不再重要并提升了可测试性。
初步个人理解:
[1]传统的HTML通过DOM对象来获取和改变文本框的内容,而Angular通过特定的表达式来获取。
[2]AngularJS具有双向绑定特性,当两个或多个文本框的值绑定时,改变其中一个,其它的也随之改变。
[ 3 ]依赖注入
【课件解析】:依赖注入(Dependency Injection,简称 DI)是一种设计模式,指某个对象依赖的其他对象无需手工创建,只需要”吼一嗓子“,因此对象在创建时,其依赖的对象由框架来自动创建并注入进来,其实就是最少知识法则;模块中所有的service和provider两类对象,都可以根据形参名称实现DI。
[ 4 ]模块化设计
【课件解析】:高内聚低耦合法则
[1] 官方提供的模块 ng、ngRoute、ngAnimate
[2] 用户自定义的模块 angular.module{‘模块名’,[ ]}
(2)、常用命令
[ 1 ]ng-app
ng-app 指令作用是告诉子元素以下的指令是归angularJS的,AngularJS会识别的
该指令定义了AngularJS应用程序的根元素
[ 2 ]ng-model
ng-model 指令用于绑定变量,这样用户在文本框输入的内容会绑定到变量上,而表达式可以实时输出变量
[ 3 ]ng-init
ng-init 指令用来对变量初始化赋值
[ 4 ]ng-controller
ng-controller 用于指定使用的控制器
[ 5 ]$scope
s
c
o
p
e
的
使
用
贯
穿
整
个
A
n
g
u
l
a
r
J
S
A
p
p
应
用
,
它
与
数
据
模
型
相
关
联
,
同
时
也
是
表
达
式
执
行
的
上
下
文
.
有
了
scope 的使用贯穿整个 AngularJS App 应用,它与数据模型相关联,同时也是表达式执行的上 下文.有了
scope的使用贯穿整个AngularJSApp应用,它与数据模型相关联,同时也是表达式执行的上下文.有了scope 就在视图和控制器之间建立了一个通道,基于作用域视图在修改数据时会
立刻更新 $scope,同样的 $scope 发生改变时也会立刻重新渲染视图.
[ 6 ]ng-repeat
ng-repeat 用来循环,类似于增强for
(3)、入门小demo
【demo1】{{表达式}}
作用:获取对象值
案例所使用命令ng-app
<html>
<head>
<title>入门小 Demo-1</title>
<script src="angular.min.js"></script></head>
<body ng-app>
{{100+100}}
</body>
</html>
执行结果为:200
【demo2】双向绑定
案例所使用命令ng-model
<html>
<head>
<title>入门小 Demo-1 双向绑定</title>
<script src="angular.min.js"></script></head>
<body ng-app>
请输入你的姓名:<input ng-model="myname"><br>
{{myname}},你好
</body>
</html>
运行结果如下:
请输入你的姓名:张三丰
张三丰,你好
【demo3】初始化指令
案例所用命令ng-init
<html>
<head>
<title>入门小 Demo-3 初始化</title>
<script src="angular.min.js"></script>
</head>
<body ng-app ng-init="myname='张根硕'">
请输入你的姓名:<input ng-model="myname"><br>
{{myname}},你好
</body>
</html>
【demo4】控制器
案例所用命令:ng-controller $scope
<html>
<head>
<title>入门小 Demo-3 初始化</title>
<script src="angular.min.js"></script>
<script>
var app=angular.module('myApp',[]); //定义了一个叫 myApp 的模块
//定义控制器
app.controller('myController',function($scope){
$scope.add=function(){
return parseInt($scope.x)+parseInt($scope.y);
}
});
</script>
</head>
<body ng-app="myApp" ng-controller="myController">
x:<input ng-model="x" > y:<input ng-model="y" >
运算结果:{{add()}}
</body>
</html>
运行结果如下:
X:333 Y:444 运行结果:777
【demo5】事件指令
<html>
<head>
<title>入门小 Demo-5 事件指令</title>
<script src="angular.min.js"></script>
<script>
//定义了一个叫 myApp 的模块
var app=angular.module('myApp',[]);
//定义控制器
app.controller('myController',function($scope){
$scope.add=function(){
$scope.z=parseInt($scope.x)+parseInt($scope.y);
}
});
</script>
</head>
<body ng-app="myApp" ng-controller="myController">
x:<input ng-model="x" >
y:<input ng-model="y" >
<button ng-click="add()">运算</button>结果:{{z}}
</body>
</html>
运算结果:点击运算,显示结果
ng-click 是最常用的单击事件指令,再点击时触发控制器的某个方法
【demo6】循环数组
<html>
<header>
<title>入门小demo6 循环数组</title>
<script src="angular.min.js"></script>
<script>
//创建模块
var app = angular.module("myApp",[]);
//创建控制器
app.controller("myController",function($scope){
//创建集合
$scope.list=[111,222,333,444,555];
})
</script>
</header>
<body ng-app="myApp" ng-controller="myController">
<table>
<tr ng-repeat="x in list">
<td>{{x}}</td>
</tr>
</table>
</body>
</html>
运行结果:
111
222
333
444
555
【demo7】循环对象数组
<html>
<header>
<title>入门小demo7 循环对象数组</title>
<script src="angular.min.js"></script>
<script>
//创建模块
var app = angular.module("myApp",[]);
//创建控制器
app.controller("myController",function($scope){
//创建集合
$scope.list=[
{name:'张三',math:100,english:100},
{name:'李四',math:95,english:92},
{name:'老王',math:0,english:0}
];
})
</script>
</header>
<body ng-app="myApp" ng-controller="myController">
<table>
<tr>
<td>姓名</td>
<td>数学</td>
<td>英语</td>
</tr>
<tr ng-repeat="student in list">
<td>{{student.name}}</td>
<td>{{student.math}}</td>
<td>{{student.english}}</td>
</tr>
</table>
</body>
</html>
运行结果如下:
姓名 | 数学 | 英语 |
---|---|---|
张三 | 100 | 100 |
李四 | 95 | 92 |
老王 | 0 | 0 |
【demo8】内置服务
html页面
<html>
<head>
<title>入门小 Demo-8 内置服务</title>
<meta charset="utf-8" />
<script src="angular.min.js"></script>
<script>
var app=angular.module('myApp',[]); //定义了一个叫 myApp 的模块
//定义控制器
app.controller('myController',function($scope,$http){
$scope.findAll=function(){
$http.get('data.json').success(
function(response){
$scope.list=response;
}
);
}
});
</script>
</head>
<body ng-app="myApp" ng-controller="myController" ng-init="findAll()">
<table>
<tr>
<td>姓名</td>
<td>数学</td>
<td>语文</td>
</tr>
<tr ng-repeat="entity in list">
<td>{{entity.name}}</td>
<td>{{entity.shuxue}}</td>
<td>{{entity.yuwen}}</td>
</tr>
</table>
</body>
</html>
建立文件 data.json
[
{"name":"张三","shuxue":100,"yuwen":93},
{"name":"李四","shuxue":88,"yuwen":87},
{"name":"老王","shuxue":77,"yuwen":56}
]
运行结果:
姓名 | 数学 | 英语 |
---|---|---|
张三 | 100 | 93 |
李四 | 88 | 87 |
老王 | 77 | 56 |
【第四章】商家
1、商家入驻
【代码逻辑】:商家注册商家信息,将数据存储到数据库中
【实现步骤】:(1)注册表单的name属性需要与实体类字段相对应
(2)在Web层Controller类中加入状态和创建时间
@RequestMapping("/add")
public Result add(@RequestBody TbSeller seller){
try {
//设置状态
seller.setStatus("0");
//设置创建时间
seller.setCreateTime(new Date());
sellerService.add(seller);
return new Result(true, "增加成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "增加失败");
}
}
2、商家审核
【代码逻辑】:在商家管理页面中显示待审核的商家,创建updateStatus(String sellerId,String status)方法来进行修改商家审核状态
【实现步骤】:
(1)在审核按钮分别绑定点击事件调用updateStatus,传入商家ID和要修改的状态值
<div class="modal-footer">
<button class="btn btn-success" data-dismiss="modal" aria-hidden="true" ng-click="updateStatus(entity.sellerId,'1')">审核通过</button>
<button class="btn btn-danger" data-dismiss="modal" aria-hidden="true" ng-click="updateStatus(entity.sellerId,'2')">审核未通过</button>
<button class="btn btn-danger" data-dismiss="modal" aria-hidden="true" ng-click="updateStatus(entity.sellerId,'3')">关闭商家</button>
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">关闭</button>
</div>
(2)前端controller代码,
$scope.updateStatus=function(sellerId,status){
//调用service
sellerService.updateStatus(sellerId,status).success(
function(response){
//返回值为Result类型
if(response.success){
//操作成功,重新加载
$scope.reloadList();
}else{
//操作失败,弹出提示
alert(response.message);
}
}
)
}
(3)后端Service代码,先通过id查询商家信息,然后设置状态值再调用存储方法
@Override
public void updateStatus(String sellerId, String status) {
// 根据id查询商家
TbSeller seller = sellerMapper.selectByPrimaryKey(sellerId);
// 修改商家审核状态
seller.setStatus(status);
// 修改商家信息
sellerMapper.updateByPrimaryKey(seller);
}
###3、商家系统登录【SpringSecurity】
【代码逻辑】:使用SpringSecurity进行登录验证,前端登录传参调用SpringSecurity的验证方法,来实现校验用 户账号密码以及权限;
【实现步骤】:
(1)导入springsecurity的jar包依赖
<!-- 身份验证 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
(2)配置web-xml
<!-- 配置认证 -->
<!-- 这个用来加载spring-security的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-security.xml</param-value>
</context-param>
<!-- 配置监听器在服务器启动时加载配置文件 -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!-- 配置监听器,固定写法,底层通过springSecurityFilterChain创建实例对象-->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(3)配置springsecurity.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:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!-- 这里配置认证需要过滤的资源 -->
<http pattern="/*.html" security="none"/>
<http pattern="/css/**" security="none"/>
<http pattern="/img/**" security="none"/>
<http pattern="/js/**" security="none"/>
<http pattern="/plugins/**" security="none"/>
<!-- 注意要过滤掉注册方法 -->
<http pattern="/seller/add.do" security="none"/>
<!-- 页面拦截规则 -->
<!-- 配置SPEL表达式是否开启 若开启access需要使用HasRole('ROLE_XXX') -->
<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" //登录成功后访问默认路径
/>
<!-- 跨域请求,由于html无法通过验证,需要开启此配置通过验证-->
<csrf disabled="true"></csrf>
<!-- 过滤内置框架页面 -->
<headers>
<frame-options policy="SAMEORIGIN" />
</headers>
<logout/>
</http>
<!-- 认证管理器 -->
<authentication-manager>
<authentication-provider user-service-ref="userService" ></authentication-provider>
</authentication-manager>
<!-- 注入自定义认证类 -->
<beans:bean id="userService" class="com.pinyougou.service.UserDetailsServiceImpl">
<beans:property name="sellerService" ref="sellerService"/>
</beans:bean>
<!-- 引用dubbo服务 -->
<dubbo:application name="pingyougou-web-shop"/>
<dubbo:registry address="zookeeper://192.168.25.128:2181"/>
<dubbo:reference id="sellerService"interface="com.pinyougou.sellergoods.service.SellerService">
</dubbo:reference>
</beans:beans>
(4)编写自定义验证类UserDetailsServiceImpl
package com.pinyougou.service;
import java.util.ArrayList;
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;
//实现UserDetailsService接口
public class UserDetailsServiceImpl implements UserDetailsService {
//定义服务类但在xml通过配置注入
private SellerService sellerService;
//xml注入需要set方法
public void setSellerService(SellerService sellerService) {
this.sellerService = sellerService;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//创建角色集合,用来存储用户角色信息
List<GrantedAuthority> authorities = new ArrayList<>();
//根据用户名查询商家
TbSeller seller = sellerService.findOne(username);
//判断用户名是否存在
if(seller!=null) {
//查询到商家
//判断商家状态是否通过审核
if("1".equals(seller.getStatus())) {
//商家已通过审核
//给商家添加角色
authorities.add(new SimpleGrantedAuthority("ROLE_SELLER"));
return new User(username, seller.getPassword(), authorities);
}else {
//商家未审核通过,不允许登录
return null;
}
}else {
//未查询到商家,不允许登录
return null;
}
}
}
(5)页面表单修改
<!-- 配置访问路径与springsecurity一致,默认为/login 方法必须为post请求 -->
<form class="sui-form" id="loginForm" action="/login" method="post">
<div class="input-prepend"><span class="add-on loginname"></span>
<!-- 定义账号与springsecurity配置一致 默认username -->
<input id="prependedInput" type="text" name="username" placeholder="邮箱/用户名/手机号" class="span2 input-xfat">
</div>
<div class="input-prepend"><span class="add-on loginpwd"></span>
<!-- 定义密码与springsecurity配置一致 默认password -->
<input id="prependedInput" type="password" name="password" placeholder="请输入密码" class="span2 input-xfat">
</div>
<div class="setting">
<label class="checkbox inline">
<input name="m1" type="checkbox" value="2" checked="">自动登录
</label>
<span class="forget">忘记密码?</span>
</div>
<div class="logined">
<!-- 绑定点击事件,提交表单 -->
<a class="sui-btn btn-block btn-xlarge btn-danger" onclick="document:loginForm.submit()" target="_blank">登 录</a>
</div>
</form>
4、商家密码加密【Bcrypt】
【Bcrypt】个人概述:是一款加密算法,会自动生成一个随机盐来实现加密,同时把盐的值随机混入加密后的密码中,加密解密都由它来完成。生成长度为60的密文。由于是随机盐,相同密码两次加密后的值基本会不一样。
【代码逻辑】:在存储密码的时候进行Bcrypt加密,并在配置文件中配置加密方式
【实现步骤】:
(1)在注册add方法中对密码进行加密
@RequestMapping("/add")
public Result add(@RequestBody TbSeller seller){
try {
//创建加密类
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
//【对密码进行加密】
String password = passwordEncoder.encode(seller.getPassword());
//存储加密后的密码
seller.setPassword(password);
//设置初始状态值
seller.setStatus("0");
//设置添加日期
seller.setCreateTime(new Date());
sellerService.add(seller);
return new Result(true, "增加成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "增加失败");
}
}
(2)在spring-security.xml中配置加密方式
<!-- 认证管理器 -->
<authentication-manager>
<authentication-provider user-service-ref="userService" >
<!-- 配置加密方式 -->
<password-encoder ref="passwordEncoder"></password-encoder>
</authentication-provider>
</authentication-manager>
<!-- 配置加密类 -->
<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
【第五章】商品
1、商品分类列表
【代码逻辑】:定义findByParentId(String parentId)方法,与点击【查询下级】按钮绑定,并参数设置为查询商品的id,用此id作为下一级的parentId的查询条件来查询。在页面头部定义ng-init(0)用来初始化查询第一级商品分类。
【实现步骤】
(1)Web层Service代码:定义方法,调用dao通过parentId查询下一级商品分类信息
@Override
public List<TbItemCat> findByParentId(Long parentId) {
//创建条件对象
TbItemCatExample example = new TbItemCatExample();
Criteria createCriteria = example.createCriteria();
//添加parentId查询条件
createCriteria.andParentIdEqualTo(parentId);
//查询数据库
List<TbItemCat> itemCatList = itemCatMapper.selectByExample(example);
//返回查询结果
return itemCatList;
}
(2)Web层Controller代码:定义方法,调用service层
@RequestMapping("/findByParentId")
public List<TbItemCat> findByParentId(Long parentId){
return itemCatService.findByParentId(parentId);
}
(3)前端service代码、controller代码:总结在面包屑中
2、面包屑
【代码逻辑】AngularJS中定义3个变量
$scope.grade //分类等级
$scope.entity_1 //查询二级分类所需要的对象(该对象所属一级分类)
$scope.entity_2 //查询三级分类所需要的对象(该对象所属二级分类)
定义selectList(p_entity)方法,参数为需要查询下一级分类的对象
在selectList方法方法里需要先判定分类等级,然后通过对应等级来给面包屑对应的对象赋值。此外还需要在调用查询方法前先对分类等级的值进行+1处理。
【实现步骤】
(1)前端Controller
//查询下一级商品分类
$scope.findByParentId=function(parentId){
itemCatService.findByParentId(parentId).success(
function(response){
$scope.list=response;
}
)
}
//定义初始分类等级
$scope.grade=1;
//设置分类等级
$scope.setGrade=function(value){
$scope.grade=value;
}
//设置面包屑显示
$scope.selectList=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.findByParentId(p_entity.id);
}
(2)html
面包屑展示 部分
<ol class="breadcrumb">
<li>
<a href="#" ng-click="setGrade(1);selectList({id:0})">顶级分类列表</a>
</li>
<li>
<a href="#" ng-click="setGrade(2);selectList(entity_1)">{{entity_1.name}}</a>
</li>
<li>
<a href="#" ng-click="setGrade(3);selectList(entity_2)">{{entity_2.name}}</a>
</li>
</ol>
数据展示部分:
<tr ng-repeat="entity in list">
<td><input type="checkbox" ></td>
<td>{{entity.id}}</td>
<td>{{entity.name}}</td>
<td>
{{entity.typeId}}
</td>
<td class="text-center">
<!-- 但分类等级为3时,不展示查询下级 -->
<span ng-if="grade<3">
<button type="button" class="btn bg-olive btn-xs" ng-click="setGrade(grade+1);selectList(entity)">查询下级</button>
</span>
<button type="button" class="btn bg-olive btn-xs" data-toggle="modal" data-target="#editModal" >修改</button>
</td>
</tr>
需要注意的是[ 1 ]:在查询方法前需要先对分类等级进行赋值
[ 2 ]:通过【ng-if】判定分类等级来实现当等级为3时不显示查询下级
3、商品录入-商品基本信息录入
【代码逻辑】
【实现步骤】
【后端】
(1)DaoMapper的修改:
在商品表的insert方法中加入selectKey,获取存入后生成的id值
<selectKey resultType="java.lang.Long" order="after" keyProperty="id">
SELECT LAST_INSERT_ID() AS id
</selectKey>
(2)创建组合类对象
public class Goods implements Serializable{
private TbGoods tbGoods; //商品SPU基本信息
private TbGoodsDesc tbGoodsDesc; //商品SPU扩展信息
private List<TbItem> itemsList; //商品SKU信息
//getter,setter方法...
}
(3)修改goodsService中add()方法
@Override
public void add(Goods goods) {
TbGoods tbGoods = goods.getTbGoods();//获取商品信息对象
TbGoodsDesc tbGoodsDesc = goods.getTbGoodsDesc();//获取商品扩展信息对象
//状态:未审核
tbGoods.setAuditStatus("0");
//存储商品基本信息
goodsMapper.insert(tbGoods);
//在商品扩展信息中存入商品id
tbGoodsDesc.setGoodsId(tbGoods.getId());
//存入商品扩展信息
goodsDescMapper.insert(tbGoodsDesc);
}
(4)控制层绑定商家id
@RequestMapping("/add")
public Result add(@RequestBody Goods goods){
try {
//获取当前登录商家的id
String sellerId = SecurityContextHolder.getContext().getAuthentication().getName();
//绑定商家id
goods.getTbGoods().setSellerId(sellerId);
//调用service
goodsService.add(goods);
return new Result(true, "增加成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "增加失败");
}
}
(5)前端controller
//保存
$scope.add=function(){
goodsService.add($scope.entity).success(
function(response){
if(response.success){
alert("添加成功");
$scope.entity={};
}else{
alert(response.message);
}
}
);
}
(6)前端html:绑定输入框ng-momel ,和保存的点击按钮的ng-click
4、富文本编辑器
(1)html表单代码
<div class="col-md-2 title editer">商品介绍</div>
<div class="col-md-10 data editer">
<textarea name="content" style="width:800px;height:400px;visibility:hidden;" ></textarea>
</div>
(2)修改前端controller代码
//保存
$scope.add=function(){
//添加富文本框内容【修改内容】
$scope.entity.tbGoodsDesc.introduction=editor.html();
goodsService.add($scope.entity).success(
function(response){
if(response.success){
alert("添加成功");
$scope.entity={};
editor.html("");
}else{
alert(response.message);
}
}
);
}
5、fastDFS入门小demo
(1)导入镜像
需要修改网关为仅主机,第一次开启时选择I move it
(2)导入入fastDFS的jar包,cmd窗口输入:
mvn install:install-file -Dfile=D:\JAVA\setup\fastdfs-1.2.jar -DgroupId=org.csource.fastdfs -DartifactId=fastdfs -Dversion=1.2 -Dpackaging=jar
mvn install:install-file -Dfile=D:\JAVA\setup\solr-solrj-4.10.4.jar -DgroupId=org.apache.solr -DartifactId=solr-solrj -Dversion=4.10.4 -Dpackaging=jar
(3)导入配置文件
(4)java类代码
public static void main(String[] args) throws Exception{
//1、加载配置文件
ClientGlobal.init("D:\\JAVA\\workspace\\fastDFS\\src\\main\\resources\\fdfs_client.conf");
//2、创建一个管理者客户端
TrackerClient trackerClient = new TrackerClient();
//3、连接管理者服务端
TrackerServer trackerServer = trackerClient.getConnection();
//4、声明存储服务器
StorageServer storageServer = null;
//5、获取存储服务器的客户端对象
StorageClient storageClient2 = new StorageClient(trackerServer, storageServer);
//6、上传文件
String[] upload_file = storageClient2.upload_file("D:\\JAVA\\setup\\xuemei3.jpg", "jpg", null);
for (String string : upload_file) {
System.out.println(string);
//group1/M00/00/00/wKgZhVvRi8yAbJJHAAD0RclXCSA283.jpg
}
}
6、商品录入-商品图片上传
(1)环境准备
导入jar包依赖
<dependencies>
<dependency>
<groupId>org.csource.fastdfs</groupId>
<artifactId>fastdfs</artifactId>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
</dependencies>
配置多媒体解析器
<!-- 配置多媒体解析器 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 文件上传上限 -->
<property name="maxUploadSize" value="1048576" />
</bean>
(2)Web层定义UploadController类:用来传输文件
@RestController
public class UploadController {
@Autowired
@Value("${FILE_SERVER_URL}")
private String file_server_url;//读取ip地址
@RequestMapping("/upload")
public Result upload(MultipartFile file) {
//获取文件的全文件名
String originalFilename = file.getOriginalFilename();
//获取文件名后缀
String extName = originalFilename.substring(originalFilename.indexOf(".")+1);
try {
//读取配置文件
util.FastDFSClient fastDFSClient = new FastDFSClient("/classpath:config/fdfs_client.conf");
//获取上传文件id
String fileId = fastDFSClient.uploadFile(file.getBytes(), extName);
//拼接ip地址组合成url
String url = file_server_url+fileId;
return new Result(true,url);
} catch (Exception e) {
e.printStackTrace();
return new Result(false,"上传失败");
}
}
}
(3)前端:
创建uploadService
app.service("uploadService",function($http){
this.uploadFile=function(){
var formData = new FormData();
formData.append("file",file.files[0]);
return $http({
url:"../upload.do",//访问路径
method:"post",//请求方法方式
data:formData,//请求参数
//指定上传格式,默认为json,设置后为multipart格式
headers:{"Content-Type":undefined},
//对表单进行二进制序列化
transformRequest:angular.identity
})
}
})
GoodsController中调用uploadService:定义新增、删除、和保存方法
//保存
$scope.add=function(){
//添加富文本框内容
$scope.entity.tbGoodsDesc.introduction=editor.html();
goodsService.add($scope.entity).success(
function(response){
if(response.success){
alert("添加成功");
$scope.entity={};
editor.html("");
}else{
alert(response.message);
}
}
);
}
//添加图片
$scope.add_image_entity=function(){
$scope.entity.tbGoodsDesc.item_images.push($scope.image_entity);
}
//删除图片
$scope.remove_image_entity=function(index){
$scope.entity.tbGoodsDesc.item_images.splice(index,1);
}
(4)页面
绑定显示数据
<div class="btn-group">
<!-- 绑定点击事件初始化image_entity对象 -->
<button type="button" class="btn btn-default" title="新建" data-target="#uploadModal" data-toggle="modal" ng-click="image_entity={}" ><i class="fa fa-file-o"></i> 新建</button>
</div>
<table class="table table-bordered table-striped table-hover dataTable">
<thead>
<tr>
<th class="sorting">颜色</th>
<th class="sorting">图片</th>
<th class="sorting">操作</th>
</thead>
<tbody>
<!-- 遍历tbGoodsDesc.item_images -->
<tr ng-repeat="pojo in entity.tbGoodsDesc.item_images"> <td>
{{pojo.color}}
</td>
<td>
<img alt="" src="{{pojo.url}}" width="100px" height="100px">
</td>
<td> <button type="button" class="btn btn-default" title="删除" ng-click="remove_image_entity($index)"><i class="fa fa-trash-o"></i> 删除</button></td>
</tr>
</tbody>
#【第六章】商品录入
###1、AngularJS实现三级联动(ng-options)
(1)选项商品分类
【思路逻辑】
使用到Angular的$watch,它的作用是监听变量的变动,当一级分类选项发生改变时,调用方法查询二级商品分类展示到页面,以此来实现三级或多级联动。
【代码实现】
<1>前端:
在goodsController中引入itemService,用以调用findByParentId()方法
//查询一级目录
$scope.selectItemCat1List=function(){
itemCatService.findByParentId(0).success(
function(response){
$scope.ItemCat1List=response;
}
)
}
//查询二级目录
$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;
}
)
})
<2>【页面】:
用到ng-options
<table>
<tr>
<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:19
</td>
</tr>
</table>
(2)显示模板ID
【前端controller】,同三级分类
//显示模板ID
$scope.$watch("entity.tbGoods.category3Id",function(newValue,oldValue){
itemCatService.findOne(newValue).success(
function(response){
$scope.entity.tbGoods.typeTemplateId=response.typeId;
}
)
})
【html页面】:
<td>
模板ID:{{entity.tbGoods.typeTemplateId}}
</td>00
(3)显示品牌下拉框和扩展属性
【前端controller】:由于brandIds在数据库是以字符串形式存储,需转换为JSON格式
//显示品牌下拉选项
$scope.$watch("entity.tbGoods.typeTemplateId",function(newValue,oldValue){
typeTemplateService.findOne(newValue).success(
function(response){
$scope.typeTemplate=response;
//获取品牌json对象
$scope.typeTemplate.brandIds = JSON.parse($scope.typeTemplate.brandIds);
//获取扩展属性json对象
$scope.entity.tbGoodsDesc.customAttributeItems = JSON.parse(response.customAttributeItems);
}
)
})
【html页面】:
<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 in typeTemplate.brandIds"></select>
</div>
<!--扩展属性-->
<div class="tab-pane" id="customAttribute">
<div class="row data-type">
<!-- 遍历扩展属性json对象 -->
<div ng-repeat="item in entity.tbGoodsDesc.customAttributeItems">
<div class="col-md-2 title">{{item.text}}</div>
<div class="col-md-10 data">
<input class="form-control" ng-model="item.value" placeholder="{{item.text}}">
</div>
</div>
</div>
</div>
【我的 BUG】:ng-options 写成ng-option
2、规格选择
(1)后端:
【逻辑思路】:在service层TypeTemplateService类中创建 List
通过id查询tb_type_template表,获取spec_ids字段,将其转换为Map对象存入List集合中,然后遍历集合,通过map存储的specId 查询tb_specification_option表,在每个Map对象中添加options属性并存入其中。
【代码实现】
<1>service层
@Autowired
private TbSpecificationOptionMapper specificationOptionMapper;
@Override
public List<Map> findSpecList(Long templateId) {
//根据模板ID查询模板对象
TbTypeTemplate typeTemplate = typeTemplateMapper.selectByPrimaryKey(templateId);
//将关联规格的json格式数据转换为List<Map>集合
List<Map> specIds = JSON.parseArray(typeTemplate.getSpecIds(),Map.class);
//遍历集合
for (Map map : specIds) {
//获取每个map中的id值查询规格选项表
TbSpecificationOptionExample example = new TbSpecificationOptionExample();
com.pinyougou.pojo.TbSpecificationOptionExample.Criteria createCriteria = example.createCriteria();
createCriteria.andSpecIdEqualTo(new Long( (Integer)map.get("id")));
//查询数据库,返回规格选项对象的集合,存如map集合中
List<TbSpecificationOption> options = specificationOptionMapper.selectByExample(example);
map.put("options", options);
}
return specIds;
}
我遇到的bug:TbSpecificationOptionMapper没有注入,报空指针异常
<2>Web层controller类
@RequestMapping("/findSpecList")
public List<Map> findSpecList(Long templateId) {
return typeTemplateService.findSpecList(templateId);
}
(2)前端:
【逻辑思路】:集合结构为[{“attributeName”:“网络制式”,“attributeValue”:[“移动3G”,“移动4G”]}]
假设定义,复选框所属的属性为key,复选框所代表的值为value,即key对应attributeName,value对应attributeValue。
(1)复选框的选中实现
当点击复选框时,判断集合中有没有是否已经包含attributeName,若包含,将value存入attributeValue数组中,若不包含,将key,value键值对存入集合中;
(2)复选框的取消选中
在点击复选框时
if(【进行判断复选框的勾选状态(传入$event参数)】){
【若为选中,执行第(1)步】
}else{
【若为未选中】
if(【判断要删除的数据是否为数组的唯一】){
【删除数据是唯一值,用集合移除数组】
}else{
【数据不是唯一值,用数组移除元素】
}
}
【代码实现】
baseController.js:定义公用方法,查询集合中是否存在某元素
【第七章】
1、商品列表
(1)显示列表:页面引入js文件,绑定数据
(2)商品的状态显示:
定义一个数组$scope.status=[“未审核”,“已审核”,“已驳回”,“已关闭”];
把状态值当作索引从数组中取出具体的值
(3)商品分类显示
定义一个数组$scope.itemCatList=[];//商品分类列表
调用itemCatService的findAll方法,把结果以id当索引存入集合中,页面再通过id取出
//商品状态
$scope.status=["未审核","已审核","已驳回","已关闭"];
//商品分类列表
$scope.itemCatList=[];
$scope.findItemCatList=function(){
itemCatService.findAll().success(
function(response){
for (var i = 0; i < response.length; i++) {
$scope.itemCatList[response[i].id]=response[i].name;
}
}
)
}
页面
<tr ng-repeat="pojo in list">
<td><input type="checkbox"></td>
<td>{{pojo.sellerId}}</td>
<td>{{pojo.goodsName}}</td>
<td>{{pojo.price}}</td>
<td>{{itemCatList[pojo.category1Id]}}</td>
<td>{{itemCatList[pojo.category2Id]}}</td>
<td>{{itemCatList[pojo.category3Id]}}</td>
<td>
<span>
{{status[pojo.auditStatus]}}
</span>
</td>
<td class="text-center">
<button type="button" class="btn bg-olive btn-xs">修改</button>
</td>
</tr>
我出现的BUG:未初始调用 ng-init=findItemCatList()方法
(4)条件查询:绑定查询数据,调用reloadList()方法
<div class="has-feedback">
状态:<select ng-model="searchEntity.auditStatus">
<option value="">全部</option>
<option value="0">未审核</option>
<option value="1">审核通过</option>
<option value="2">已驳回</option>
<option value="3">已通过</option>
</select>
商品名称:<input ng-model="searchEntity.goodsName">
<button class="btn btn-default" ng-click="reloadList()">查询</button>
</div>
2、商品修改
【显示基本信息,图片,附加属性】:
(1)通过$location.search()[“id”]获取传递参数
(2)数据库存储json格式的字符串需要用JSON.parse()转换为对象
(3)扩展属性与新增的代码冲突,会重新对 s c o p e . e n t i t y . t b G o o d s D e s c . c u s t o m A t t r i b u t e I t e m s 赋 值 为 空 , 需 要 在 规 格 i d 变 量 监 控 方 法 中 加 个 判 断 i f ( scope.entity.tbGoodsDesc.customAttributeItems赋值为空,需要在规格id变量监控方法中加个判断if( scope.entity.tbGoodsDesc.customAttributeItems赋值为空,需要在规格id变量监控方法中加个判断if(location.search()[“id”]==null)
后端service
/**
* 根据ID获取实体
* @param id
* @return
*/
@Override
public Goods findOne(Long id){
//创建组合实体类对象
Goods goods = new Goods();
//查询tbGoods并封装进组合实体类
TbGoods tbGoods = goodsMapper.selectByPrimaryKey(id);
goods.setTbGoods(tbGoods);
//查询扩展表tbGoodsDesc
TbGoodsDesc tbGoodsDesc = goodsDescMapper.selectByPrimaryKey(id);
goods.setTbGoodsDesc(tbGoodsDesc);
//查询扩展表itemsList
TbItemExample example = new TbItemExample();
com.pinyougou.pojo.TbItemExample.Criteria criteria = example.createCriteria();
//添加goodsId条件
criteria.andGoodsIdEqualTo(id);
List<TbItem> itemsList = itemMapper.selectByExample(example);
goods.setItemsList(itemsList);
return goods;
}
前端controller
//查询实体
$scope.findOne=function(){
var id =$location.search()["id"];
if(id==null){
return ;
}
goodsService.findOne(id).success(
function(response){
$scope.entity= response;
//富文本框
editor.html($scope.entity.tbGoodsDesc.introduction);
//图片
$scope.entity.tbGoodsDesc.itemImages=JSON.parse($scope.entity.tbGoodsDesc.itemImages);
//附加属性
$scope.entity.tbGoodsDesc.customAttributeItems=JSON.parse($scope.entity.tbGoodsDesc.customAttributeItems);
//规格SKU列表数据
for(var i=0;i<$scope.entity.itemsList.length;i++){
$scope.entity.itemsList.spec[i]=JSON.parse($scope.entity.itemsList.spec[i])
}
}
);
}
【显示规格】
goodsController:调用searchObjectByKey()查询规格结果集合中是否存在对应的规格属性
//修改显示规格复选框
$scope.checkAttributeValue=function(specName,optionName){
var items = $scope.entity.tbGoodsDesc.specificationItems;
var object = $scope.searchObjectByKey(items,"attributeName",specName);
//判断集合中是否能查询出对应的规格属性
if(object!=null){
if(object.attributeValue.indexOf(optionName)>0){
//有勾选规格
return true;
}else{
//没有勾选规格
return false;
}
}else{
//集合没有对象的规格属性
return false;
}
}
html页面调用ng-checked
<div>
<div class="col-md-2 title">{{pojo.text}}</div>
<div class="col-md-10 data" >
<span ng-repeat="option in pojo.options">
<input type="checkbox"
ng-click="updateSpecAttribute($event,pojo.text,option.optionName);createItemsList()"
ng-checked="checkAttributeValue(pojo.text,option.optionName)"
>{{option.optionName}}
</span>
</div>
</div>
3、商品保存
(1)后端
【代码逻辑】
[ 1 ] 在service层:goods组合实体类共涉及三张表,一张goods基本表,一张goodsDesc扩展表,一张Item扩展表
goods基本表和goodsDesc扩展表直接调用update修改
由于item扩展表无法确定是添加还是减少数据,需要先删除再存储。
[ 2 ] 在controller层判断传入的goods所属商家是否和当前登录商家ID所匹配
4、运营商后台-商品审核
5、注解事务配置
加入applicationContext-tx.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启事务控制的注解支持 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
【第八章】广告管理与缓存
1、广告管理
(1)广告图片上传
拷贝资源:UploadController.java 、 uploadService.js 、fdfs_client.conf
springMVC.xml多媒体解析器
<!-- 配置多媒体解析器 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<property name="maxUploadSize" value="1048576" />
</bean>
前端contentController.js修改
//上传文件
$scope.upload=function(){
uploadService.uploadFile().success(
function(response){
if(response.success){
$scope.entity.pic=response.message;
}else{
alert(response.message);
}
}
)
}
content.html页面修改
<tr>
<td>图片</td>
<td>
<input type="file" id="file">
<button ng-click="upload()">上传</button>
<img src="{{entity.pic}}" width="200px" height="100px"></img>
</tr>
(2)广告类目管理
定义变量contentCategoryList,查询广告类目封装进去,页面使用ng-options显示,需要注意的是页面引入service服务
前端contentController.js修改
//查询类目条目
$scope.findContentCategoryList=function(){
contentCategoryService.findAll().success(
function(response){
//注意变量名不要定义重复
$scope.contentCategoryList=response;
}
);
}
html页面修改
<tr>
<td>内容类目</td>
<td>
<select class="form-control" ng-model="entity.categoryId"
ng-options="item.id as item.name for item in contentCategoryList"/>
</td>
</tr>
(3)广告状态、图片显示
页面修改
<tr>
<td>状态</td>
<td><input type="checkbox" ng-model="entity.status"
placeholder="状态" ng-true-value="1" ng-false-value="2"></td>
</tr>
<td>
<img src="{{entity.pic}}" width="100px" height="50px"></img>
</td>
angularJS表达式在src中需要加括号
2、广告展示
方法名:findByCategoryId(Long id)
需要添加的条件有:根据id查询,状态必须为有效,按顺序排序
service层
/**
* 根据id查询广告
*/
@Override
public List<TbContent> findByCategoryId(Long categoryId) {
TbContentExample example = new TbContentExample();
Criteria criteria = example.createCriteria();
criteria.andCategoryIdEqualTo(categoryId);//根据id查询
criteria.andStatusEqualTo("1");//状态为有效
example.setOrderByClause("sort_order");//排序
List<TbContent> list = contentMapper.selectByExample(example);
return list;
}
前端变量定义:
定义广告数组$scope.contentList=[ ];以categoryId为角标将查询的集合存入里面
前端controller层
app.controller("contentController",function($scope,contentService){
//初始化定义集合
$scope.contentList=[];
//根据id查询广告
$scope.findByCategoryId=function(categoryId){
contentService.findByCategoryId(categoryId).success(
function(response){
$scope.contentList[categoryId]=response;
}
);
}
})
前端页面修改
<!--banner轮播-->
<div id="myCarousel" data-ride="carousel" data-interval="4000" class="sui-carousel slide">
<ol class="carousel-indicators">
<li data-target="#myCarousel" data-slide-to="{{$index}}" ng-repeat="item in contentList[1]" class="{{$index==0?'active':''}}"></li>
</ol>
<div class="carousel-inner">
<div class="{{$index==0?'active':''}} item" ng-repeat="item in contentList[1]">
<a href="{{item.url}}">
<img src="{{item.pic}}" />
</a>
</div>
</div><a href="#myCarousel" data-slide="prev" class="carousel-control left">‹</a><a href="#myCarousel" data-slide="next" class="carousel-control right">›</a>
</div>
通过三元表式来定义active属性:class="{{$index==0?‘active’:’ ‘}}"
3、广告数据的缓存
(1)加入缓存
导入相关的依赖
<!-- 缓存 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
在服务层添加缓存
/**
* 根据id查询广告
*/
@Override
public List<TbContent> findByCategoryId(Long categoryId) {
List<TbContent> list = (List<TbContent>)redisTemplate.boundHashOps("content").get(categoryId);
if(list==null) {
//查询数据库
System.out.println("从数据库中查询");
TbContentExample example = new TbContentExample();
Criteria criteria = example.createCriteria();
criteria.andCategoryIdEqualTo(categoryId);//根据id查询
criteria.andStatusEqualTo("1");//状态为有效
example.setOrderByClause("sort_order");//排序
list = contentMapper.selectByExample(example);
redisTemplate.boundHashOps("content").put(categoryId, list);
}else {
//查询缓存
System.err.println("从缓存中查询");
}
return list;
}
(2)更新缓存
在增删改的时候对缓存进行清除
增加:直接删缓存
/**
* 增加
*/
@Override
public void add(TbContent content) {
contentMapper.insert(content);
//删除缓存
redisTemplate.boundHashOps("content").delete(content.getCategoryId());
}
删除:需要在删除语句前清除缓存
/**
* 批量删除
*/
@Override
public void delete(Long[] ids) {
for(Long id:ids){
//通过id查询categoryId值
Long categoryId = contentMapper.selectByPrimaryKey(id).getCategoryId();
//删除缓存
redisTemplate.boundHashOps("content").delete(categoryId);
//删除数据
contentMapper.deleteByPrimaryKey(id);
}
}
修改:先查修改前所属categoryId,然后获取传参过来的categoryId,判断两者是否相同,相同则说明未修改所属分组,只用清除一次。不相同则需要分别清理。
/**
* 修改
*/
@Override
public void update(TbContent content){
//查询修改前的所属分组id
Long categoryId = contentMapper.selectByPrimaryKey(content.getId()).getCategoryId();
//清除缓存
redisTemplate.boundHashOps("content").delete(categoryId);
//修改数组
contentMapper.updateByPrimaryKey(content);
//获取修改后的所属分组id
if(categoryId!=content.getCategoryId()) {
//分组id被修改,清除修改后所属分组的缓存
redisTemplate.boundHashOps("content").delete(content.getCategoryId());
}
}
【第九章】
1、solr的部署
(1)安装tomcat(注意与jdk版本的冲突问题)
(2)将solr的war包导入tomcat的webapp目录下
(3)启动tomcat解压war包
(4)将solr的example\lib\ext目录下的扩展jar包导入tomcat项目webapp/lib目录下
(5)配置solr的example\solr目录拷出来取名solrhome作为
【第十章】
1、高亮显示
后端service
(1)用 solrTemplate调用queryForHighlightPage(query,
TbItem.class)方法,需要query对象
(2)创建高亮query对象,选择SimpleHighlightQuery实现类
(3)设置高亮选项:
query.setHighlightOptions(highlightOptions),需要高亮选项对象
(4)创建高亮选项highlightOptions对象,并设置高亮域,addField(“item_title”)
(5)设置高亮前缀和后缀
highlightOptions.setSimplePrefix("");//高亮前缀
highlightOptions.setSimplePostfix("");//高亮后缀
(6)按照关键字查询
Criteria criteria=new
Criteria(“item_keywords”).is(searchMap.get(“keywords”));
query.addCriteria(criteria);
HighlightPage page = solrTemplate.queryForHighlightPage(query, TbItem.class)
(7)循环高亮入口
(8)判定高亮内容是否为空,不为空将其存储到实例对象中
(9)返回结果集
/**
* 根据关键字搜索列表
* @param searchMap
* @return
*/
private Map searchList(Map searchMap) {
Map map = new HashMap();
HighlightQuery query = new SimpleHighlightQuery();
//设置高亮域
HighlightOptions highlightOptions = new HighlightOptions();
highlightOptions.addField("item_title");
//设置高亮前缀
highlightOptions.setSimplePrefix("<em style='color:red'>");
//设置高亮后缀
highlightOptions.setSimplePostfix("</em>");
//设置高亮选项
query.setHighlightOptions(highlightOptions);
//按照关键字查询
Criteria criteria = new Criteria("item_keywords").is(searchMap.get("keywords"));
query.addCriteria(criteria );
//执行查询
HighlightPage<TbItem> page = solrTemplate.queryForHighlightPage(query , TbItem.class);
//遍历高亮入口集合
for (HighlightEntry<TbItem> h : page.getHighlighted()) {
//获取原实体类
TbItem item = h.getEntity();
if(h.getHighlights().size()>0&&h.getHighlights().get(0).getSnipplets().size()>0) {
//设置高亮结果
item.setTitle(h.getHighlights().get(0).getSnipplets().get(0));
}
}
map.put("rows", page.getContent());
return map;
}
前端js
由于AngularJS有防HTML攻击,需对高亮标签添加信任,配置一个过滤器,运用到$sce.trustAsHtml();
app.filter("trustHtml",['$sce',function($sce){
return function(data){
return $sce.trustAsHtml(data);
}
}])
html页面修改
用ng-bind-html
<div class="attr" ng-bind-html="item.title | trustHtml"></div>
【十一章】
1、价格区间刷选
【业务逻辑】
2、分页
后端:【业务逻辑】
(1)初始定义分页属性,当前页pageNo,每页显示条目数pageSize
(2)分页查询,query.setOffset( arg0);//设置起始索引
query.setrows(); //设置每页条目数
返回值添加总页数totalPage、总记录数
前端:
(1)构建页码数组pageLaber
3、多关键字查询
去掉用户搜索输入的多余关键字
4、排序
【代码逻辑】:前端传递sort(排序的值:升序还是降序) 和sortField(排序的字段)
5、搜索页首页对接
【代码逻辑】:首页搜索框定义变量ng-model=“keywords”,通过location传递给搜索页面,搜索页面再通过$location.search()方法接受参数,并初始化赋值
【注意事项】
[ 1 ]传参的时候需要用#?
[ 2 ]接收参数时使用$location对象而不是location
[ 3 ]接收参数前需初始调用(接收参数)方法,接收参数后需调用查询方法
6、商品审核时将数据加入solr库
【代码逻辑】:在商品审核时调用seller-goods-service的goodsService,通过Long[ ] ids的值查询出审核商品所有的SKU对象集合,判断结果集是否有查到数据,查到则存入solr库中。
【个人理解】:觉得需要添加商品上架审核,在上架审核中添加到solr库,而不是在商品审核中,由于实现原理类似,不在此过多纠结
【十二章】freemark
建议学习:themeleaf
1、入门demo指令
(1)#assign:在页面定义变量
<#-- [1]#assign:在页面定义变量 -->
<#assign linkman="李四">
${linkman}<br>
(2)#include:用于模板的嵌套
<#include "head.ftl“>
(3)if:条件判断
<#if success=true>
你已通过实名认证
<#else>
你未通过实名认证
</#if>
(4)list:循环
<#-- [4]#list:循环 -->
<#list goodsList as goods>
${goods.name} ${goods.price} <br>
</#list>
(5)内置函数:
?size :集合大小
?eval:将json字符串转换为对象
2、配置代码
public static void main(String[] args) throws Exception {
//1、创建一个配置对象
Configuration configuration = new Configuration(Configuration.getVersion());
//2.设置模板所在目录
configuration.setDirectoryForTemplateLoading(new File("D:\\JAVA...."));
//3.设置默认编码集
configuration.setDefaultEncoding("UTF-8");
//4.获取模板对象
Template template = configuration.getTemplate("test.ftl");
//5.设置数据类型
.....
//6.创建输出流对象
Writer out = new FileWriter("d:\\test.html");
//7.输出
template.process(map, out);
//8.释放资源
out.close();
System.err.println("页面已生成");
}
整合到spring需要配置FreeMarkerConfigurer的 bean对象替换原先的配置对象
<!-- 配置属性 -->
<bean id="freemarkerConfig"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<!-- 配置模板所在目录 -->
<property name="templateLoaderPath" value="/WEB-INF/ftl/"/>
<!-- 配置默认编码集 -->
<property name="defaultEncoding" value="UTF-8" />
</bean>
【总结】:freemaker生成静态页面
构建一个模板页面,在配置文件中配置[1].整合spring的Bean对象,[2]保存页面生成的目录(.properties文件),在后端service层通过数据库查询出页面需要的数据对象,在模板页面中进行调用,前端可以整合AngularJS实现一些页面交互效果。
涉及功能:
(1)页面基本数据展示:
包括商品名称、商品价格、商品卖点、商品描述、商品扩展属性等等基本数据,这一类数据的特点是都为基本类型数据(字符串或数值,在页面展示时不需要循环遍历获取子属性),通过在后端service层查询数据库赋好值,在页面直接通过指令调用
(2)商品sku规格展示:
所有数据:goodsDesc表中的specification_items字段
数据格式:[{“attributeName”:“网络制式”,“attributeValue”:[“移动3G”,“移动4G”]},{“attributeName”:“屏幕尺寸”,“attributeValue”:[“6寸”,“5.5寸”]}]
因为在数据是以JSON类型字符串存储的,需要转换为对象后才能在页面进行显示。
所以在页面定义了变量specificationList:用于接受该JSON字符串转换为对象后的值(转换可在前端也可在后端);
在页面展示时用到双层嵌套循环,第一层循环JSON对象,遍历获取规格名称attributeName的值,第二层循环attributeValue获取每个规格属性的值。
显示效果:
网络制式 : 移动3G 移动4G
屏幕尺寸 : 6寸 5.5寸
(3)规格的数据绑定,以及选择效果切换:
需求:当点击规格按钮时出现特定的效果显示。
数据绑定:定义变量$scope.specificationItems={};定义一个方法,当点击规格时,将规格所属的值赋值给specificationItems,通过点击按钮改变变量的值来实现绑定。
//选择规格时给传参对象赋值
$scope.selectSpecification=function(key,value){
$scope.specificationItems[key]=value;
//改变sku(后面的逻辑)
$scope.search();
}
选择效果切换:定义一个方法,传入规格按钮所属的固定的值,然后将其与specificationItems进行对比,若两者相同,说明该按钮为点击按钮,在class=”“中调用并赋予select特效。
//判断规格是否被选中
$scope.isSelected=function(key,value){
if($scope.specificationItems[key]==value){
return true;
}else{
return false;
}
}
(4)默认sku显示
需求:当首次进入页面,显示该SPU商品的默认SKU。
实现:在后端查询item表时通过是否默认进行倒叙DESC排序,这样查询出来的itemList集合,0号索引即为默认SKU,定义一个方法将itemList[0]赋值给specificationItems对象,这样规格按钮会通过(3)中的方法自动判定是否应该被勾选。
//加载sku数据
$scope.loadSku=function(){
$scope.sku = skuList[0];
$scope.specificationItems=JSON.parse(JSON.stringify($scope.sku.spec));
}
需求:在切换规格时,同时改变SKU的其他属性(价格,商品名称等)
实现:在页面加载时,将该商品的所有SKU对象存入skuList集合中,在点击规格按钮时,同时判定所选的SKU(specificationItems对象)是否存在skuList中,若存在改变页面SKU显示数据。
方法实现逻辑:
//选择规格时,改变商品参数显示
$scope.search=function(){
for(i=0;i<skuList.length;i++){
var flag = $scope.matchObject(skuList[i].spec,$scope.specificationItems);
if(flag){
$scope.sku = skuList[i];
return ;
}
}
}
//匹配两集合的值是否相等
$scope.matchObject=function(map1,map2){
//遍历map1匹配map2
for(var key in map1){
if(map1[key]!=map2[key]){
return false;
}
}
//遍历map2匹配map1
for(var key in map2){
if(map2[key]!=map1[key]){
return false;
}
}
return true;
}
(5)购买数量的加减:逻辑略,
【未实现的功能】
当用户不是通过‘+’ - 按钮来增加,而是直接在文本框输入值来购买时,需要对文本框输入的值进行判定,如果小于1或者大于200,将其赋予范围内的值。
//增加和减少购买数量
$scope.addNum=function(value){
$scope.num=$scope.num+value;
if($scope.num<1){
$scope.num=1;
}
if($scope.num>200){
$scope.num=200;
}
}
【需要注意的点】:
(1)页面名称以商品id命名;
(2)当模板页面需要引入(json格式)数组对象,且其中有数值类型的值时,值后面需要加?c来取消逗号,以免破坏数据格式;
(3)在生成规格列表时,其中调用方法的传递参数需要用引号引起来,来实现传递固定的静态值。简单来说,在生成规格选项列表时,我们用的是遍历,其中绑定的值都是变量,但静态页面中每个选项传递的参数都是特定的,需要将变量加上引号将其变成固定值
【十三章】JMS,ActiveMQ
1、点对点、发布和订阅
(1)原生方式实现
提供者
public static void main(String[] args) throws JMSException {
//1.创建工厂对象
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.128:61616");
//2.获取连接对象
Connection connection = connectionFactory.createConnection();
//3.开启连接
connection.start();
//4.获取session会话对象
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
//5.创建消息发布者
//Quene quene = session.createQuene("text-quene");
Topic topic = session.createTopic("text-topic");
//6.创建消息生产者
//MessageProducer producer = session.createProducer(quene)
MessageProducer producer = session.createProducer(topic);
//7.创建消息文本信息
TextMessage textMessage = session.createTextMessage("这是一条群发消息");
//8.发布消息
producer.send(textMessage);
//9.关闭资源
producer.close();
session.close();
connection.close();
System.out.println("已发布群发消息");
}
消费者
public static void main(String[] args) throws JMSException, IOException {
//1.创建工厂对象
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.128:61616");
//2.获取连接对象
Connection connection = connectionFactory.createConnection();
//3.开启连接
connection.start();
//4.获取session会话对象
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//5.创建主题对象
//Quene quene = session.createQuene("text-quene");
Topic topic = session.createTopic("text-topic");
//6.创建消息订阅者对象
//MessageProducer producer = session.createProducer(quene)
MessageConsumer consumer = session.createConsumer(topic);
//7.设置监听
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage)message;
try {
System.out.println("接受到消息:"+textMessage.getText());
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
System.out.println("订阅者已就绪");
//8.等待键盘输入
System.in.read();
//9.释放资源
consumer.close();
session.close();
connection.close();
}
(2)spring整合
【十四章】短信验证-阿里大于
【实现思路】
(1)用户在注册页面申请账号,输出用户名,密码,手机号后点击发送短信验证,这时发请求到后端
后端:【实现发短信】
(2)后端接受到请求后首先随机生成一个6位数验证码,然后发送点对点消息到activeMQ中间件
消息采用mapMessage类型,存储[1]手机号,[2]验证码、[3]阿里大于签名、[4]模板id
(3)需要注意的是在生成验证码的的时候需要将验证码存储到redis中,采用hash存储,大KEY为固定的“sms”,小key为手机号码。值为生成的验证码
(4)在短信接受的服务端pinyougou-sms-service,创建一个监听类,读取到消息参数
(5)调用发短信服务的工具类发送短信
(6)用户收到短信,输入短信验证码,提交注册请求
这里传递的参数有:[1]用户名,[2]密码,[3]手机号,[4]验证码
(7)【校验两个密码文本框是否一样】通过分别给两个密码文本框绑定不同的变量名,在前端controller中先判断这两个密码变量是否相等,若相等发送请求到后端,若不相等,弹出错误提示框return。
后端:
(8)【实现校验验证码】后端接受到请求后
从redis中用手机号做小key取出缓存中验证码,比较两者是否相等
【注】:为了程序的健壮性,需先对其中一个进行非空判定,再进行比较,避免空指针异常
(9)【判定用户名是否存在】校验成功后,查询数据库,判断用户名是否已存在,若已存在,返回信息到前台,若用户名可用进行下一步
(10)【对密码进行加密】使用md5加密
(11)【存储数据】将数据存储到数据库