JT开发文档
1.0京淘项目搭建
1.01创建jt 父级项目
1.02选择jar包类型
1.10添加pom.xml文件
<!--定义了父级的依赖项 定义了springBoot内部的全部关联配置 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<!--springboot2.0版本中 与IDE整合有问题 -->
<properties>
<java.version>1.8</java.version>
<!--默认条件下执行的maven解析版本问题 -->
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
</properties>
<dependencies>
<!--开箱即用 spring-boot-starter springBoot的启动器 -->
<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>
<!--添加属性注入依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--支持热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</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>
<version>5.1.32</version>
</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>
<!-- 引入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>
<!--添加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>
</dependencies>
<!--只添加parent和依赖即可,不需要添加插件 -->
1.2.1构建jt-common
1.2.2选择maven-module方式创建项目
1.2.3选择jar包类型
3.格式如下
1.3.1构建jt-manage项目
1.3.1.1创建项目
1.3.2选择打包类型
1.3.3添加继承/依赖/插件
<dependencies>
<dependency>
<groupId>com.jt</groupId>
<artifactId>jt-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--跳过测试类打包 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
1.3.4导入静态资源文件
1.3.5编辑jt-manage
package com.jt.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
@RequestMapping("/page/{moduleName}")
public String module(@PathVariable String moduleName) {
return moduleName;
}
}
package com.jt.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.jt.service.ItemService;
@Controller
public class ItemController {
@Autowired
private ItemService itemService;
}
package com.jt.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jt.pojo.Item;
public interface ItemMapper extends BaseMapper<Item>{
}
package com.jt.service;
public interface ItemService {
}
package com.jt.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.jt.mapper.ItemMapper;
@Service
public class ItemServiceImpl implements ItemService {
@Autowired
private ItemMapper itemMapper;
}
package com.jt;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.jt.mapper")
public class SpringBootRun {
public static void main(String[] args) {
SpringApplication.run(SpringBootRun.class, args);
}
}
1.3.6编辑com.jt.pojo的类
package com.jt.pojo;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
import lombok.experimental.Accessors;
//pojo基类,完成2个任务,2个日期,实现序列化
@Data
@Accessors(chain=true)
public class BasePojo implements Serializable{
private Date created;
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 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 BasePojo{
@TableId(type=IdType.AUTO)
private Long id; //商品id
private String title; //商品标题
private String sellPoint; //商品卖点信息
private Long price; //商品价格 Long > dubbo
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(",");
}
}
2.3.6.1启动效果
1.4.1编辑manage 里的vo对象
package com.jt.vo;
import java.io.Serializable;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class EasyUITable implements Serializable{
/**
* 实行不能随意定义,必须满足js数据要求
*/
private Integer total;
private List<?> rows;
}
1.5商品列表展现
1.5.1 商品表设计
create table tb_item
(
id bigint(10) not null auto_increment comment '商品ID,也是商品编号',
title varchar(100),
sell_point varchar(150),
price bigint(20) comment '单位为:分',
num int(10),
barcode varchar(30),
image varchar(500) comment '最多5张图片',
cid bigint(10),
status int(1) default 1 comment '默认值为1,可 选值:1正常,2下架,3删除',
created datetime,
updated datetime comment '列表排序时按修改 时间排序,所以在新增时需要设置此值。',
primary key (id)
);
1.5.2编辑jt-common pojo
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 BasePojo{
@TableId(type=IdType.AUTO)
private Long id; //商品id
private String title; //商品标题
private String sellPoint; //商品卖点信息
private Long price; //商品价格 扩大100倍保存
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(",");
}
}
1.5.5编辑jt-manage ItemController
@RestController //保证返回值数据都是JSON时使用\
@RequestMapping("/item")
public class ItemController {
@Autowired
private ItemService itemService;
/**
* 根据条件查询数据信息.
* url:http://localhost:8091/item/query?page=1&rows=20
*/
@RequestMapping("/query")
public EasyUITable findItemByPage
(Integer page,Integer rows) {
return itemService.findItemByPage(page,rows);
}
}
1.5.6编辑 jt-manage ItemService 和ItemServiceImpl
package com.jt.service;
public interface ItemService {
EasyUITable findItemByPage(Integer page, Integer rows);
}
package com.jt.service;
@Service
public class ItemServiceImpl implements ItemService {
@Autowired
private ItemMapper itemMapper;
/*
* 1.查询商品总记录数
* 2.进行分页查询
*
* 分页sql: 每页20条
* 第1页 起始位置,展现条数
* select * from tb_item limit 0,20 [0,19]
* 第2页
* select * from tb_item limit 20,20 [20,39]
* 第3页
* select * from tb_item limit 40,20 [40,59]
* 第N页
* select * from tb_item order by updated limit (page-1)*rows,rows
*/
@Override
public EasyUITable findItemByPage(Integer page, Integer rows) {
int total = itemMapper.selectCount(null);
int start = (page - 1) * rows;
List<Item> userList =
itemMapper.findItemByPage(start,rows);
return new EasyUITable(total, userList);
}
}
1.5.7编辑Mapper接口
package com.jt.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jt.pojo.Item;
public interface ItemMapper extends BaseMapper<Item>{
@Select("select * from tb_item order by updated desc limit #{start},#{rows}")
List<Item> findItemByPage(@Param("start")Integer start,@Param("rows")Integer rows);
}
1.6Mybatis-plus方式实现分页
1.6.1编辑业务层ItemServiceImpl
* mybatis-plus分页说明
* 1.new Page<>(current, size);
* current:当前页数
* size: 每页条数
*/
@Override
public EasyUITable findItemByPage(Integer page, Integer rows) {
Page<Item> tempPage = new Page<>(page, rows);
QueryWrapper<Item> queryWrapper = new QueryWrapper<Item>();
queryWrapper.orderByDesc("updated");
//当前查询的分页结果对象
IPage<Item> IPage =
itemMapper.selectPage(tempPage, queryWrapper);
//获取总记录数
int total = (int) IPage.getTotal();
//获取分页的结果
List<Item> userList = IPage.getRecords();
return new EasyUITable(total, userList);
}
1.6.2编辑配置类
说明:定义MybatisConfis的配置类,在com.jr.config中
package com.jt.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
@Configuration //配置类
public class MybatisConfig {
/* <bean id="paginationInterceptor" class=""/> */
//添加分页拦截器,否则分页有问题!!!!
@Bean //将对象交给Spring容器管理
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
1.7 实现商品分类目录回显
1.71格式化价格
return now.format("yyyy-MM-dd hh:mm:ss");data-o<th ptions="field:'price',width:70,align:'right',formatter:KindEditorUtil.formatPrice">价格</th>
2.common.js
// 格式化价格 value是数据库数据
ormatPrice : function(val,row){
return (val/100).toFixed(2);
},
1.7.2格式化时间
1.item-list页面
<th data-options="field:'created',width:130,align:'center',formatter:KindEditorUtil.formatDateTime">创建日期</th>
2.common.js
// 格式化时间
formatDateTime : function(val,row){
var now = new Date(val);
},
1.8格式化叶子类目
1.8.1页面分析
<th data-options="field:'cid',width:100,align:'center',formatter:KindEditorUtil.findItemCatName">叶子类目</th>
1.8.2编辑common.js
//格式化名称
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.8.4编辑 jt-common ItemCat pojo对象
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.8.4编辑jt-manage ItemCatController
@RestController
@RequestMapping("/item/cat")
public class ItemCatController {
@Autowired
private ItemCatService itemCatService;
/**
* 根据itemCatId查询商品分类名称
* http://localhost:8091/item/cat/queryItemName?itemCatId=560
*/
@RequestMapping("/queryItemName")
public String findItemCatNameById(Long itemCatId) {
//1.先根据id查询对象
ItemCat itemCat =
itemCatService.findItemCatById(itemCatId);
//2.将对象中的name名称获取
return itemCat.getName();
}
}
1.8.5编辑 jt-manage ItemCatService 和ItemCat ServiceImpl
package com.jt.service;
public interface ItemCatService {
ItemCat findItemCatById(Long itemCatId);
}
package com.jt.service;
@Service
public class ItemCatServiceImpl implements ItemCatService {
@Autowired
private ItemCatMapper itemCatMapper;
@Override
public ItemCat findItemCatById(Long itemCatId) {
return itemCatMapper.selectById(itemCatId);
}
}
1.8.6ajax嵌套问题
说明:当ajax进行嵌套时,一般将内部的ajax的请求方式设置为同步.
1.8.7页面效果展现
1京淘项目后台维护
1.1编辑jt-manage vo对象
package com.jt.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
//[{id:"编号",text:"文本信 息",state:"open/closed"}]
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class EasyUITree {
private Long id; //节点ID
private String text; //文本信息
private String state; //open/closed
}
1.2…1商品分类页面TreeAjax请求
1.2.2编辑 jt- manage ItemCatController
/**
* 获取商品分类列表信息
* url:/item/cat/list
* 返回值: EasyUITree
*
* @RequestParam 获取参数实现数据的转化
*/
@RequestMapping("/list")
public List<EasyUITree> findItemCatByParentId
(@RequestParam(value = "id",defaultValue = "0")
Long parentId){
return itemCatService.findItemCatByParentId(parentId);
}
1.2.3编辑 jt-manage ItemCatService
/**
* 1.根据parentId查询数据库记录
* 2.循环遍历数据,之后封装EasyUITree的list集合
*/
@Override
public List<EasyUITree> findItemCatByParentId(Long parentId) {
//1.查询数据
List<ItemCat> itemCatList =
findItemCatListByParentId(parentId);
//2.实现数据封装
List<EasyUITree> treeList =
new ArrayList<EasyUITree>(itemCatList.size());
for (ItemCat itemCat : itemCatList) {
Long id = itemCat.getId();
String text = itemCat.getName();
//如果是父级 closed,否则 表示3级标题 open
String state = itemCat.getIsParent()?"closed":"open";
EasyUITree tree = new EasyUITree(id, text, state);
treeList.add(tree);
}
return treeList;
}
private List<ItemCat> findItemCatListByParentId(Long parentId) {
QueryWrapper<ItemCat> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id", parentId);
List<ItemCat> itemCatList =
itemCatMapper.selectList(queryWrapper);
return itemCatList;
}
1.2.3页面效果
1.3商品新增
1.3.1 编辑 jt-common封装SysResult vo 对象
package com.jt.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 该类是系统级VO对象.
* @author Administrator
*
*/
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class SysResult {
private Integer status; //状态码 200成功 201失败
private String msg; //提示信息
private Object data; //返回数据
/**
* 1.只是告知用户 执行成功
* @return
*/
public static SysResult success() {
return new SysResult(200, null, null);
}
/**
* 2.成功之后返回数据data String=name
*/
public static SysResult success(Object data) {
return new SysResult(200, null, data);
}
/**
* 3.成功之后返回msg
*/
public static SysResult success(String msg,Object data) {
return new SysResult(200, msg, data);
}
//失败之后调用
public static SysResult fail(){
return new SysResult(201,"业务执行失败", null);
}
}
1.3.2编辑 jt-manage ItemController
/**
* 实现商品新增
*/
@RequestMapping("/save")
public SysResult saveItem(Item item) {
try {
itemService.saveItem(item);
return SysResult.success();
} catch (Exception e) {
return SysResult.fail();
}
}
1.3.3编辑 jt-manage ItemService
@Override
public void saveItem(Item item) {
item.setStatus(1) //表示正常状态
.setCreated(new Date())
.setUpdated(item.getCreated());
itemMapper.insert(item);
}
1.3.4全局异常处理机制
说明:在jt-common中添加SysExecution类.实现全局异常处理.
//返回数据为JSON
@RestControllerAdvice //异常通知 对Controller层生效
@Slf4j //记录日志
public class SysExecution {
//当系统中出现运行时异常时生效
@ExceptionHandler(RuntimeException.class)
public SysResult error(Exception exception) {
exception.printStackTrace();
log.error(exception.getMessage());
return SysResult.fail();
}
}
2.重构ItemController
/**
* 实现商品新增
*/
@RequestMapping("/save")
public SysResult saveItem(Item item) {
itemService.saveItem(item);
return SysResult.success();
}
1.4商品更新
1.4.1页面跳转过程
text:'编辑',
iconCls:'icon-edit',
handler:function(){
//获取用户选中的数据
var ids = getSelectionsIds();
if(ids.length == 0){
$.messager.alert('提示','必须选择一个商品才能编辑!');
return ;
}
if(ids.indexOf(',') > 0){
$.messager.alert('提示','只能选择一个商品!');
return ;
}
1.5.1编辑 jt-manage ItemController
/**
* 实现商品更新
*/
@RequestMapping("/update")
public SysResult updateItem(Item item) {
itemService.updateItem(item);
return SysResult.success();
}
1.5.2编辑jt-manage ItemService 和ItemServiceImpl
@Override
public void updateItem(Item item) {
item.setUpdated(new Date());
itemMapper.updateById(item);
//所有数据都更新.
}
1.6.1编辑ItemController
/**
* 商品下架
* url:/item/instock
* type:post
* params:{ids:111,222}
*/
@RequestMapping("instock")
public SysResult instockItem(Long[] ids) {
int status = 2; //表示下架
itemService.updateStatus(ids,status);
return SysResult.success();
}
1.6.2编辑ItemService
/**
* 任务:将ids中所有的数据的状态status改为2
*/
@Override
public void updateStatus(Long[] ids, Integer status) {
//1.小白级别
/*
* for (Long id : ids) {
* Item item = new Item();
* item.setId(id)
* .setStatus(status) .setUpdated(new Date());
* itemMapper.updateById(item);
* }
*/
//2.菜鸟级别 sql
Item item = new Item();
item.setStatus(status).setUpdated(new Date());
UpdateWrapper<Item> updateWrapper = new UpdateWrapper<Item>();
List idList = Arrays.asList(ids);
updateWrapper.in("id", idList);
itemMapper.update(item, updateWrapper);
}
1商品后台管理
1.1商品删除
1.1.1页面分析
1.1.2编辑ItemController
@RequestMapping("delete")
public SysResult deleteItem(Long[] ids) {
itemService.deleteItem(ids);
return SysResult.success();
}
1.1.3编辑ItemController
@Override
public void deleteItem(Long[] ids) {
List idList = Arrays.asList(ids);
itemMapper.deleteBatchIds(idList);
}
1.2.1编辑 jt-common pojo
package com.jt.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
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;
@Data
@Accessors(chain = true)
@TableName("tb_item_desc")
public class ItemDesc extends BasePojo{
@TableId //只标识主键
private Long itemId; //必须与item id相同
private String itemDesc;
}
1.2.4编辑ItemController
/**
* 实现商品新增
*/
@RequestMapping("/save")
public SysResult saveItem(Item item,ItemDesc itemDesc) {
itemService.saveItem(item,itemDesc);
return SysResult.success();
}
1.2.5编辑ItemService
/**
* 实现2张表同时入库
*
*/
@Override
@Transactional //事务控制
public void saveItem(Item item,ItemDesc itemDesc) {
item.setStatus(1) //表示正常状态
.setCreated(new Date())
.setUpdated(item.getCreated());
itemMapper.insert(item);
//利用Mybatis-plus入库之后,会自动的将主键ID进行回显
itemDesc.setItemId(item.getId())
.setCreated(item.getCreated())
.setUpdated(item.getCreated());
itemDescMapper.insert(itemDesc);
}
1.3商品详情数据回显
1.3.1页面分析
1.url分析
2.页面js分析
// 加载商品描述
//_data = SysResult.ok(itemDesc)
$.getJSON('/item/query/item/desc/'+data.id,function(_data){
if(_data.status == 200){
//UM.getEditor('itemeEditDescEditor').setContent(_data.data.itemDesc, false);
itemEditEditor.html(_data.data.itemDesc);
}
});
1.3.2编辑ItemController
说明:根据ItemId查询商品详情信息.
/**
* 根据Id查询商品详情信息
*/
@RequestMapping("/query/item/desc/{itemId}")
public SysResult findItemDescById
(@PathVariable Long itemId) {
ItemDesc desc = itemService.findItemDescById(itemId);
return SysResult.success(desc);
//{status:200,msg:'',data:{itemId:"1123123",itemDesc:"html代码"}}
}
1.3.3编辑Item Service
@Override
public ItemDesc findItemDescById(Long itemId) {
return itemDescMapper.selectById(itemId);
}
1.3.4 页面效果
1.4商品详情更新
1.4.1页面url
1.4.2编辑ItemController
/**
* 实现商品更新
*/
@RequestMapping("/update")
public SysResult updateItem(Item item,ItemDesc itemDesc) {
itemService.updateItem(item,itemDesc);
return SysResult.success();
}
1.4.3编辑ItemService
@Override
@Transactional //控制事物
public void updateItem(Item item,ItemDesc itemDesc) {
item.setUpdated(new Date());
itemMapper.updateById(item);
//所有数据都更新.
itemDesc.setItemId(item.getId())
.setUpdated(item.getUpdated());
itemDescMapper.updateById(itemDesc);
}
1.5 商品删除
1.5.1页面分析
$.messager.confirm('确认','确定删除ID为 '+ids+' 的商品吗?',function(r){
if (r){
//100,101,102,103
var params = {"ids":ids};
$.post("/item/delete",params, function(data){
if(data.status == 200){
$.messager.alert('提示','删除商品成功!',undefined,function(){
$("#itemList").datagrid("reload");
});
}else{
$.messager.alert("提示",data.msg);
}
});
}
});
1.5.2编辑ItemController
/**
* url: /item/delete
* 参数: {"ids":ids} 100,101,102
* 返回值: SysResult
* @return
*/
@RequestMapping("/delete")
public SysResult deleteItems(Long[] ids) {
itemService.deleteItems(ids);
return SysResult.success();
}
1.5.3编辑ItemService
@Override
@Transactional
public void deleteItems(Long[] ids) {
List<Long> idList = Arrays.asList(ids);
itemMapper.deleteBatchIds(idList);
itemDescMapper.deleteBatchIds(idList);
}
京淘后台管理
1.1图片上传
1.1.1入门案例
<body>
<h1>实现文件长传</h1>
<!--enctype="开启多媒体标签" -->
<form action="http://localhost:8091/file" method="post"
enctype="multipart/form-data">
<input name="fileImage" type="file" />
<input type="submit" value="提交"/>
</form>
</body>
1.1.2编辑FileController
@RestController
public class FileController {
/**
* 要求: 实现图片上传,返回上传成功信息
* 规范: 参数名称必须与页面name属性一致
* 步骤:
* 1.确定文件上传的路径.
* 2.校验文件路径是否正确.
* 3.实现文件上传
* @throws IOException
* @throws IllegalStateException
*/
@RequestMapping("/file")
public String file(MultipartFile fileImage) throws IllegalStateException, IOException {
String fileDir = "D:/1_JT/images";
File file = new File(fileDir);
if(!file.exists()) {
//如果文件不存在,则新建文件
file.mkdirs();
}
String name = fileImage.getName();
System.out.println("name:"+name);
String fileName = fileImage.getOriginalFilename();
//D:images/a.jpg
String realPath = "D:/1_JT/images/"+fileName;
//实现文件上传
fileImage.transferTo(new File(realPath));
return "文件上传成功!!!!";
}
}
1.2复文本编辑器实现文件上传
1.2.1图片上传回显数据要求
{"error":0,"url":"图片的保存路径","width":图片的宽度,"height":图片的高度}
参数说明: 0代表是一张图片,如果是0,前台才可以解析并显示。1代表不是图片,
不显示如果不设置宽度和高度,则默认用图片原来的大小,所以不用设置
1.2.2封装Vo对象
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ImageVO {
private Integer error; //0正常 1失败
private String url; //图片虚拟路径
private Integer width; //宽度
private Integer height; //高度
public static ImageVO fail() {
return new ImageVO(1, null, null, null);
}
public static ImageVO success(String url,Integer width,Integer height) {
return new ImageVO(0, url, width, height);
}
}
1.2.3文件上传页面分析
1.url
页面js:
//编辑器参数
kingEditorParams : {
filePostName : "uploadfile",
uploadJson : '/pic/up;oad',
dir : "image"
},
1.2.4正则表达式
正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。
许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开的。正则表达式通常缩写成“regex”,单数有regexp、regex,复数有regexps、regexes、regexen。
1.2.5正则语法
元字符 描述
\ 将下一个字符标记符、或一个向后引用、或一个八进制转义符。例如,“\\n”匹配\n。“\n”匹配换行符。序列“\\”匹配“\”而“\(”则匹配“(”。即相当于多种编 程语言中都有的“转义字符”的概念。
^ 匹配输入字行首。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。
$ 匹配输入行尾。如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置。
* 匹配前面的子表达式任意次。例如,zo*能匹配“z”,也能匹配“zo”以及“zoo”。*等价于{0,}。
+ 匹配前面的子表达式一次或多次(大于等于1次)。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。
? 匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“do”或“does”。?等价于{0,1}。
{n} n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。
{n,} n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。
{n,m} m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o为一组,后三个o为一组。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。
? 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,}, {n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少地匹配所搜索的字符串,而默认的贪婪模式则尽可能多地匹配所搜索的字符串。例如,对于字符串“oooo”,“o+”将尽可能多地匹配“o”,得到结果[“oooo”],而“o+?”将尽可能少地匹配“o”,得到结果 ['o', 'o', 'o', 'o']
.点 匹配除“\n”和"\r"之外的任何单个字符。要匹配包括“\n”和"\r"在内的任何字符,请使用像“[\s\S]”的模式。
(pattern) 匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“\(”或“\)”。
(?:pattern) 非获取匹配,匹配pattern但不获取匹配结果,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分时很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。
(?=pattern) 非获取匹配,正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern) 非获取匹配,正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。
(?<=pattern) 非获取匹配,反向肯定预查,与正向肯定预查类似,只是方向相反。例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。
*python的正则表达式没有完全按照正则表达式规范实现,所以一些高级特性建议使用其他语言如java、scala等
(?<!patte_n) 非获取匹配,反向否定预查,与正向否定预查类似,只是方向相反。例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。
*python的正则表达式没有完全按照正则表达式规范实现,所以一些高级特性建议使用其他语言如java、scala等
x|y 匹配x或y。例如,“z|food”能匹配“z”或“food”(此处请谨慎)。“[z|f]ood”则匹配“zood”或“food”。
[xyz] 字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。
[^xyz] 负值字符集合。匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“plin”任一字符。
[a-z] 字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。
注意:只有连字符在字符组内部时,并且出现在两个字符之间时,才能表示字符的范围; 如果出字符组的开头,则只能表示连字符本身.
[^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符。
\b 匹配一个单词的边界,也就是指单词和空格间的位置(即正则表达式的“匹配”有两种概念,一种是匹配字符,一种是匹配位置,这里的\b就是匹配位置的)。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”;“\b1_”可以匹配“1_23”中的“1_”,但不能匹配“21_3”中的“1_”。
\B 匹配非单词边界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。
\cx 匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的“c”字符。
\d 匹配一个数字字符。等价于[0-9]。grep 要加上-P,perl正则支持
\D 匹配一个非数字字符。等价于[^0-9]。grep要加上-P,perl正则支持
\f 匹配一个换页符。等价于\x0c和\cL。
\n 匹配一个换行符。等价于\x0a和\cJ。
\r 匹配一个回车符。等价于\x0d和\cM。
\s 匹配任何不可见字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。
\S 匹配任何可见字符。等价于[^ \f\n\r\t\v]。
\t 匹配一个制表符。等价于\x09和\cI。
\v 匹配一个垂直制表符。等价于\x0b和\cK。
\w 匹配包括下划线的任何单词字符。类似但不等价于“[A-Za-z0-9_]”,这里的"单词"字符使用Unicode字符集。
\W 匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。
\xn 匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,“\x41”匹配“A”。“\x041”则等价于“\x04&1”。正则表达式中可以使用ASCII编码。
\num 匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,“(.)\1”匹配两个连续的相同字符。
\n 标识一个八进制转义值或一个向后引用。如果\n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n为一个八进制转义值。
\nm 标识一个八进制转义值或一个向后引用。如果\nm之前至少有nm个获得子表达式,则nm为向后引用。如果\nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若n和m均为八进制数字(0-7),则\nm将匹配八进制转义值nm。
\nml 如果n为八进制数字(0-7),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。
\un 匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配版权符号(©)。
\p{P} 小写 p 是 property 的意思,表示 Unicode 属性,用于 Unicode 正表达式的前缀。中括号内的“P”表示Unicode 字符集七个字符属性之一:标点字符。
其他六个属性:
L:字母;
M:标记符号(一般不会单独出现);
Z:分隔符(比如空格、换行等);
S:符号(比如数学符号、货币符号等);
N:数字(比如阿拉伯数字、罗马数字等);
C:其他字符。
*注:此语法部分语言不支持,例:javascript。
\<
\> 匹配词(word)的开始(\<)和结束(\>)。例如正则表达式\<the\>能够匹配字符串"for the wise"中的"the",但是不能匹配字符串"otherwise"中的"the"。注意:这个元字符不是所有的软件都支持的。
( ) 将( 和 ) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1 到\9 的符号来引用。
| 将两个匹配条件进行逻辑“或”(or)运算。例如正则表达式(him|her) 匹配"it belongs to him"和"it belongs to her",但是不能匹配"it belongs to them."。注意:这个元字符不是所有的软件都支持的。
1.2.6编辑FileController
@Autowired
private FileService fileService;
@RequestMapping("/pic/upload")
public ImageVO uploadFile(MultipartFile uploadFile) {
return fileService.uploadFile(uploadFile);
}
1.2.7编辑FileService
@Service
public class FileServiceImpl implements FileService {
private String localDirPath = "D:/1_JT/images/";
/**
* 步骤:
* 1.准备文件上传根目录 D:\1_JT\images
* 2.校验文件类型. jpg|png|gif......
* 3.为了检索快速,需要分目录存储 1.按照类型 2.按照商品类目 3.按照时间
* 4.文件名称不能重复 UUID
*/
@Override
public ImageVO uploadFile(MultipartFile uploadFile) {
ImageVO imageVO = null;
//1.现获取文件名称 abc.jpg ABC.JPG
String fileName = uploadFile.getOriginalFilename();
//2.校验是否为图片类型 字符串的匹配规则 正则表达式
fileName = fileName.toLowerCase(); //将字符转化为小写
if(!fileName.matches("^.+\\.(jpg|png|gif)$")) {
//说明不是图片
return ImageVO.fail();
}
//3.校验程序是否为恶意程序. 检查图片是否有宽 高
try {
BufferedImage bufferedImage =
ImageIO.read(uploadFile.getInputStream());
int height = bufferedImage.getHeight();
int width = bufferedImage.getWidth();
if(height==0 || width == 0) {
return ImageVO.fail();
}
//4.实现分目录保存. date转为字符串 yyyy/MM/dd
String datePath = new SimpleDateFormat("yyyy/MM/dd/")
.format(new Date());
String fileLocalPath = localDirPath + datePath;
File fileDir = new File(fileLocalPath);
if(!fileDir.exists()) {
fileDir.mkdirs();
}
//5.准备文件名称 uuid.type
String uuid = UUID.randomUUID().toString();
int index = fileName.lastIndexOf(".");
String type = fileName.substring(index); //.jpg
String uuidName = uuid + type;
//6.实现文件上传. D:\1_JT\images\2020\01\03\ uuid.jpg
String realFilePath = fileLocalPath + uuidName;
uploadFile.transferTo(new File(realFilePath));
String url = "https://img14.360buyimg.com/n0/jfs/t1/56892/29/14466/153329/5db7d937Ef56a8fd2/c6d77f97bbd7e155.jpg";
imageVO = ImageVO.success(url, width, height);
} catch (IOException e) {
e.printStackTrace();
return ImageVO.fail();
}
return imageVO;
}
}
1.2.8页面效果
1.3 文件上传优化
1.3.1利用properties 实现数据动态注入
1.3.1.1编辑properties文件
#配置图片路径信息
image.localDirPath=D:/1_JT/images/
1.3.1.2动态注入数据
1.3.2实现图片真实回显
url网络虚拟路径:
http://mange.jt.com/2020/01/03/a.jpg
本地磁盘路径:
D:\1_JT\images/2020/01/03/a.jpg
说明:应该准备一个网络的虚拟路径为用户提供访问的链接.但是要求网络链接地址能够找到真实图片信息.
1.3.2.1编辑properties文件
#配置图片路径信息
image.localDirPath=D:/1_JT/images/
image.urlPath=http://image.jt.com/
1.3.2.2编辑FileServiceImpl
说明:实现图片虚拟地址注入,并且拼接URL地址,形成真实的本地地址.
@Service
@PropertySource("classpath:/properties/image.properties")
public class FileServiceImpl implements FileService {
@Value("${image.localDirPath}")
private String localDirPath; // = "D:/1_JT/images/";
@Value("${image.urlPath}") //
private String urlPath;
/**
* 步骤:
* 1.准备文件上传根目录 D:\1_JT\images
* 2.校验文件类型. jpg|png|gif......
* 3.为了检索快速,需要分目录存储 1.按照类型 2.按照商品类目 3.按照时间
* 4.文件名称不能重复 UUID
*/
@Override
public ImageVO uploadFile(MultipartFile uploadFile) {
ImageVO imageVO = null;
//1.现获取文件名称 abc.jpg ABC.JPG
String fileName = uploadFile.getOriginalFilename();
//2.校验是否为图片类型 字符串的匹配规则 正则表达式
fileName = fileName.toLowerCase(); //将字符转化为小写
if(!fileName.matches("^.+\\.(jpg|png|gif)$")) {
//说明不是图片
return ImageVO.fail();
}
//3.校验程序是否为恶意程序. 检查图片是否有宽 高
try {
BufferedImage bufferedImage =
ImageIO.read(uploadFile.getInputStream());
int height = bufferedImage.getHeight();
int width = bufferedImage.getWidth();
if(height==0 || width == 0) {
return ImageVO.fail();
}
//4.实现分目录保存. date转为字符串 yyyy/MM/dd
String datePath = new SimpleDateFormat("yyyy/MM/dd/")
.format(new Date());
String fileLocalPath = localDirPath + datePath;
File fileDir = new File(fileLocalPath);
if(!fileDir.exists()) {
fileDir.mkdirs();
}
//5.准备文件名称 uuid.type
String uuid = UUID.randomUUID().toString();
int index = fileName.lastIndexOf(".");
String type = fileName.substring(index); //.jpg
String uuidName = uuid + type;
//6.实现文件上传. D:\1_JT\images\2020\01\03\ uuid.jpg
String realFilePath = fileLocalPath + uuidName;
uploadFile.transferTo(new File(realFilePath));
//http://image.jt.com/yyyy/MM/dd/
String url = urlPath + datePath + uuidName;
imageVO = ImageVO.success(url, width, height);
} catch (IOException e) {
e.printStackTrace();
return ImageVO.fail();
}
return imageVO;
}
}