1. 完成商品后台管理
1.1 表格数据的展现方式
1.1.1 编辑页面
</script>-->
<table class="easyui-datagrid" style="width:500px;height:300px" data-options="url:'datagrid_data.json',method:'get',fitColumns:true,singleSelect:true,pagination:true">
<thead>
<tr>
<th data-options="field:'code',width:100">Code</th>
<th data-options="field:'name',width:100">Name</th>
<th data-options="field:'price',width:100,align:'right'">Price</th>
</tr>
</thead>
</table>
1.1.2 返回值类型的说明
属性信息: total/rows/属性元素
{
"total":2000,
"rows":[
{"code":"A","name":"果汁","price":"20"},
{"code":"B","name":"汉堡","price":"30"},
{"code":"C","name":"鸡柳","price":"40"},
{"code":"D","name":"可乐","price":"50"},
{"code":"E","name":"薯条","price":"10"},
{"code":"F","name":"麦旋风","price":"20"},
{"code":"G","name":"套餐","price":"100"}
]
}
1.2 JSON知识回顾
1.2.1 JSON介绍
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它使得人们很容易的进行阅读和编写。
1.2.2 Object对象类型
1.2.3 Array格式
1.2.4 嵌套格式
例子:
{"id":"100","hobbys":["玩游戏","敲代码","看动漫"],"person":{"age":"19","sex":["男","女","其他"]}}
1.3 编辑EasyUITablle的VO对象
package com.jt.vo;
import com.jt.pojo.Item;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.List;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class EasyUITable {
private Long total;
private List<Item> rows;
}
1.4 商品列表展现
1.4.1 页面分析
业务说明: 当用户点击列表按钮时.以跳转到item-list.jsp页面中.会解析其中的EasyUI表格数据.发起请求url:’/item/query’
要求返回值必须满足特定的JSON结构,所以采用EasyUITable方法进行数据返回.
<table class="easyui-datagrid" id="itemList" title="商品列表"
data-options="singleSelect:false,fitColumns:true,collapsible:true,pagination:true,url:'/item/query',method:'get',pageSize:20,toolbar:toolbar">
<thead>
<tr>
<th data-options="field:'ck',checkbox:true"></th>
<th data-options="field:'id',width:60">商品ID</th>
<th data-options="field:'title',width:200">商品标题</th>
<th data-options="field:'cid',width:100,align:'center',formatter:KindEditorUtil.findItemCatName">叶子类目</th>
<th data-options="field:'sellPoint',width:100">卖点</th>
<th data-options="field:'price',width:70,align:'right',formatter:KindEditorUtil.formatPrice">价格</th>
<th data-options="field:'num',width:70,align:'right'">库存数量</th>
<th data-options="field:'barcode',width:100">条形码</th>
<th data-options="field:'status',width:60,align:'center',formatter:KindEditorUtil.formatItemStatus">状态</th>
<th data-options="field:'created',width:130,align:'center',formatter:KindEditorUtil.formatDateTime">创建日期</th>
<th data-options="field:'updated',width:130,align:'center',formatter:KindEditorUtil.formatDateTime">更新日期</th>
</tr>
</thead>
</table>
1.4.2 请求路径的说明
请求路径: /item/query
参数: page=1 当前分页的页数.
rows = 20 当前锋分页行数.
当使用分页操作时,那么会自动的拼接2个参数.进行分页查询.
1.4.3 编辑ItemController
package com.jt.controller;
import com.jt.vo.EasyUITable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.jt.service.ItemService;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController //由于ajax调用 采用JSON串返回
@RequestMapping("/item")
public class ItemController {
@Autowired
private ItemService itemService;
/**
* url: http://localhost:8091/item/query?page=1&rows=20
* 请求参数: page=1&rows=20
* 返回值结果: EasyUITable
*/
@RequestMapping("/query")
public EasyUITable findItemByPage(Integer page,Integer rows){
return itemService.findItemByPage(page,rows);
}
}
1.4.4 编辑ItemService
package com.jt.service;
import com.jt.pojo.Item;
import com.jt.vo.EasyUITable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.jt.mapper.ItemMapper;
import java.util.List;
@Service
public class ItemServiceImpl implements ItemService {
@Autowired
private ItemMapper itemMapper;
/**
* 分页查询商品信息
* Sql语句: 每页20条
* select * from tb_item limit 起始位置,查询的行数
* 查询第一页
* select * from tb_item limit 0,20; [0-19]
* 查询第二页
* select * from tb_item limit 20,20; [20,39]
* 查询第三页
* select * from tb_item limit 40,20; [40,59]
* 查询第N页
* select * from tb_item limit (n-1)*rows,rows;
*
*
* @param rows
* @return
*/
@Override
public EasyUITable findItemByPage(Integer page, Integer rows) {
//1.手动完成分页操作
int startIndex = (page-1) * rows;
//2.数据库记录
List<Item> itemList = itemMapper.findItemByPage(startIndex,rows);
//3.查询数据库总记录数
Long total = Long.valueOf(itemMapper.selectCount(null));
//4.将数据库记录 封装为VO对象
return new EasyUITable(total,itemList);
//MP
}
}
1.4.5 页面效果展现
1.5 参数格式化说明
1.5.1 商品价格格式化说明
1).页面属性说明
当数据在进行展现时,会通过formatter关键字之后进行数据格式化调用. 具体的函数KindEditorUtil.formatPrice函数.
<th data-options="field:'price',width:70,align:'right',formatter:KindEditorUtil.formatPrice">价格</th>
2).页面JS分析
// 格式化价格 val="数据库记录" 展现的应该是缩小100倍的数据
formatPrice : function(val,row){
return (val/100).toFixed(2);
},
1.5.2 格式化状态信息
1). 页面标识
<th data-options="field:'status',width:60,align:'center',formatter:KindEditorUtil.formatItemStatus">状态</th>
2).页面JS分析
// 格式化商品的状态
formatItemStatus : function formatStatus(val,row){
if (val == 1){
return '<span style="color:green;">正常</span>';
} else if(val == 2){
return '<span style="color:red;">下架</span>';
} else {
return '未知';
}
},
1.6 格式化叶子类目
1.6.1 页面分析
说明:根据页面标识, 要在列表页面中展现的是商品的分类信息. 后端数据库只传递了cid的编号.我们应该展现的是商品分类的名称.方便用户使用…
<th data-options="field:'cid',width:100,align:'center',formatter:KindEditorUtil.findItemCatName">叶子类目</th>
1.6.2 页面js分析
//格式化名称 val=cid 返回商品分类名称
findItemCatName : function(val,row){
var name;
$.ajax({
type:"get",
url:"/item/cat/queryItemName", //
data:{itemCatId:val},
cache:true, //缓存
async:false, //表示同步 默认的是异步的true
dataType:"text",//表示返回值参数类型
success:function(data){
name = data;
}
});
return name;
}
1.6.3 编辑ItemCatPOJO对象
package com.jt.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
@TableName("tb_item_cat")
@Data
@Accessors(chain = true)
public class ItemCat extends BasePojo{
@TableId(type = IdType.AUTO)
private Long id;
private Long parentId;
private String name;
private Integer status;
private Integer sortOrder;
private Boolean isParent; //数据库进行转化
}
1.6.4 编辑ItemCatController
@RestController
@RequestMapping("/item/cat")
public class ItemCatController {
@Autowired
private ItemCatService itemCatService;
/**
* url地址:/item/cat/queryItemName
* 参数: {itemCatId:val}
* 返回值: 商品分类名称
*/
@RequestMapping("/queryItemName")
public String findItemCatNameById(Long itemCatId){
//根据商品分类Id查询商品分类对象
ItemCat itemCat = itemCatService.findItemCatById(itemCatId);
return itemCat.getName(); //返回商品分类的名称
}
}
1.6.5 编辑ItemCatService
package com.jt.service;
import com.jt.mapper.ItemCatMapper;
import com.jt.pojo.ItemCat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ItemCatServiceImpl implements ItemCatService{
@Autowired
private ItemCatMapper itemCatMapper;
@Override
public ItemCat findItemCatById(Long itemCatId) {
return itemCatMapper.selectById(itemCatId);
}
}
1.6.6 页面效果展现
1.7 关于Ajax嵌套问题说明
1.7.1 问题描述
当将ajax改为异步时,发现用户的请求数据不能正常的响应.
//格式化名称 val=cid 返回商品分类名称
findItemCatName : function(val,row){
var name;
$.ajax({
type:"get",
url:"/item/cat/queryItemName",
data:{itemCatId:val},
//cache:true, //缓存
async:true, //表示同步 默认的是异步的true
dataType:"text",//表示返回值参数类型
success:function(data){
name = data;
}
});
return name;
},
1.7.2 问题分析
商品的列表中发起2次ajax请求.
1).
data-options="singleSelect:false,fitColumns:true,collapsible:true,pagination:true,url:'/item/query',method:'get',pageSize:20,toolbar:toolbar">
2).ajax请求
1.7.3 解决方案
说明: 一般条件的下ajax嵌套会将内部的ajax设置为同步的调用.不然可能会犹豫url调用的时间差导致数据展现不完全的现象.
1.8 关于端口号占用问题
1.9 关于common.js引入问题
说明:一般会将整个页面的JS通过某个页面进行标识,之后被其他的页面引用即可… 方便以后的JS的切换.
1).引入xxx.jsp页面
1.10 MybatisPlus 完成分页操作
1.10.1 编辑ItemService
//尝试使用MP的方式进行分页操作
@Override
public EasyUITable findItemByPage(Integer page, Integer rows) {
QueryWrapper<Item> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("updated");
//暂时只封装了2个数据 页数/条数
IPage<Item> iPage = new Page<>(page, rows);
//MP 传递了对应的参数,则分页就会在内部完成.返回分页对象
iPage = itemMapper.selectPage(iPage,queryWrapper);
//1.获取分页的总记录数
Long total = iPage.getTotal();
//2.获取分页的结果
List<Item> list = iPage.getRecords();
return new EasyUITable(total, list);
}
1.10.2 编辑配置类
@Configuration //标识我是一个配置类
public class MybatisPlusConfig {
//MP-Mybatis增强工具
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
}
2 商品新增
2.1 工具栏菜单说明
2.1.1 入门案例介绍
toolbar: [{
iconCls: 'icon-help',
handler: function(){alert("点击工具栏")}
},{
iconCls: 'icon-help',
handler: function(){alert('帮助工具栏')}
},'-',{
iconCls: 'icon-save',
handler: function(){alert('保存工具栏')}
},{
iconCls: 'icon-add',
text: "测试",
handler: function(){alert('保存工具栏')}
}]
2.1.2 表格中的图标样式
页面结构:
2.2 页面弹出框效果
2.2.1 页面弹出框效果展现
$("#btn1").bind("click",function(){
//注意必须选中某个div之后进行弹出框展现
$("#win1").window({
title:"弹出框",
width:400,
height:400,
modal:false //这是一个模式窗口,只能点击弹出框,不允许点击别处
})
})
2.3 树形结构展现
2.3.1 商品分类目录结构
说明:一般电商网站商品分类信息一般是三级目录.
表设计: 一般在展现父子级关系时,一般采用parent_id的方式展现目录信息.
2.3.2 树形结构入门-html结构
<script type="text/javascript">
/*通过js创建树形结构 */
$(function(){
$("#tree").tree({
url:"tree.json", //加载远程JSON数据
method:"get", //请求方式 get
animate:false, //表示显示折叠端口动画效果
checkbox:true, //表述复选框
lines:false, //表示显示连接线
dnd:true, //是否拖拽
onClick:function(node){ //添加点击事件
//控制台
console.info(node);
}
});
})
</script>
2.3.3 树形结构入门-JSON串结构
一级树形结构的标识.
“[{“id”:“3”,“text”:“吃鸡游戏”,“state”:“open/closed”},{“id”:“3”,“text”:“吃鸡游戏”,“state”:“open/closed”}]”
2.3.4 VO对象封装-EasyUITree
package com.jt.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class EasyUITree implements Serializable {
private Long id; //节点ID信息
private String text; //节点的名称
private String state; //节点的状态 open 打开 closed 关闭
}
2.4 商品分类展现
2.4.1 页面分析
<tr>
<td>商品类目:</td>
<td>
<a href="javascript:void(0)" class="easyui-linkbutton selectItemCat">选择类目</a>
<input type="hidden" name="cid" style="width: 280px;"></input>
</td>
</tr>
2.4.2 页面JS标识
页面URL标识:
2.4.3 编辑ItemCatController
/**
* 业务需求: 用户通过ajax请求,动态获取树形结构的数据.
* url: http://localhost:8091/item/cat/list
* 参数: 只查询一级商品分类信息 parentId = 0
* 返回值结果: List<EasyUITree>
*/
@RequestMapping("/list")
public List<EasyUITree> findItemCatList(){
Long parentId = 0L;
return itemCatService.findItemCatList(parentId);
}
2.4.4 编辑ItemCatService
/**
* 返回值: List<EasyUITree> 集合信息
* 数据库查询返回值: List<ItemCat>
* 数据类型必须手动的转化
* @param parentId
* @return
*/
@Override
public List<EasyUITree> findItemCatList(Long parentId) {
//1.查询数据库记录
QueryWrapper<ItemCat> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id", parentId);
List<ItemCat> catList = itemCatMapper.selectList(queryWrapper);
//2.需要将catList集合转化为voList 一个一个转化
List<EasyUITree> treeList = new ArrayList<>();
for(ItemCat itemCat :catList){
Long id = itemCat.getId();
String name = itemCat.getName();
//如果是父级 应该closed 如果不是父级 应该open
String state = itemCat.getIsParent()?"closed":"open";
EasyUITree tree = new EasyUITree(id, name, state);
treeList.add(tree);
}
return treeList;
}
2.4.5 页面效果展现
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jt</groupId> <artifactId>jt</artifactId> <version>1.0-SNAPSHOT</version> <!--当前父级工程打包类型,如果修改了项目的名称记得修改modules标签 --> <modules> <module>jt-common</module> <module>jt-manage</module> <module>jt-web</module> <module>jt-sso</module> <module>jt-cart</module> <module>jt-order</module> </modules> <!--定义父级工程打包类型--> <packaging>pom</packaging> <!--1.引入springboot的父级项目 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <!--2.引入属性的配置--> <properties> <java.version>1.8</java.version> <!--跳过测试类打包--> <skipTests>true</skipTests> </properties> <!-- 在父级项目中添加jar包文件--> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <!--引入插件lombok 自动的set/get/构造方法插件 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--引入数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!--springBoot数据库连接 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--spring整合mybatis-plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.2.0</version> </dependency> <!--springBoot整合JSP添加依赖 --> <!--servlet依赖 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> </dependency> <!--jstl依赖 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <!--使jsp页面生效 --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> <!--支持热部署 --> <!-- <dependency>--> <!-- <groupId>org.springframework.boot</groupId>--> <!-- <artifactId>spring-boot-devtools</artifactId>--> <!-- </dependency>--> <!--添加httpClient jar包 --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <!--引入dubbo配置--> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.2.0</version> </dependency> <!--添加Quartz的支持 --> <!-- <dependency>--> <!-- <groupId>org.springframework.boot</groupId>--> <!-- <artifactId>spring-boot-starter-quartz</artifactId>--> <!-- </dependency>--> <!-- 引入aop支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!--spring整合redis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> </dependency> </dependencies> <!-- 父级工程只是项目的管理者不会在其中编辑代码,所以不需要添加build --> </project>
#配置redis单台服务器 redis.host=192.168.126.129 redis.port=6379 #配置redis分片机制 redis.nodes=192.168.126.129:6379,192.168.126.129:6380,192.168.126.129:6381 #配置哨兵节点 redis.sentinel=192.168.126.129:26379 #配置redis集群 redis.clusters=192.168.126.129:7000,192.168.126.129:7001,192.168.126.129:7002,192.168.126.129:7003,192.168.126.129:7004,192.168.126.129:7005
package com.jt.anno; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD)//注解对方法有效 @Retention(RetentionPolicy.RUNTIME)//运行期有效 public @interface CacheFind { public String preKey();//用户标识key的前缀 public int seconds() default 0;//如果用户不写表示不需要超时,如果写了以用户为准 }
package com.jt.aop; import com.jt.anno.CacheFind; import com.jt.util.ObjectMapperUtil; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisSentinelPool; import redis.clients.jedis.ShardedJedis; import java.util.Arrays; @Aspect //我是一个切面类 @Component //将类交给spring容器管理 public class CacheAOP { @Autowired private JedisCluster jedis; // private Jedis jedis;//单台的redis // private ShardedJedis jedis;//分片机制 // private JedisSentinelPool jedisSentinelPool;//配置哨兵机制 /** * 切面=切入点+通知方法 * 注解相关+环绕通知 控制目标方法是否执行 * * 难点: * 1.如何获取注解对象??? * 2.动态生成key prekey+用户参数数组 * 3.如何获取方法的返回值类型 */ @Around("@annotation(cacheFind)") public Object around(ProceedingJoinPoint joinPoint, CacheFind cacheFind){ //从池中获取jedis对象 //Jedis jedis=jedisSentinelPool.getResource(); Object result=null; try { //1.拼接redis存储数据的key Object[] args=joinPoint.getArgs(); String key=cacheFind.preKey()+"::"+Arrays.toString(args); //2.查询redis之后判断是否有数据 if(jedis.exists(key)){ //1.拼接redis之后判断是否有数据 String json=jedis.get(key); //动态获取方法的返回值类型 向上造型 向下造型 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Class returnType=methodSignature.getReturnType(); result=ObjectMapperUtil.toObj(json, returnType); System.out.println("AOP查询redis缓存"); }else { //表示数据不存在,需要查询数据库 result = joinPoint.proceed();//执行目标方法及通知 //将查询的结果保存到redis中去 String json = ObjectMapperUtil.toJSON(result); //判断数据是否需要超时时间 if (cacheFind.seconds() > 0) { jedis.setex(key, cacheFind.seconds(), json); } else { jedis.set(key, json); } System.out.println("aop执行目标方法查询数据库"); } } catch (Throwable throwable) { throwable.printStackTrace(); } //jedis.close();//将使用完成的链接记得关闭 return result; } // //公式:切入点表达式+通知方法 // // /** // * 关于切入点表达式的使用说明 // * 粗粒度: // * 1.bean(bean的Id) 一个类 // * 2.within(包名.类名) 多个类 // */ // //@Pointcut("bean(itemCatServiceImpl)") // //@Pointcut("within(com.jt.service..*)")//匹配多级目录 // //@Pointcut("within(com.jt.service.*)") // @Pointcut("execution(* com.jt.service..*.*(..))")//方法参数级别 // public void pointCut(){ // // //定义切入点表达式 只为了占位 // } // // /** // * 区别:pointCut()表示切入点表达式的引用 适用于多个通知共用切入点的情况 // * @Before("bean(itemCatServiceImpl)") 适用于单个通知,不需要复用的 // */ // //定义前置通知,与切入点表达式绑定,注意绑定的是方法 // // /** // * 需求:获取目标对象的相关信息 // * 1.获取目标方法的路径 包名 类名 方法名 // * 2.获取目标方法的类型 class // * 3.获取传递的参数 // * 4.记录当前的执行时间 // * // */ // @Before("pointCut()") // //@Before("bean(itemCatServiceImpl)") // public void before(JoinPoint joinPoint){ // // // String className=joinPoint.getSignature().getDeclaringTypeName(); // String methodName=joinPoint.getSignature().getName(); // Class targetClass=joinPoint.getTarget().getClass(); // Object[] args=joinPoint.getArgs(); // Long runTime=System.currentTimeMillis(); // System.out.println("方法路径:"+className+"."+methodName); // System.out.println("目标方法:"+targetClass); // System.out.println("参数:"+ Arrays.toString(args)); // System.out.println("执行时间:"+runTime+"毫秒"); // } // @AfterReturning("pointCut()") public void afterReturn(){ System.out.println("我是后置通知!!!!"); } @After("pointCut()") public void after(){ System.out.println("我是最终通知!!!"); } // // // /** // * 环绕通知 // * 注意事项: // * 1.环绕通知中必须添加参数 // * 2.ProceedingJoinPoint 只能环绕通知使用 // * 3.ProceedingJoinPoint 如果当做参数 则必须位于参数的第一位 // */ // @Around("pointCut()") // public Object around(ProceedingJoinPoint joinPoint){ // // System.out.println("环绕通知开始!!!!"); // Object result=null; // try { // result=joinPoint.proceed();//执行下一个通知或者目标方法 // } catch (Throwable throwable) { // throwable.printStackTrace(); // } // System.out.println("环绕通知结束!!!"); // return result; // } }
package com.jt.aop; import com.fasterxml.jackson.databind.util.JSONPObject; import com.jt.vo.SysResult; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.servlet.http.HttpServletRequest; import java.sql.SQLException; //@ControllerAdvice //拦截controller层 //@ResponseBody @RestControllerAdvice //定义全局异常处理类 aop=异常通知 public class SystemAOP { //定义全局异常的方法,当遇到了什么异常时,程序开始执行 参数一般class类型 /** * 如果一旦发生异常,则应该输出异常的信息,之后返回错误数据即可 * * 解决跨域全局异常处理的规则: * 京淘项目的跨域都是使用JSONP, * http://xxxxx?callback=xxxxxx * 如果请求中携带了callback参数 则认为是JSONP跨域请求 * 难点: * 如何获取callback参数呢? * * @return */ @ExceptionHandler({RuntimeException.class}) public Object systemAop(Exception e, HttpServletRequest request){ e.printStackTrace(); String callback=request.getParameter("callback"); if (StringUtils.isEmpty(callback)){ return SysResult.fail(); }else { //证明是jsponp跨域请求 return new JSONPObject(callback, SysResult.fail()); } } }
package com.jt.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class CorsConfig implements WebMvcConfigurer { //在后端 配置cros允许访问的策略 @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedMethods("GET","POST")//定义允许跨域的请求类型 .allowedOrigins("*")//任意的网址都可以访问 .allowCredentials(true)//是否允许携带cookie .maxAge(1800);//设定请求长链接超时时间 } }
package com.jt.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import redis.clients.jedis.*; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @Configuration @PropertySource("classpath:/properties/redis.properties") public class JedisConfig { @Value("${redis.clusters}") private String clusters; @Bean public JedisCluster jedisCluster(){ Set<HostAndPort> nodes=new HashSet<>(); String[] nodesArray=clusters.split(","); for (String node:nodesArray){ String host=node.split(":")[0]; int port=Integer.parseInt(node.split(":")[1]); HostAndPort hostAndPort=new HostAndPort(host, port); nodes.add(hostAndPort); } return new JedisCluster(nodes); } // @Value("${redis.sentinel}") // private String sentinel;//暂时只有单台 // // @Bean // public JedisSentinelPool jedisSentinelPool(){ // // Set<String> sentinels =new HashSet<>(); // sentinels.add(sentinel); // JedisPoolConfig jedisPoolConfig=new JedisPoolConfig(); // jedisPoolConfig.setMaxTotal(100);//最大链接数量 // jedisPoolConfig.setMaxIdle(40);//最大空闲数量 // jedisPoolConfig.setMinIdle(20);//最小空闲数量 // return new JedisSentinelPool("mymaster", sentinels,jedisPoolConfig); // } // @Value("${redis.nodes}") // private String nodes;//node,node,node.... // /** // * 配置redis分片机制 // * // */ // @Bean // public ShardedJedis shardedJedis(){ // nodes=nodes.trim();//去除两边多余的空格 // List<JedisShardInfo> shards=new ArrayList<>(); // String[] nodeArray=nodes.split(","); // for (String strNode : nodeArray){//strNode=host:port // // String host=strNode.split(":")[0]; // int port=Integer.parseInt(strNode.split(":")[1]); // JedisShardInfo info=new JedisShardInfo(host,port); // shards.add(info); // } // // return new ShardedJedis(shards); // } // @Value("${redis.host}") // private String host; // @Value("${redis.port}") // private Integer port; // // @Bean // public Jedis jedis(){ // // return new Jedis(host,port); // } }
package com.jt.config; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import java.util.Date; @Component //将对象交给spring容器管理 public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { Date dete=new Date();//保证时间一致 this.setInsertFieldValByName("created", dete, metaObject); this.setUpdateFieldValByName("updated", dete, metaObject); } @Override public void updateFill(MetaObject metaObject) { this.setUpdateFieldValByName("updated", new Date(), metaObject); } }
package com.jt.util; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class CookieUtil { //1.新增cookie public static void addCookie(HttpServletResponse response,String cookieName, String cookieValue, int seconds, String domain){ Cookie cookie=new Cookie(cookieName, cookieValue); cookie.setMaxAge(seconds); cookie.setDomain(domain); cookie.setPath("/"); response.addCookie(cookie); } //2.根据name查询value的值 public static String getCookieValue(HttpServletRequest request,String cookieName){ Cookie[] cookies=request.getCookies(); if (cookies!=null && cookies.length>0){ for (Cookie cookie: cookies) { if (cookieName.equals(cookie.getName())){ return cookie.getValue(); } } } return null; } //3.删除cookie public static void deleteCookie(HttpServletResponse response,String cookieName,String domain){ addCookie(response, cookieName, "", 0, domain); } }
package com.jt.util; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.util.StringUtils; public class ObjectMapperUtil { /** * 1.将用户传递的数据转化为json串 * 2.将用户传递的json串 转化为对象 * */ private static final ObjectMapper MAPPER=new ObjectMapper(); //1.将用户传递的数据转化为json串 public static String toJSON(Object object){ if (object==null){ throw new RuntimeException("传递的数据为null,请检查"); } try { String json=MAPPER.writeValueAsString(object); return json; } catch (JsonProcessingException e) { //将检查异常,转化为运行时异常 e.printStackTrace(); throw new RuntimeException(e); } } //需求:要求用户传递什么样的类型,我返回什么样的对象 泛型的知识 public static <T> T toObj(String json,Class<T> target){ if (StringUtils.isEmpty(json) || target==null){ throw new RuntimeException("参数不能为null"); } try { return MAPPER.readValue(json, target); } catch (JsonProcessingException e) { e.printStackTrace(); throw new RuntimeException(e); } } }
package com.jt.vo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; /** * SysResult 主要的目的是为了与页面进行交互 ajax/json * */ @Data @Accessors(chain = true) @NoArgsConstructor @AllArgsConstructor public class SysResult { private Integer status;//200成功 201失败 private String msg;//服务器提示信息 成功 失败 private Object data;//服务器返回值数据 //可以利用static的静态方法,将数据动态返回 public static SysResult fail(){ return new SysResult(201,"业务执行失败",null); } /** * 1.只需要返回状态码信息 200 * 2.需要返回状态及业务数据 200/data * 3.返回提示信息/data业务数据 * @return */ public static SysResult success(){ return new SysResult(200,"业务执行成功",null); } public static SysResult success(Object data){ return new SysResult(200,"业务执行成功",data); } /** * 只想返回提示信息 * * @return */ public static SysResult success(String msg,Object data){ return new SysResult(200,msg,data); } }
package com.jt.pojo; import java.io.Serializable; import java.util.Date; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import lombok.Data; import lombok.experimental.Accessors; //pojo基类,完成2个任务,2个日期,实现序列化 @Data @Accessors(chain=true) public class BasePojo implements Serializable{ @TableField(fill = FieldFill.INSERT)//入库时自动添加 private Date created; @TableField(fill = FieldFill.INSERT_UPDATE)//入库/更新操作自动添加 private Date updated; }
package com.jt.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.experimental.Accessors; @TableName("tb_cart") @Data @Accessors(chain = true) public class Cart extends BasePojo{//使用包装类型 @TableId(type = IdType.AUTO) private Long id; private Long userId;//用户id号 private Long itemId;//商品id号 private String itemTitle;//商品标题 private String itemImage;//图片 private Long itemPrice;//商品价格 private Integer num;//商品数量 }
package com.jt.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; import lombok.experimental.Accessors; @JsonIgnoreProperties(ignoreUnknown=true) //表示JSON转化时忽略未知属性 @TableName("tb_item") @Data @Accessors(chain=true) public class Item extends com.jt.pojo.BasePojo { @TableId(type=IdType.AUTO) private Long id; //商品id private String title; //商品标题 private String sellPoint; //商品卖点信息 private Long price; //商品价格 Long > dubbo 0.98*100=98/100=0.98 private Integer num; //商品数量 private String barcode; //条形码 private String image; //商品图片信息 1.jpg,2.jpg,3.jpg private Long cid; //表示商品的分类id private Integer status; //1正常,2下架 //为了满足页面调用需求,添加get方法 public String[] getImages(){ return image.split(","); } }
package com.jt.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.experimental.Accessors; @TableName("tb_item_cat") @Data @Accessors(chain = true) public class ItemCat extends BasePojo{ @TableId(type = IdType.AUTO) private Long id; private Long parentId; private String name; private Integer status; private Integer sortOrder; private Boolean isParent;//数据库进行转化 }
package com.jt.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; @TableName("tb_item_desc") @Data @Accessors(chain = true) public class ItemDesc extends BasePojo{ //item中的id与ItemDesc中的Id应该保持一致.... @TableId //只标识主键 不能自增 private Long itemId; private String itemDesc; }
package com.jt.pojo; import java.util.Date; import java.util.List; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.experimental.Accessors; @TableName("tb_order") @Data @Accessors(chain=true) public class Order extends BasePojo{ @TableField(exist=false) //入库操作忽略该字段 private OrderShipping orderShipping; //封装订单商品信息 一对多 @TableField(exist=false) //入库操作忽略该字段 private List<OrderItem> orderItems; @TableId private String orderId;//(type=IdType.AUTO) 订单号:登陆用户id+当前时间戳,UUID 2*128 private String payment; private Integer paymentType; private String postFee; private Integer status; private Date paymentTime; private Date consignTime;//状态:1、未付款2、已付款3、未发货4、已发货5、交易成功6、交易关闭', private Date endTime; private Date closeTime; private String shippingName; private String shippingCode; private Long userId; private String buyerMessage; private String buyerNick; private Integer buyerRate; }
package com.jt.pojo; import java.util.Date; import java.util.List; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.experimental.Accessors; @TableName("tb_order_item") @Data @Accessors(chain=true) public class OrderItem extends BasePojo{ @TableId private String itemId; //删除主键 private String orderId; private Integer num; private String title; private Long price; private Long totalFee; private String picPath; }
package com.jt.pojo; import java.util.Date; import java.util.List; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.experimental.Accessors; @TableName("tb_order_shipping") @Data @Accessors(chain=true) public class OrderShipping extends BasePojo{ @TableId private String orderId; private String receiverName; private String receiverPhone; private String receiverMobile; private String receiverState; private String receiverCity; private String receiverDistrict; private String receiverAddress; private String receiverZip; }
package com.jt.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.experimental.Accessors; @TableName("tb_user") @Data @Accessors(chain = true) public class User extends BasePojo{//必须实现序列化接口,父级代替完成 @TableId(type = IdType.AUTO)//设定主键自增 private Long id;//用户密码 private String username;//用户名 private String password;//密码 需要MD5加密 private String phone;//电话号码 private String email;//暂时使用电话号码代替邮箱 }
package com.jt.service; import com.jt.pojo.Cart; import org.springframework.transaction.annotation.Transactional; import java.util.List; public interface DubboCartService { List<Cart> findCartListByUserId(Long userId); @Transactional void updateCartNum(Cart cart); @Transactional void saveCart(Cart cart); void deleteCarts(Long userId, Long itemId); }
package com.jt.service; import com.jt.pojo.Item; import com.jt.pojo.ItemDesc; public interface DubboItemService { Item findItemById(Long itemId); ItemDesc findItemDescById(Long itemId); }
package com.jt.service; import com.jt.pojo.Order; import org.springframework.transaction.annotation.Transactional; public interface DubboOrderService { @Transactional String saveOrder(Order order); Order findOrderById(String id); }
package com.jt.service; import com.jt.pojo.User; import org.springframework.transaction.annotation.Transactional; public interface DubboUserService { @Transactional void saveUser(User user); String doLogin(User user); }