您的位置:首页 > 理论基础 > 数据结构算法

通用灵活的网站内容展示数据结构设计与代码编写(借助AngularJs)

2015-11-08 15:08 651 查看

前言

假设公司需要快速构建一个CMS系统,同时支持灵活多变的内容展示形式。该CMS系统需要支持用户自定义内容的属性、值、以及展示方式。因此,不能仅仅使用固定的表字段来表示某个内容或者栏目的属性。同时,栏目与内容的展示,在某一程度上应该遵循固定的数据格式,便于最大限度的代码复用。

数据结构

参考Jspxcms本文中将涉及几种主要数据结构:

栏目命名为节点(Node)

某一具体内容命名为信息(Info)

页面展示模板资源命名为模型(Model)

可附加的属性定义(AttribueDefine)

属性值(AttributeValue)

节点(Node)

主要包含栏目(Node), 栏目属性关联(NodeAttribute)两个实体

Node

记录栏目的基本属性,其中记录父节点和根两个属性即可令Node集合形成树,从而支持复杂的栏目组合。

@Id
@Column(name = "node_id")
private int nodeId;//栏目ID

@Column(name = "node_name", length = 100)
private String nodeName;//栏目名称

@Column(name = "parent_id")
private int parentId;//父节点

@Column(name = "node_model_id")
private int nodeModelId;//栏目模板

@Column(name = "node_img_url", length = 1024)
private String nodeImgUrl;//栏目图URL

@Column(name = "node_sequence", length = 3)
private String nodeSequence;//栏目顺序号

@Column(name = "is_root", length = 1)
private String isRoot;//是否根栏目

@Column(name = "is_show", length = 1)
private String isShow;//是否启用

@Column(name = "home_flag", length = 1)
private String homeFlag;//首页展示

@Column(name = "version_no", length = 17)
private String versionNo;//版本号


NodeAttribute

记录外挂属性定义与属性值的Id,通过外挂形式支持无限的属性/值集合。这里将值直接记录在该实体中。

@Id
@Column(name= "node_id")
private int nodeId;//栏目id

@Column(name = "attribute_define_id")
private Long attributeDefineId;//属性定义id

@Id
@Column(name = "key_code", length = 50)
private String keyCode;//key

@Id
@Column(name = "attribute_value", length = 2000)
private String attributeValue;//属性值


内容(Info)

Info

Info作为栏目的内容,挂在某个栏目之下,同时也支持外挂属性定义和属性值。

@Id
@Column(name = "info_id",unique=true,nullable=false)
private Long infoId;//内容ID  info_id bigint      主键

@Column(name = "node_id",nullable=false)
private Integer nodeId;//栏目ID   node_id int not null

@Column(name = "priority",length=2)
private String priority;//优先级   priority

@Column(name = "info_title",length=150,nullable=false)
private String infoTitle;//标题   info_title  varchar(150)    not null

@Column(name = "info_subtitle",length=150)
private String infoSubtitle;//副标题   info_subtitle   varchar(150)

@Column(name = "info_link",length=1024)
private String infoLink;//转向链接  info_link   varchar(1024)

@Column(name = "is_new_window",length=1)
private String isNewWindow;//是否在新窗口打开   is_new_window   char(1)     0否、1是

@Column(name = "info_path",length=1024)
private String infoPath;//信息路径  info_path   varchar(1024)

@Column(name = "info_template_id")
private int infoTemplateId;//内容模板

@Column(name = "meta_description",length=255)
private String metaDescription;//描述 meta_description    varchar(255)

@Column(name = "info_author",length=100)
private String infoAuthor;//作者  info_author varchar(100)

@Column(name = "start_date",length=8)
private String itartDate;//有效期起日期   start_date  char(8)

@Column(name = "stop_date",length=8)
private String stopDate;//有效期止日期    stop_date   char(8)

@Column(name = "version_no",length=17,nullable=false)
private String versionNo;//版本号  version_no  varchar (17)    not null    新增和更新时填写


InfoAttribute

通过外挂形式支持无限属性定义与属性值。这里将值直接记录在该实体中。

@Id
@Column(name = "info_id")
private Long infoId;//内容ID

@Column(name = "attribute_define_id")
private Long attributeDefineId;//属性定义ID

@Id
@Column(name = "key_code", length = 50)
private String keyCode;//key

@Id
@Column(name = "attribute_type")
private String attributeType;//值


属性(Attribute)

将属性定义与属性值区分开,其中属性值(AttributeValue)仅与属性定义连接,作为记录某属性的多个备选值列表用。实际应用是,使用的值将直接记录在(NodeAttribute或者InfoAttribute中)。

AttributeDefine

@Id
@Column(name = "attribute_define_id")
private Long attributeDefineId;//属性定义id

@Column(name ="attribute_type", length =1)
private String attributeType;//属性类型

@Column(name = "input_type", length = 50)
private String inputType;//输入类型

@Column(name ="field_name", length = 100)
private String fieldName;//字段名称

@Column(name = "field_code", length = 50)
private String fieldCode;//字段代码

@Column(name = "attribute_sequence", length = 3)
private String attributeSequence;//字段顺序

@Column(name = "is_mandatory",length = 1)
private String isMandatory;//是否必填

@Column(name = "default_value", length = 100)
private String defaultValue;//默认值

@Column(name = "information", length = 255)
private String information;//提示信息


DefineValue

@Id
@Column(name = "define_value_id")
private Long defineValueId;//属性定义值id

@Column(name = "attribute_define_id")
private Long attributeDefineId;//属性定义id

@Column(name = "value_attribute", length = 100)
private String valueAttribute;//值

@Column(name = "sequence_attribute", length = 3)
private String sequenceAttribute;//排序


后台代码

数据包装

因为需要同时返回栏目(内容)以及相应属性,因此需要将数据集合封装好并回传。以栏目封装为例。

最外层为NodeItem,其中包含Node以及一个属性/值集合NodeAttributeDefinePair 。类定义如下

public class NodeItem {
Node node;
List<NodeAttributeDefinePair> pairs;
public NodeItem(){}
public NodeItem(Node node, List<NodeAttributeDefinePair> pairs){
this.node  =node;
this.pairs = pairs;
}
public Node getNode() {
return node;
}
public void setNode(Node node) {
this.node = node;
}
public List<NodeAttributeDefinePair> getPairs() {
return pairs;
}
public void setPairs(List<NodeAttributeDefinePair> pairs) {
this.pairs = pairs;
}
}

public class NodeAttributeDefinePair {

private NodeAttribute nodeAttribute;
private AttributeDefine define;
private AttributeDefineValue value;

public NodeAttributeDefinePair(){}
public NodeAttributeDefinePair(NodeAttribute attr, AttributeDefine define, AttributeDefineValue value){
this.nodeAttribute = attr;
this.define = define;
this.value = value;
}
public NodeAttributeDefinePair(NodeAttribute attr, AttributeDefine define){
this.nodeAttribute  =attr;
this.define = define;
}

public NodeAttribute getNodeAttribute() {
return nodeAttribute;
}
public void setNodeAttribute(NodeAttribute nodeAttribute) {
this.nodeAttribute = nodeAttribute;
}
public AttributeDefine getDefine() {
return define;
}
public void setDefine(AttributeDefine define) {
this.define = define;
}
public AttributeDefineValue getValue() {
return value;
}
public void setValue(AttributeDefineValue value) {
this.value = value;
}
}


接口整理

初步考虑,用于获取数据的接口应该包含一下几个:

1、获取某栏目以及相应属性

2、获取某内容以及相应属性

3、获取某栏目的子栏目列表以及属性

4、获取某栏目的内容列表以及属性

具体实现

就是根据父子关系按照id进行搜索,比较简单,结合数据包装返回即可,因此不做过多描述。

前台代码(AngularJs)

根据上节的接口描述,可以将实现上述接口的方法统一封装为模块,并在需要的地方注入并调用即可。【使用$q 实现异步获取】

模块定义

angular.module('nodeInfo',[])
/*列表类型服务*/
.service('nodeInfolistService', function(){
this.listSubNodes = function($scope, $http,$q,paramsxxxx){
var q = $q.defer();
var url = ctx + 'api/xxxxxxx';
var params = {
params:paramsxxxx
};
$http({method:'GET', params:params,url:url})
.success(function(response){
q.resolve(response);
})
.error(function(response){
q.reject('服务器开小差了,请稍后重试');
});
return q.promise;
};

//......略
})
/*内容获取类型服务*/
.service('nodeInfoitemService', function(){
//......略
});


模块使用

外部使用的时候,首先在页面引入相应的js文件,在页面的APP申明中注入该模块,并引用服务即可。

代码如下:

/*注入nodeInfo*/
var app = angular.module('app',['nodeInfo']);

app.service('appService',function(){
});
/*引入服务nodeInfolistService*/
app.controller('appCtrl', function($scope, $http,$q, appService, nodeInfolistService){
var currentNodeId = $('#currentNodeId').text();
var parentId = $('#parentId').text();
//promise方式异步获取
var subPromise = nodeInfolistService.listSubNodes($scope, $http, $q, currentNodeId);
subPromise.then(
function(subNodes){
$scope.subNodes = subNodes;
},
function(err){
console.log(err);
}
)
});


通过归纳整理通用的数据获取接口库,并封装数据获取的相应服务,使网站的任意形式内容均可以以统一的方式获取,极大的简化了后期开发的代码量。同时,若后台逻辑变化时,也仅需要修改封装的服务即可,不需要修改其它页面。

效果

node列表



info列表



node详情+info列表

这里的”第一集”就是一个info



info详情

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息