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

前言

假设公司需要快速构建一个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列表
node列表
info列表
infolist
node详情+info列表
这里的”第一集”就是一个info
这里写图片描述
info详情
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值