自定义销售属性的展示和数据存储解决方案

之前一直在国内某服装电商公司.
我们的商品结构设计是:
款 -> 颜色 -> 尺码[SKU]. 比较简单.

现在,遇到一个新的需求. 我们需要支持用户自定义销售属性.如下截图:

SKU列表会随着属性的增加和删除会发生变化, 如下截图:

增加了一个型号短袖后,变成如下:


我的数据表设计如下
由 Product表, SellProperty表和SKU表三张表来支持该功能.
表结构如下:
CREATE TABLE `purchaser_product` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `product_code` varchar(45) DEFAULT NULL COMMENT '商品编码',
  `product_name` varchar(45) NOT NULL COMMENT '商品名称',
  `price` decimal(12,2) DEFAULT NULL COMMENT '价格',
  `unit` int(11) NOT NULL COMMENT '单位-个,件等',
  `category_id` bigint(20) NOT NULL,
  `top_category_id` bigint(20) NOT NULL,
  `description` text COMMENT '商品描述',
  `create_time` datetime NOT NULL,
  `creator_name` varchar(45) NOT NULL,
  `update_time` datetime DEFAULT NULL,
  `updator_name` varchar(45) DEFAULT NULL,
  `delete_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除',
  `on_shelf_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '上下架状态',
  `brand` varchar(45) DEFAULT NULL COMMENT '商品品牌',
  `model` varchar(45) DEFAULT NULL COMMENT '商品型号',
  `vendor_id` bigint(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8;
CREATE TABLE `purchaser_product_sell_property` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `product_id` bigint(20) DEFAULT NULL,
  `property_name` varchar(45) DEFAULT NULL,
  `order_num` int(11) DEFAULT NULL,
  `creator_name` varchar(45) DEFAULT NULL,
  `creator_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;

CREATE TABLE `purchaser_sku` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `product_id` bigint(20) NOT NULL,
  `sell_property_name_struct` varchar(45) DEFAULT NULL COMMENT '销售属性名的结构,从第一级到最后,以逗号分隔',
  `price` decimal(12,2) NOT NULL,
  `product_code` varchar(45) NOT NULL,
  `sell_property_value_struct` varchar(45) DEFAULT NULL COMMENT '销售属性值的结构,从第一级到最后,以逗号分隔',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=69 DEFAULT CHARSET=utf8;


首先最复杂的就是增加属性,会变化表格.
我抽象了一个JS对象,有两个主要方法:
addProperty(property) // 添加属性
addPropertyValue(property,propertyValue) //添加属性值
var propertyMap = {
                propertyList:[],
                total:0,
                propertyObj:{},
                validProperties:[],
                deleteProperty : function(propertyName) {
                    delete this.propertyObj[propertyName];
                    removeByValue(this.propertyList,propertyName);
                    this.show();
                },
                hasValue : function() {
                    var hasValue = false;
                    $.each(this.propertyObj,function(i,n){
                        hasValue = n.length > 0;
                    });
                    return hasValue;
                },
                propertyHasValue : function(property) {
                    return this.propertyObj[property] == null ? true:(this.propertyObj[property].length>0);
                },
                addProperty : function(propertyName) {
                    this.propertyList.push(propertyName);
                    this.propertyObj[propertyName] = [];
                    var tpl = document.getElementById("property_row_tpl").innerHTML;
                    var html = juicer(tpl,{property:propertyName});
                    $("#property_area").append(html);

                },
                addValue : function(propertyName,value) {
                    if (contains(this.propertyObj[propertyName],value) ) {
                        layer.msg("属性值已经存在",{icon: 5,time: 1000});
                        return;
                    }
                    this.propertyObj[propertyName].push(value);
                    var tpl = document.getElementById("property_value_tpl").innerHTML;
                    var html = juicer(tpl,{propertyValue:value,propertyName:propertyName});
                    $("#" + propertyName + "_value_display_area").append(html);
                    this.show();
                },
                deleteValue : function(propertyName,value) {
                    removeByValue(this.propertyObj[propertyName],value);
                    this.show();
                },
                show : function() {
                    //没有属性,直接返回
                    if (this.propertyList == null || this.propertyList.length <= 0) {
                        $("#tablearea").html("");
                        return;
                    }

                    var propertySize = this.propertyList.length;//总共有多少个属性

                    var validProperty = null;
                    var last = null;
                    var validPropertyList = [];
                    var totalTR = 1;
                    for (var i = propertySize - 1; i >= 0; i--) {
                        if (this.propertyObj[this.propertyList[i]].length > 0) {
                            validProperty = {propertyValues:[],index:0,pre:null,next:null,nextLoop:1};
                            //如果属性对应的属性值列表有值,说明此属性有效
                            validProperty.index = i + 1;
                            validProperty.propertyValues = this.propertyObj[this.propertyList[i]];
                            if (last != null) {
                                last.pre = validProperty;
                            }
                            validProperty.next = last;
                            validProperty.name = this.propertyList[i];
                            last = validProperty;
                            validPropertyList.unshift(validProperty);
                            totalTR = totalTR * validProperty.propertyValues.length;
                        }
                    }
                    // 没有有效属性,也就是所有属性没有属性值,直接返回
                    if (validPropertyList == null || validPropertyList.length <= 0) {
                        $("#tablearea").html("");
//                        var defaultTable = document.getElementById("default_sku_tpl").innerHTML;
//                        $("#tablearea").html(defaultTable);
                        return;
                    }
                    this.validProperties = validPropertyList;
                    for (var i = 0; i < validPropertyList.length; i++) { //计算每一个属性的所有下级属性长度乘积的结果,用来表示,下级一共多少行.
                        var nextLoop = 1;
                        var nextNode = validPropertyList[i].next;
                        while (nextNode != null) {
                            nextLoop = nextLoop * nextNode.propertyValues.length;
                            nextNode = nextNode.next;
                        }
                        validPropertyList[i].nextLoop = nextLoop;
                    }

                    var totalTD = validPropertyList.length; // 每行有几个TD
                    var tableContent = '';
                    for (var i = 1; i <= totalTR; i++) { //需要有多少行
                        var tr = '<tr>';
                            for (var j = 1; j <= totalTD; j++) {
                                var currentProperty = validPropertyList[j - 1];
                                if (j == totalTD) {
                                    var index = parseInt((i - 1)%currentProperty.propertyValues.length);
                                    var propertyValueStruct = loadPropertyValues(currentProperty, i);
                                    var propertyStruct = loadProperty(currentProperty);
                                    console.log(propertyStruct);
                                    var id = i + "-" + j;
                                    tr +=  "<td>"+currentProperty.propertyValues[index]+"</td>";
                                    tr +=  "<td style='display:none'><input type='text' class='form-control product_code_class' placeholder='填写商品编码' id='productcode_"+id+"'  propertystruct='"+propertyValueStruct+"' property='"+propertyStruct+"'></td>";
                                    tr +=  "<td><input type='text' value='' class='touchspin' id='price_"+id+"'></td>";
                                    break;
                                }
                                var isFirst = false;
                                if (currentProperty.nextLoop == 1) {
                                    isFirst = ( i % currentProperty.nextLoop == 0);
                                } else {
                                    isFirst = (i % currentProperty.nextLoop == 1);
                                }
                                if (isFirst) {
                                    var index = parseInt(((i - 1)/currentProperty.nextLoop)%currentProperty.propertyValues.length);
                                    tr +=  "<td rowspan='"+ currentProperty.nextLoop +"'>"+currentProperty.propertyValues[index]+"</td>";
                                }
                            }
                        tr += "</tr>";
                        tableContent += tr;
                    }
                    var tpl = document.getElementById("table_tpl").innerHTML;
                    var html = juicer(tpl,{list:validPropertyList});
                    html = html.replace("######",tableContent);
                    $("#tablearea").html(html);
                    $(".touchspin").TouchSpin({
                        min: 0,
                        max: 100000,
                        step: 0.1,
                        decimals: 2,
                        prefix: '$'
                    });
                }

            };




  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Safe Software公司是世界领先的空间数据转换软件供应商,成立于1993年,长期致力于通过提供无缝的数据转换解决方案来提高用户对各类空间数据的访问能力。 该公司提供的数据处理系统FME Suite已经销售到全球25个国家的电力、电信、交通、公用设施、石油天然气、规划、测绘、国土、gov机构等行业,以及大批的GIS/GPS/RS软件开发商.目前全球用户数已达150,000个。由于FME Suite在数据转换领域的通用性,它正在逐渐成为业界在各种应用程序之间共享地理空间数据的事实标准。 Safe software公司提供以下产品: FME是一个对于读、写和转换空间数据的完整解决方案。所有FME套件都包括了三个核心组件来满足对空间数据转换和改造的需求。 FME Object是一套控件集,能够在应用程序中添加对于空间数据读、写和处理的功能。FME Object能够让用户自己开发的应用程序直接读、写不同的空间数据类型,是一个集成的解决方案而不是简单的数据转换。 SpatialDirect是一个有效的、可配置的和具有伸缩性的系统,能够让用户通过Internet/Intranet来获取特定格式和投影的空间数据。SpatialDirect 可以和通用的发布系统相集成如ArcIMS、MapGuide和MapXtreme,也可以作为单独的应用。 FME Suite是一套完整的访问空间数据解决方案,可用于读写存储和转换各种空间数据。它能实现各类GIS及CAD格式的数据相互转换。 FME有以下特点: - 以FME为中心实现超过100 种GIS及CAD空间数据格式,如DWG、DXF、DGN、 ArcInfo Corvage、Shape File、ArcSDE、Oracle SDO等的相互转换; - 独立地直接浏览各种格式的空间数据,同时浏览图形、属性和坐标数据; - 提供为数据转换进行自定义的图形化界面,能够可视化定义从原始数据到目标数据的图形与属性的对应关系; - 将数据转换与丰富的GIS数据处理功能结合在一起如坐标系统转换、迭加分析、相交运算、构造闭合多边形、属性合并等; - 提供了FME Plug-in Builder API、FME Object API,用户可以为FME扩展新的数据格式,通过这些接口将FME嵌入到自己的应用系统中,实现方便的应用集成; - 支持海量数据处理,大型的数据转换通过编写脚本及批处理模式高效运行,即使输入数据多达数千个甚至上万个文件。 基于FME上述特点,您可以对已经拥有的各类空间数据实现以下高效的管理及应用: - 利用FME提供的强大数据处理能力,您能够改善已有空间数据的质量; - 利用FME提供的强健数据加载和提取功能,将已有的各类空间数据加载到大型空间数据库进行集中管理,实现不同数据的集成; - 基于FME的数据集成和转换技术,在同一单位内部或不同单位之间实现跨越多个GIS或CAD系统的数据共享。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
### 回答1: 使用Apache Sqoop可以从Hive数据仓库获取数据,然后将其发布到Kafka。例如,可以使用以下命令将Hive表中的数据导出到Kafka:sqoop export --connect jdbc:hive2: //localhost:10000/default --table mytable --export-dir /user/hive/warehouse/mytable --input-fields-terminated-by '\t' --output-topic mytopic。 ### 回答2: Hive是一种基于Hadoop的数据仓库解决方案,而Kafka是一种分布式流处理平台。在将Hive数据收集并上传到Kafka之前,需要先配置Hive和Kafka的环境。 首先,确保Hive表中的数据是可用的。可以通过在Hive中创建外部表,将数据从Hadoop集群的存储目录加载到Hive中。例如,我们可以在Hive中创建一个表并指定外部存储位置: ``` CREATE EXTERNAL TABLE my_table ( column1 STRING, column2 INT, ... ) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LOCATION '/path/to/data'; ``` 接下来,需要安装和配置Kafka。确保Kafka集群处于可用状态,并创建一个主题以接收上传的数据。 一种将Hive数据上传到Kafka的方法是使用Apache Flume。Flume是一种用于高可靠性,分布式,可管理的收集,聚合和移动大量日志数据的流式数据传输系统。通过使用Flume的Kafka Sink插件,可以将Hive数据实时传输到Kafka。 然后,创建Flume配置文件,配置Kafka Sink。以下是一个简单的例子: ``` # Flume agent properties my_agent.sources = my_source my_agent.channels = my_channel my_agent.sinks = my_sink # Source properties my_agent.sources.my_source.type = spooldir my_agent.sources.my_source.spoolDir = /path/to/hive/data # Channel properties my_agent.channels.my_channel.type = memory # Sink properties my_agent.sinks.my_sink.type = org.apache.flume.sink.kafka.KafkaSink my_agent.sinks.my_sink.topic = my_topic my_agent.sinks.my_sink.brokerList = kafka-broker1:9092,kafka-broker2:9092 # Binding source, channel, and sink my_agent.sources.my_source.channels = my_channel my_agent.sinks.my_sink.channel = my_channel ``` 在此配置中,指定了源类型为spooldir(指向Hive数据的目录),通道类型为内存,以及Kafka Sink插件的属性,如主题和代理列表。 最后,运行Flume代理,Flume将监视Hive数据目录中的任何更改,并将更改的数据实时传输到Kafka主题。 上述是一种通过Flume将Hive数据收集并上传给Kafka的方法。根据实际需求和环境,也可以考虑其他方法,如使用自定义脚本或编程语言来实现数据传输。 ### 回答3: Hive是一个数据仓库工具,常用于数据分析和查询。而Kafka是一个分布式流处理平台,用于高吞吐量的实时数据处理。将Hive数据收集并上传给Kafka可以通过以下步骤进行: 1. 安装Hive和Kafka:首先需要在服务器上安装并配置Hive和Kafka,确保它们能够正常运行。 2. 创建Hive表:使用Hive的DDL语句,创建一个表来存储需要收集的数据。例如,创建一个表来存储电商网站的用户购买记录: ``` CREATE TABLE purchase_records ( user_id INT, product_id INT, purchase_date STRING ) STORED AS ORC; ``` 3. 编写Hive脚本:使用Hive的HQL语句查询需要收集的数据,并将查询结果输出到一个临时表中。 ``` INSERT INTO TABLE temp_purchase_records SELECT user_id, product_id, purchase_date FROM purchase_records WHERE purchase_date BETWEEN '2022-01-01' AND '2022-01-31'; ``` 4. 配置Kafka连接:在Hive配置文件中,添加Kafka连接器的配置信息,包括Kafka的地址、主题等。 5. 将数据上传到Kafka:在Hive脚本中,使用Kafka的UDF(用户定义函数)将临时表中的数据发送到Kafka。例如,使用KafkaUDF将购买记录发送到名为"purchase_topic"的Kafka主题中: ``` INSERT INTO TABLE kafka_records SELECT kafkaSend(topic='purchase_topic', value=CONCAT(user_id, ',', product_id, ',', purchase_date)) FROM temp_purchase_records; ``` 以上步骤实现了将Hive中的数据收集并上传到Kafka的过程。通过定义合适的Hive脚本,并在其中配置Kafka连接和使用KafkaUDF,可以根据实际需求将特定的数据收集并实时上传到Kafka主题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值