taotao 商城 -3

1商品类目选择功能实现

1.1 功能分析


样式绑定了selectItemCat,可以用工具栏上的搜索File Search,patten选择*.js
类目选择js代码如下

    // 初始化选择类目组件
    initItemCat : function(data){
        $(".selectItemCat").each(function(i,e){   //i e分别表示循环的下标与对象
            var _ele = $(e);          //DOM对象转换成jquery对象
            if(data && data.cid){
                _ele.after("<span style='margin-left:10px;'>"+data.cid+"</span>");
            }else{
                _ele.after("<span style='margin-left:10px;'></span>");
            }
            _ele.unbind('click').click(function(){
                $("<div>").css({padding:"5px"}).html("<ul>")  //新建div并转成jquery对象
                .window({                    //打开窗口
                    width:'500',
                    height:"450",
                    modal:true,
                    closed:true,
                    iconCls:'icon-save',
                    title:'选择类目',
                    onOpen : function(){
                        var _win = this;
                        $("ul",_win).tree({      //初始化tree
                            url:'/item/cat/list',  //请求url,响应的数据自动处理
                            animate:true,
                            onClick : function(node){
                                if($(this).tree("isLeaf",node.target)){
                                    // 填写到cid中
                                    _ele.parent().find("[name=cid]").val(node.id);
                                    _ele.next().text(node.text).attr("cid",node.id);
                                    $(_win).window('close');
                                    if(data && data.fun){
                                        data.fun.call(this,node);
                                    }
                                }
                            }
                        });
                    },
                    onClose : function(){
                        $(this).window("destroy");
                    }
                }).window('open');
            });
        });
    },

页面加载后$(function(){})执行方法

    //页面初始化完毕后执行此方法
    $(function(){
        //创建富文本编辑器
        itemAddEditor = TAOTAO.createEditor("#itemAddForm [name=desc]");
        //初始化类目选择和图片上传器
        TAOTAO.init({fun:function(node){
            //根据商品的分类id取商品 的规格模板,生成规格信息。第四天内容。
            //TAOTAO.changeItemParam(node, "itemAddForm");
        }});
    });

初始化tree请求的url:/item/cat/list
参数:初始化tree时只需要把第一级节点展示,子节点异步加载。long id(父节点id)
返回值:json。数据格式如下

[{    
    "id": 1,    
    "text": "Node 1",    
    "state": "closed"
},{    
    "id": 2,    
    "text": "Node 2",    
    "state": "closed"   
}] 

state:如果节点下有子节点“closed”,如果没有子节点“open”
创建一个pojo来描述tree的节点信息,包含三个属性id、text、state。放到taotao-common工程中

public class EasyUITreeNode implements Serializable{

    private long id;
    private String text;
    private String state;
......

查询的表:tb_item_cat
查询列:Id、name、isparent
查询条件:parentId

1.2 Dao层

tb_item_cat可以使用逆向工程生成的代码

1.3 Service层

参数:long parentId
业务逻辑:
1、根据parentId查询节点列表
2、转换成EasyUITreeNode列表。
3、返回。
返回值:List<EasyUITreeNode>
ItemCatServiceImpl添加方法getItemCatList实现业务逻辑

@Service
public class ItemCatServiceImpl implements ItemCatService {

    @Autowired
    private TbItemCatMapper itemCatMapper;
    
    @Override
    public List<EasyUITreeNode> getItemCatList(long parentId) {
        //根据父节点id查询子节点列表
        TbItemCatExample example = new TbItemCatExample();
        //设置查询条件
        Criteria criteria = example.createCriteria();
        //设置parentid
        criteria.andParentIdEqualTo(parentId);
        //执行查询
        List<TbItemCat> list = itemCatMapper.selectByExample(example);
        //转换成EasyUITreeNode列表
        List<EasyUITreeNode> resultList = new ArrayList<>();
        for (TbItemCat tbItemCat : list) {
            EasyUITreeNode node = new EasyUITreeNode();
            node.setId(tbItemCat.getId());
            node.setText(tbItemCat.getName());
            //如果节点下有子节点“closed”,如果没有子节点“open”
            node.setState(tbItemCat.getIsParent()?"closed":"open");
            //添加到节点列表
            resultList.add(node);
        }
        return resultList;
    }
}

发布服务

<dubbo:service interface="com.taotao.service.ItemCatService" ref="itemCatServiceImpl" timeout="300000"/>

1.4 表现层

引用服务

<dubbo:reference interface="com.taotao.service.ItemCatService" id="itemCatService" />

创建Controller
初始化tree请求的url:/item/cat/list
参数:long id(父节点id)
返回值:json。数据格式 List<EasyUITreeNode>

@Controller
public class ItemCatController {
    
    @Autowired
    private ItemCatService itemCatService;

    @RequestMapping("/item/cat/list")
    @ResponseBody
    public List<EasyUITreeNode> getItemCatList(@RequestParam(name="id", defaultValue="0")Long parentId) {
        List<EasyUITreeNode> list = itemCatService.getItemCatList(parentId);
        return list;
    }
}

2 图片上传

2.1 图片服务器使用

服务端两个角色:
Tracker:管理集群,tracker也可以实现集群。每个tracker节点地位平等。收集Storage集群的状态。
Storage:实际保存文件。Storage分为多个组,每个组之间保存的文件是不同的。每个组内部可以有多个成员,组成员内部保存的内容是一样的,组成员的地位是一致的,没有主从的概念。


2.2 文件上传下载

文件上传



客户端上传文件后存储服务器将文件ID返回给客户端,此文件ID用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。
组名:文件上传后所在的storage组名称,在文件上传成功后有storage服务器返回,需要客户端自行保存。
虚拟磁盘路径:storage配置的虚拟路径,与磁盘选项store_path*对应。如果配置了store_path0则是M00,如果配置了store_path1则是M01,以此类推。
数据两级目录:storage服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件。
文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储服务器IP地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息。
文件下载

2.3 图片上传测试

上传文件步骤:
1、加载配置文件,配置文件中的内容就是tracker服务的地址。配置文件内容:tracker_server=192.168.25.175:22122
2、创建一个TrackerClient对象。直接new一个。
3、使用TrackerClient对象创建连接,获得一个TrackerServer对象。
4、创建一个StorageServer的引用,值为null
5、创建一个StorageClient对象,需要两个参数TrackerServer对象、StorageServer的引用
6、使用StorageClient对象上传图片。
7、返回数组。包含组名和图片的路径。
使用工具类上传时,添加fastdfs_client项目

import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
import org.junit.Test;

import com.taotao.utils.FastDFSClient;

public class TestFastDFS {

    @Test
    public void uploadFile() throws Exception {
        //1、向工程中添加jar包
        //2、创建一个配置文件。配置tracker服务器地址
        //3、加载配置文件
        ClientGlobal.init("C:/Java/eclipse_workspace2/taotao-manager-web/src/main/resources/resource/client.conf");
        //4、创建一个TrackerClient对象。
        TrackerClient trackerClient = new TrackerClient();
        //5、使用TrackerClient对象获得trackerserver对象。
        TrackerServer trackerServer = trackerClient.getConnection();
        //6、创建一个StorageServer的引用null就可以。
        StorageServer storageServer = null;
        //7、创建一个StorageClient对象。trackerserver、StorageServer两个参数。
        StorageClient storageClient = new StorageClient(trackerServer, storageServer);
        //8、使用StorageClient对象上传文件。
        String[] strings = storageClient.upload_file("C:/Users/C5228335/Desktop/test.jpg", "jpg", null);
        for (String string : strings) {
            System.out.println(string);
            
        }
    }
    
    @Test
    public void testFastDfsClient() throws Exception {
        FastDFSClient fastDFSClient = new FastDFSClient("C:/Java/eclipse_workspace2/taotao-manager-web/src/main/resources/resource/client.conf");
        String string = fastDFSClient.uploadFile("C:/Users/C5228335/Desktop/test.jpg");
        System.out.println(string);
    }
}

3 图片上传功能实现

3.1 js分析

    // 编辑器参数
    kingEditorParams : {
        //指定上传文件参数名称
        filePostName  : "uploadFile",
        //指定上传文件请求的url。
        uploadJson : '/pic/upload',
        //上传类型,分别为image、flash、media、file
        dir : "image"
    },

  //图片上传功能
  initPicUpload : function(data){
        $(".picFileUpload").each(function(i,e){  //遍历所有对象
            var _ele = $(e);
            _ele.siblings("div.pics").remove();
            _ele.after('\
                <div class="pics">\
                    <ul></ul>\
                </div>');
            //有图片则回显图片
            if(data && data.pics){
                var imgs = data.pics.split(",");
                for(var i in imgs){
                    if($.trim(imgs[i]).length > 0){
                        _ele.siblings(".pics").find("ul").append("<li><a href='"+imgs[i]+"' target='_blank'><img src='"+imgs[i]+"' width='80' height='50' /></a></li>");
                    }
                }
            }
            //给“上传图片按钮”绑定click事件
            $(e).click(function(){
                var form = $(this).parentsUntil("form").parent("form");
                //打开图片上传窗口,富文本编辑器插件,初始化指定上传路径,loadPlugin加载多图片上传插件
                KindEditor.editor(TT.kingEditorParams).loadPlugin('multiimage',function(){
                    var editor = this;
                    editor.plugin.multiImageDialog({
                        clickFn : function(urlList) {  //上传成功后回调
                            var imgArray = [];
                            KindEditor.each(urlList, function(i, data) {
                                imgArray.push(data.url);
                                form.find(".pics ul").append("<li><a href='"+data.url+"' target='_blank'><img src='"+data.url+"' width='80' height='50' /></a></li>");
                            });
                            form.find("[name=image]").val(imgArray.join(","));
                            editor.hideDialog();
                        }
                    });
                });
            });
        });
    },

3.2 功能实现

1 Web项目添加commons-io/fileupload 的jar包,在springmvc.xml中配置多媒体解析器

        <!-- 多媒体解析器 -->
    <!-- 配置文件上传解析器 -->
    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 设定默认编码 -->
        <property name="defaultEncoding" value="UTF-8"></property>
        <!-- 设定文件上传的最大值5MB,5*1024*1024 -->
        <property name="maxUploadSize" value="5242880"></property>
    </bean>

2 创建PictureController,使用@ResponseBody注解返回java对象,将返回的map对象转成string类型

@Controller
public class PictureController {
    
    @Value("${IMAGE_SERVER_URL}")
    private String IMAGE_SERVER_URL;

    @RequestMapping("/pic/upload")
    @ResponseBody
    public String picUpload(MultipartFile uploadFile) {
        try {
            //接收上传的文件
            //取扩展名
            String originalFilename = uploadFile.getOriginalFilename();
            String extName = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
            //上传到图片服务器
            FastDFSClient fastDFSClient = new FastDFSClient("classpath:resource/client.conf");
            String url = fastDFSClient.uploadFile(uploadFile.getBytes(), extName);
            url = IMAGE_SERVER_URL + url;
            //响应上传图片的url
            Map result = new HashMap<>();
            result.put("error", 0);
            result.put("url", url);
            return JsonUtils.objectToJson(result);
        } catch (Exception e) {
            e.printStackTrace();
            Map result = new HashMap<>();
            result.put("error", 1);
            result.put("message", "图片上传失败");
            return JsonUtils.objectToJson(result);
        }
        
    }
}

4 富文本编辑器

常用的富文本编辑器
KindEditor:http://kindeditor.net/
UEditor(百度编辑器): http://ueditor.baidu.com/website/
CKEditor:http://ckeditor.com/
使用方法
在jsp中引入KindEditor的css和js代码。

<script type="text/javascript" charset="utf-8" src="/js/kindeditor-4.1.10/kindeditor-all-min.js"></script>
<script type="text/javascript" charset="utf-8" src="/js/kindeditor-4.1.10/lang/zh_CN.js"></script>

在表单中添加textarea控件

<td>商品描述:</td>
<td>
     <textarea style="width:800px;height:300px;visibility:hidden;" name="desc"></textarea>
</td>

初始化富文本编辑器。使用官方提供的方法初始化

    var itemAddEditor ;
    //页面初始化完毕后执行此方法
    $(function(){
        //创建富文本编辑器
        itemAddEditor = TAOTAO.createEditor("#itemAddForm [name=desc]");
        //初始化类目选择和图片上传器
        TAOTAO.init({fun:function(node){
            //根据商品的分类id取商品 的规格模板,生成规格信息。第四天内容。
            //TAOTAO.changeItemParam(node, "itemAddForm");
        }});
    });

    createEditor : function(select){
        return KindEditor.create(select, TT.kingEditorParams);
    },

取富文本编辑器的内容。表单提交之前,把富文本编辑器的内容同步到textarea控件中。

        //同步文本框中的商品描述
        itemAddEditor.sync();

5 商品添加功能实现

5.1 功能分析

请求的url:/item/save
参数:表单的数据。可以使用pojo接收表单的数据,要求pojo的属性和input的name属性要一致。用TbItem对象接收表单数据,String接收描述。
返回值:Json数据。应该包含一个status的属性。可以使用TaotaoResult,放到taotao-common中。

业务逻辑:
1、生成商品id,使用IDUtils生成商品id
2、补全TbItem对象的属性
3、向商品表插入数据
4、创建一个TbItemDesc对象
5、补全TbItemDesc的属性
6、向商品描述表插入数据
7、TaotaoResult.ok()

5.2 DAO层

向tb_item, tb_item_desc表中插入数据,可以使用逆向工程。
创建taotaoResult类

import java.io.Serializable;
import java.util.List;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 淘淘商城自定义响应结构
 */
public class TaotaoResult implements Serializable{

    // 定义jackson对象
    private static final ObjectMapper MAPPER = new ObjectMapper();

    // 响应业务状态
    private Integer status;

    // 响应消息
    private String msg;

    // 响应中的数据
    private Object data;

    public static TaotaoResult build(Integer status, String msg, Object data) {
        return new TaotaoResult(status, msg, data);
    }

    public static TaotaoResult ok(Object data) {
        return new TaotaoResult(data);
    }

    public static TaotaoResult ok() {
        return new TaotaoResult(null);
    }

    public TaotaoResult() {

    }

    public static TaotaoResult build(Integer status, String msg) {
        return new TaotaoResult(status, msg, null);
    }

    public TaotaoResult(Integer status, String msg, Object data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }

    public TaotaoResult(Object data) {
        this.status = 200;
        this.msg = "OK";
        this.data = data;
    }

//    set/get method for status/msg/data

    /**
     * 将json结果集转化为TaotaoResult对象
     * 
     * @param jsonData json数据
     * @param clazz TaotaoResult中的object类型
     * @return
     */
    public static TaotaoResult formatToPojo(String jsonData, Class<?> clazz) {
        try {
            if (clazz == null) {
                return MAPPER.readValue(jsonData, TaotaoResult.class);
            }
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (clazz != null) {
                if (data.isObject()) {
                    obj = MAPPER.readValue(data.traverse(), clazz);
                } else if (data.isTextual()) {
                    obj = MAPPER.readValue(data.asText(), clazz);
                }
            }
            return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 没有object对象的转化
     * 
     * @param json
     * @return
     */
    public static TaotaoResult format(String json) {
        try {
            return MAPPER.readValue(json, TaotaoResult.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Object是集合转化
     * 
     * @param jsonData json数据
     * @param clazz 集合中的类型
     * @return
     */
    public static TaotaoResult formatToList(String jsonData, Class<?> clazz) {
        try {
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (data.isArray() && data.size() > 0) {
                obj = MAPPER.readValue(data.traverse(),
                        MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
            }
            return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }

}

5.3 Service层

ItemServiceImpl类添加addItem方法,返回值TaotaoResult

    @Override
    public TaotaoResult addItem(TbItem item, String desc) {
        //生成商品id
        long itemId = IDUtils.genItemId();
        //补全item的属性
        item.setId(itemId);
        //商品状态,1-正常,2-下架,3-删除
        item.setStatus((byte) 1);
        item.setCreated(new Date());
        item.setUpdated(new Date());
        //向商品表插入数据
        itemMapper.insert(item);
        //创建一个商品描述表对应的pojo
        TbItemDesc itemDesc = new TbItemDesc();
        //补全pojo的属性
        itemDesc.setItemId(itemId);
        itemDesc.setItemDesc(desc);
        itemDesc.setUpdated(new Date());
        itemDesc.setCreated(new Date());
        //向商品描述表插入数据
        itemDescMapper.insert(itemDesc);
        //返回结果
        return TaotaoResult.ok();
    }

通过<dubbo:service interface="com.taotao.service.ItemService" ref="itemServiceImpl" timeout="300000"/>发布服务

5.4 表现层

通过<dubbo:reference interface="com.taotao.service.ItemService" id="itemService" />引用服务
请求的url:/item/save,ItemController中添加saveItem方法

    @RequestMapping(value="/item/save", method=RequestMethod.POST)
    @ResponseBody
    public TaotaoResult addItem(TbItem item, String desc) {
        TaotaoResult result = itemService.addItem(item, desc);
        return result;
    }

item-add.js中,提交表单,传递的数据被TBItem与String desc描述接收

        //ajax的post方式提交表单
        //$("#itemAddForm").serialize()将表单序列号为key-value形式的字符串
        $.post("/item/save",$("#itemAddForm").serialize(), function(data){
            if(data.status == 200){
                $.messager.alert('提示','新增商品成功!');
            }
        });
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,451评论 25 707
  • 追风的人 在这样一个明媚的午后 我们沿着风的方向 去看风的足迹 阳光热烈 白云也跟着风儿移走 如果你看见了 天空棉...
    燕Annie阅读 158评论 2 1
  • P先生最近犯了点错误,让我惩罚他(☆_☆) 这样的机会,是要把握住的。 — 那你后天不许去踢球了。 — 啊?!都答...
    写字小号阅读 192评论 5 4
  • 这世界上有最美好的感情等你发现。 六 今天是孩子的生日,爸爸妈妈都出差不在家,奶奶一个人照顾他。孩子从很小的时候就...
    崖柏今天早起了阅读 396评论 1 2