店铺注册之前端设计
使用sui mobile
看demo
选择表单之后复制网页源码,在webapp下创建一个index.html页面然后复制进去。然后把css和js的资源替换掉dns。
然后将表单改成商店信息,修改其他类似这样
我们不希望用户通过index.html访问我们的页面需要定义好的路由规则访问,
这样就不能访问了,只能通过路由,在controller做转发,这里返回为什么
不用WEB-INF和.html呢原因是我们在spring-web.xml已经配置好了,
验证成功之后重回写controller
package com.imooc.o2o.web.shopadmin;
@Controller
@RequestMapping("/shopadmin")
public class ShopManagementController {
@Autowired
private ShopService shopService;
@ResponseBody
@RequestMapping(value = "/registershop", method = RequestMethod.GET)
private Map registerShop(HttpServletRequest request) {
// 返回值
Map modelMap = new HashMap();
// 1、接收前端传过来的Shop字符串信息,包括店铺信息以及图片信息,并转换为shop实体类
// shopStr是跟前端约定好的key值
String shopStr = HttpServletRequestUtil.getString(request, "shopStr");
// 接收完成后需要做转换
ObjectMapper mapper = new ObjectMapper();
// 定义一个shop实体类去接收
Shop shop = null;
// 开始转换
try {
shop = mapper.readValue(shopStr, Shop.class);
} catch (Exception e) {
// 转换失败就返回modelMap,告诉前台说转换失败
modelMap.put("success", false);
modelMap.put("errMsg", e.getMessage());
return modelMap;
}
// 接收图片
CommonsMultipartFile shopImg = null;
// 文件上传解析器,去解析request里面的文件信息
// 从request本次会话的上下文中去获取相关文件上传的内容
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(
request.getSession().getServletContext());
// 如果有上传的文件流
//需要将request转换MultipartHttpServletRequest,这个对象就能够提取相对应的文件流
//shopImg与前端约定
if (commonsMultipartResolver.isMultipart(request)) {
MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest)request;
shopImg = (CommonsMultipartFile) multipartHttpServletRequest.getFile("shopImg");
}else {
modelMap.put("success", false);
modelMap.put("errMsg", "上传图片不能为空");
return modelMap;
}
// 2、注册店铺
if(shop != null && shopImg != null) {
//假定前端传过来的值是不可靠所以越少依赖前端的值越好
//就拿店铺的实体类来说,onwer的信息可以从session获取而不用去前端获取
PersonInfo owner = new PersonInfo();
owner.setUserId(1L);
shop.setOwner(owner);
File shopImgFile = new File(PathUtil.getImgBasePath() + ImageUtil.getRandomFileName());
try {
shopImgFile.createNewFile();
}catch(IOException e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.getMessage());
return modelMap;
}
try {
inputStreamToFile(shopImg.getInputStream(), shopImgFile);
}catch(IOException e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.getMessage());
return modelMap;
}
ShopExecution se = shopService.addShop(shop, shopImgFile);
if(se.getState()==ShopStateEnum.CHECK.getState()) {
modelMap.put("success", true);
}else {
modelMap.put("success", false);
modelMap.put("errMsg", se.getStateInfo());
}
return modelMap;
}else {
modelMap.put("success", false);
modelMap.put("errMsg", "店铺信息不能为空");
return modelMap;
}
}
//因为CommonsMultipartFile不能强制转换为File,但是它有InputStream方法
//所以用InputStream来转换
private static void inputStreamToFile(InputStream ins, File file) {
FileOutputStream os = null;
try {
os = new FileOutputStream(file);
int bytesRead = 0;
byte[] buffer = new byte[1024];
while((bytesRead=ins.read(buffer))!=-1) {
os.write(buffer, 0, bytesRead);
}
}catch(Exception e){
throw new RuntimeException("调用inputStreamToFile产生异常" + e.getMessage());
}finally{
try {
if(os!=null) {
os.close();
}
if(ins!=null) {
ins.close();
}
}catch(IOException e) {
throw new RuntimeException("关闭inputStreamToFile产生异常" + e.getMessage());
}
}
}
}
编写js内容
js内容主要就是操作html页面的,主要包括获取初始的信息,第一步主要是获取商铺分类,所属区域的列表信息,第二步是当我们提交的时候将表单里的内容全部获取到,然后通过Ajax给转发到后台,所有的动作都是异步的,这样就不影响用户的体验。
/**
* 第一功能:从后台获取店铺分类,以及区域等信息将它填充至前台的html控件里面 第二功能:将表单的全部信息获取到转发到后台去注册店铺
*/
$(function(){
// 初始化url,获取店铺的分类和区域信息
var initUrl = '/o2o/shopadmin/getshopinitinfo';
// 注册店铺的url
var registerShopUrl = '/o2o/shopadmin/registershop';
alert(initUrl);
getShopInitInfo();
// 获取店铺的基本信息
function getShopInitInfo(){
$.getJSON(initUrl,function(data){
if(data.success){
var tempHtml = '';
var tempAreaHtml = '';
data.shopCategoryList.map(function(item, index){
tempHtml += '';
});
data.areaList.map(function(item, index){
tempAreaHtml += '';
});
$('#shop-category').html(tempHtml);
$('#area').html(tempAreaHtml);
}
});
//实现第二功能
$('#submit').click(function(){
var shop = {};
shop.shopName = $('#shop-name').val();
shop.shopAddr = $('#shop-addr').val();
shop.phone = $('shop-phone').val();
shop.shopDesc = $('shop-desc').val();
shop.shopCategory = {
shopCategoryId:$('#shop-category').find('option').not(function(){
return !this.selected;
}).data('id')
};
shop.area = {
areaId:$('#area').find('option').not(function(){
return !this.selected;
}).data('id')
};
var shopImg = $('#shop-img')[0].files[0];
var formData = new FormData();
formData.append('shopImg', shopImg);
formData.append('shopStr', JSON.stringify(shop));
$.ajax({
url:registerShopUrl,
type:'POST',
data:formData,
contentType:false,
proceesData:false,
cache:false,
success:function(data){
if(data.success){
$.toast("提交成功!");
}else{
$.toast("提交失败!" + data.errMsg);
}
}
})
})
}
})
现在就来实现shopinitinfo方法,这个方法主要是返回区域,店铺类别相关的信息,之前我们已经实现了获取区域列表,现在从dao层开始实现店铺类别。
创建相关mapper
注意from不是form还有最后一个字段parent_id不用逗号。
测试:
public class ShopCategoryDaoTest extends BaseTest {
@Autowired
private ShopCategoryDao shopCategoryDao;
@Test
public void testQueryShopCategory() {
List shopCategoryList = shopCategoryDao.queryShopCategory(new ShopCategory());
assertEquals(2, shopCategoryList.size());
//测试当查找店铺类别传入参数时
ShopCategory testCategory = new ShopCategory();
ShopCategory parentCategory = new ShopCategory();
parentCategory.setShopCategoryId(1L);
testCategory.setParent(parentCategory);
shopCategoryList = shopCategoryDao.queryShopCategory(testCategory);
assertEquals(1, shopCategoryList.size());
}
}
service层
@Service
public class ShopCategoryServiceImpl implements ShopCategoryService {
@Autowired
ShopCategoryDao shopCategoryDao;
@Override
public List getShopCategoryList(ShopCategory shopCategoryCondition) {
return shopCategoryDao.queryShopCategory(shopCategoryCondition);
}
}
返回去写controller层的实现getShopInitInfo这个方法
很多controller都是这样
首先,写一个modelMap对象
然后就一个list来接收相关信息
然后就trycatch调用service的方法赋给上面的list,如果成功就把两个list放入moelMap,如果失败就把失败信息放入modelMap,最后在放回modelMap
package com.imooc.o2o.web.shopadmin;
@Controller
@RequestMapping("/shopadmin")
public class ShopManagementController {
@Autowired
private ShopService shopService;
@Autowired
private ShopCategoryService shopCategoryService;
@Autowired
private AreaService areaService;
/**
* 获取区域以及店铺类别的信息将它返回给前台
* @param request
* @return
*/
@ResponseBody
@RequestMapping(value="/getshopinitinfo", method=RequestMethod.GET)
private Map getInitInfo(HttpServletRequest request){
Map modelMap = new HashMap();
List shopCategoryList = new ArrayList();
List areaList = new ArrayList();
try {
shopCategoryList = shopCategoryService.getShopCategoryList(new ShopCategory());
areaList = areaService.getAreaList();
modelMap.put("shopCategoryList", shopCategoryList);
modelMap.put("areaList", areaList);
modelMap.put("success", true);
}catch(Exception e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.getMessage());
}
return modelMap;
}
/**
* 获取前台表单信息并且转换为Shop实体类
* @param request
* @return
*/
@ResponseBody
@RequestMapping(value = "/registershop", method = RequestMethod.POST)
private Map registerShop(HttpServletRequest request) {
// 返回值
Map modelMap = new HashMap();
// 1、接收前端传过来的Shop字符串信息,包括店铺信息以及图片信息,并转换为shop实体类
// shopStr是跟前端约定好的key值
String shopStr = HttpServletRequestUtil.getString(request, "shopStr");
// 接收完成后需要做转换
ObjectMapper mapper = new ObjectMapper();
// 定义一个shop实体类去接收
Shop shop = null;
// 开始转换
try {
shop = mapper.readValue(shopStr, Shop.class);
} catch (Exception e) {
// 转换失败就返回modelMap,告诉前台说转换失败
modelMap.put("success", false);
modelMap.put("errMsg", e.getMessage());
return modelMap;
}
// 接收图片
CommonsMultipartFile shopImg = null;
// 文件上传解析器,去解析request里面的文件信息
// 从request本次会话的上下文中去获取相关文件上传的内容
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(
request.getSession().getServletContext());
// 如果有上传的文件流
//需要将request转换MultipartHttpServletRequest,这个对象就能够提取相对应的文件流
//shopImg与前端约定
if (commonsMultipartResolver.isMultipart(request)) {
MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest)request;
shopImg = (CommonsMultipartFile) multipartHttpServletRequest.getFile("shopImg");
}else {
modelMap.put("success", false);
modelMap.put("errMsg", "上传图片不能为空");
return modelMap;
}
// 2、注册店铺
if(shop != null && shopImg != null) {
//假定前端传过来的值是不可靠所以越少依赖前端的值越好
//就拿店铺的实体类来说,onwer的信息可以从session获取而不用去前端获取
PersonInfo owner = new PersonInfo();
owner.setUserId(1L);
shop.setOwner(owner);
File shopImgFile = new File(PathUtil.getImgBasePath() + ImageUtil.getRandomFileName());
try {
shopImgFile.createNewFile();
}catch(IOException e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.getMessage());
return modelMap;
}
try {
inputStreamToFile(shopImg.getInputStream(), shopImgFile);
}catch(IOException e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.getMessage());
return modelMap;
}
ShopExecution se = shopService.addShop(shop, shopImgFile);
if(se.getState()==ShopStateEnum.CHECK.getState()) {
modelMap.put("success", true);
}else {
modelMap.put("success", false);
modelMap.put("errMsg", se.getStateInfo());
}
return modelMap;
}else {
modelMap.put("success", false);
modelMap.put("errMsg", "店铺信息不能为空");
return modelMap;
}
}
//因为CommonsMultipartFile不能强制转换为File,但是它有InputStream方法
//所以用InputStream来转换
private static void inputStreamToFile(InputStream ins, File file) {
FileOutputStream os = null;
try {
os = new FileOutputStream(file);
int bytesRead = 0;
byte[] buffer = new byte[1024];
while((bytesRead=ins.read(buffer))!=-1) {
os.write(buffer, 0, bytesRead);
}
}catch(Exception e){
throw new RuntimeException("调用inputStreamToFile产生异常" + e.getMessage());
}finally{
try {
if(os!=null) {
os.close();
}
if(ins!=null) {
ins.close();
}
}catch(IOException e) {
throw new RuntimeException("关闭inputStreamToFile产生异常" + e.getMessage());
}
}
}
}
首先店铺是存储在二级id里面的,而不是首页的一级id,下面都是一级id
二级id
可以看到旧书籍交易信息店铺是在二级id二手书籍店铺类别下的
也就是说店铺所属的shopCategory的parentid是非空的,因为需要在
需要把new ShopCategory换成pardentid不为空的类别供店铺来选择
要更改的是mapper文件,要先判断传进来的参数是否为空,如果不为空那么它的parent_id也不能为空,如果为空就选出所有的类别来。
更写更合理的controller层
如果使用inputStringtofile转换为file,这样每次都得创建空文件,并且如果失败会有异常不稳定。所以需要修改,把inputStringtofile注释掉。并且把上面注册店铺包产该方法去掉。
就能减少Comm转换为file所带来的风险并且减少代码
验证码
引入jar包
引入jar包之后就需要在web.xml去编写它的一个servlet,这个servlet就负责去生成验证码
Kaptcha
com.google.core.Kaptcha.servlet.KaptchaServlet
kaptcha.border
no
kaptcha.testproducer.font.color
no
kaptcha.image.width
135
kaptcha.testproducer.char.string
ABCDEFGKPRSTWX345689
kaptcha.image.height
50
kaptcha.testproducer.font.size
43
kaptcha.noise.color
black
kaptcha.textproducer.char.length
4
kaptcha.textproducer.font.names
Arial
使用验证码
验证码
创建changeVerifyCode方法
在shopoperation.js接收验证码并且传进后台里面去。
将编写好的common.js引进html中
编写servletMapping
Kaptcha
/Kaptcha
把web.xml里的test改成text,然后启动服务器,验证码生成了,但是是黑色的。我们指定是红色的,发现text写错test了
然后希望不管提交成功还是失败反正提交之后就得刷新一次验证码,所以需要在shopoperation.js最后加上
前端编写完之后去编写后端,后端就是接收验证码去验证验证码,输入的验证码是否与图片验证码一样的。这里我们只验证大写,因为我们之前在web.xml里设置都是大写和数字,我们也需要输入大写,小写是会不成功匹配的,我们也可以在这个chek方法里加个判断小写条件就行了。
之后在controller层调用这个方法判断
总结:
验证码的使用:
1、首先导入jar包
2、然后在web.xml定义servlet,这个servlet是为katcha做服务的,包括指定了样式,例如字体等等,同时设置servelt-mapping去相应katcha的请求。
3、在shopoperation.html中以html的形式生成验证码,就是编写验证码的控件,然后再验证码控件的图片编写一个onclick方法,这个方法主要去调用验证码的servlet去生成新的验证码。
4、生成之后当提交就在js里获取到,并且通过ajax传入到后台,然后在后端通过controller对验证码进行比对。
前后端验证:
打开tomcat的调试模式:
然后访问我们的页面,打开调试工具之后打开sources里面找到js文件
然后设置断点,然后输入所有字段,按f10进行按行调试判断是否js编写正确以至于获取到值。可以看到我们少写了#,并且ajax里的process写错,发现错误就回到代码去修改之后再刷新页面。
当前端调试完成后,按f8直接跳到后台,我们先在controller里设置断点,然后按f6按行调试,调试到验证验证码的时候就按f5进入,发现图片的验证码我们可以获取得到,但是我们自己输入的验证码却获取不到,原因是缺少处理文件流的mutilpartResolver就是你传入了文件流,我们的后台没有办法识别的到。这个项目的配置不是在web.xml,不然就是在spring-web.xml,在spring-web.xml里配置一个
之后再导入jar包去处理文件上传的类库
之后再debug启动服务器在测试就有了,之后一直调试到最后,它就会去添加,但是我这里还出现一个问题,就是水印的图片得方法resources文件下就好了。
最后可以看到数据库数据的添加,文件里缩略图的生成。
我们的店铺注册大概就做好,为什么说大概呢,因为还有很多没有实现,例如验证电话号码啊非空等等,还有验证码大小写。