基于Hyperledger Fabric构建一个简易 Web A

作者: eye_water_ | 来源:发表于2018-08-28 22:08 被阅读47次

    最近接触了Hyperledger Fabric,官网给的app并没有界面,不过有一个单页版的项目,在此基础上做出了一个简易的 web app
    github项目地址
    功能:

    1. 发布食品
    2. 添加配料信息
    3. 中转食品
    4. 查询食品信息
    5. 查询配料信息
    6. 查询中转信息

    在线Demo 最好使用PC端访问(没有针对手机端界面进行设置,已填过编号为1001的食品信息,可以查着玩玩,没有对web安全进行过多配置,请手下留情,服务器到期时间2018-10-5)

    前言

    本篇文章只会对我认为比较重要地部分进行大致地讲解,不会讲解细节,如果有什么疑惑的地方请在评论区进行评论,会根据评论情况进行下一篇文章的编写

    项目架构

    前端使用AngularJs来进行页面渲染,后台使用Node.js返回Json数据,数据存储在Hyperledger Fabric提供的数据库

    启动脚本详解

    source-app/server.js

    const middlewares = [
      express.static(path.join(__dirname, 'public')),//静态文件
      bodyParser.urlencoded({ extended: true }),//解析POST所传参数
      cookieParser(),//使用Cookie来进行Flash文字演示
      session({
        secret: 'super-secret-key',
        key: 'super-secret-cookie',
        resave: false,
        saveUninitialized: false,
        cookie: { maxAge: 60000}
      }),
      flash()
    ]
    app.use(middlewares)//激活中间件
    app.use(express.static(path.join(__dirname, 'views')));//使用模板
    app.set('view engine', 'ejs');//配置模板引擎
    require('./routes.js')(app);
    

    URL分为三个部分

    Part 1

    获取HTML
    source-app/routes.js

    app.get('/', function(req, res) {
            tuna.index(req, res);
    });
    ...
    

    Part 2

    表单

    表单提交流程

    填写表单->POST到特定的URL->处理表单信息->重定向到首页

    表单的HTML代码编写
    source-app/views/form.ejs

    <form method="post" action="/re_form" novalidate>
        <div class="section">食品信息</div>
        <div class="inner-wrap">
            <label>食品编号 <input type="text" name="field1" /></label>
            <label>食品名称 <input type="text" name="field1" /></label>
            <label>食品规格 <input type="text" name="field1" /></label>
            <label>食品生产日期 <input type="text" name="field1" /></label>
            <label>食品保质期 <input type="text" name="field1" /></label>
            <label>食品批次号 <input type="text" name="field1" /></label>
            <label>食品生产许可证编号<input type="text" name="field1" /></label>
            <label>食品生产商名称 <input type="text" name="field1" /></label>
            <label>食品生产价格<input type="text" name="field1" /></label>
            <label>食品生产所在地<input type="text" name="field1" /></label>
        </div>
        <div class="button-section">
         <input type="submit" name="提交" />
        </div>
    </form>
    

    POST到特定的URL
    source-app/routes.js

    app.post('/re_form', function(req, res) {
            var function_name = 'addProInfo'//调用chaincode中的函数
            tuna.re_form(req, res, function_name);
    });
    

    表单处理
    source-app/controller.js

    const request = {
        chaincodeId: 'source-app',
        fcn: function_name,//调用chaincode中的函数
        args: req.body.field1,//获取表单所填信息(函数所需参数)
        chainId: 'mychannel',
        txId: tx_id
    };
    

    调用Chaincode

    chaincode/source-app/source-app.go

    func (a *FoodChainCode) addProInfo(stub shim.ChaincodeStubInterface, args []string) pb.Response {
        var err error 
        var FoodInfos FoodInfo
    
        if len(args)!=10{
            return shim.Error("Incorrect number of arguments.")
        }
        FoodInfos.FoodID = args[0]
        if FoodInfos.FoodID == ""{
            return shim.Error("FoodID can not be empty.")
        }
        
        
        FoodInfos.FoodProInfo.FoodName = args[1]
        FoodInfos.FoodProInfo.FoodSpec = args[2]
        FoodInfos.FoodProInfo.FoodMFGDate = args[3]
        FoodInfos.FoodProInfo.FoodEXPDate = args[4]
        FoodInfos.FoodProInfo.FoodLOT = args[5]
        FoodInfos.FoodProInfo.FoodQSID = args[6]
        FoodInfos.FoodProInfo.FoodMFRSName = args[7]
        FoodInfos.FoodProInfo.FoodProPrice = args[8]
        FoodInfos.FoodProInfo.FoodProPlace = args[9]
        ProInfosJSONasBytes,err := json.Marshal(FoodInfos)
        if err != nil{
            return shim.Error(err.Error())
        }
    
        err = stub.PutState(FoodInfos.FoodID,ProInfosJSONasBytes)
        if err != nil{
            return shim.Error(err.Error())
        }
    
        return shim.Success(nil)
    }
    

    Part 3

    返回Json
    source-app/routes.js

    app.get('/source/:id', function(req, res) {
        var function_name = 'getProInfo'//调用chaincode函数
        tuna.get_tuna(req, res, function_name);
    });
    

    source-app/controller.js

    const request = {
        chaincodeId: 'source-app',
        txId: tx_id,
        fcn: function_name,//调用chaincode函数
        args: [key]//函数所需参数
    };
    

    调用Chaincode
    chaincode/source-app/source-app.go

    func(a *FoodChainCode) getProInfo (stub shim.ChaincodeStubInterface,args []string) pb.Response{
        
        if len(args) != 1{
            return shim.Error("Incorrect number of arguments.")
        }
        FoodID := args[0]
        resultsIterator,err := stub.GetHistoryForKey(FoodID)
        if err != nil {
            return shim.Error(err.Error())
        }
        defer resultsIterator.Close()
        
        var foodProInfo ProInfo
    
        for resultsIterator.HasNext(){
            var FoodInfos FoodInfo
            response,err :=resultsIterator.Next()
            if err != nil {
                return shim.Error(err.Error())
            }
            json.Unmarshal(response.Value,&FoodInfos)
            if FoodInfos.FoodProInfo.FoodName != ""{
                foodProInfo = FoodInfos.FoodProInfo
                continue
            }
        }
        jsonsAsBytes,err := json.Marshal(foodProInfo)//转为Json格式
        if err != nil {
            return shim.Error(err.Error())
        }
        return shim.Success(jsonsAsBytes)
    }
    
    

    例子
    访问http://120.27.18.178:3389/source/1001,可以得到以下数据

    {"FoodName":"苹果","FoodSpec":"123456","FoodMFGDate":"2018-8-27","FoodEXPDate":"一月","FoodLOT":"123","FoodQSID":"456","FoodMFRSName":"啦啦啦","FoodProPrice":"2","FoodProPlace":"郑州"}
    

    AngularJs与单页页面

    如果我们没有进行查询,我们希望页面呈现为这样


    blog_1.JPG

    当我们查询之后,我们希望页面呈现为这样


    blog_1.JPG

    HTML代码编写

    source-app/views/search.ejs

    <body ng-app="application" ng-controller="appController">
            <div class="form-wrapper">
                <input type="text" id="search" placeholder="食品编号..." ng-model="query_id" required>
                <input type="submit" value="搜索" id="submit" ng-click="querySource()">
            </div>
            <table cellspacing="0" id="query_source">
                <tr>
                    <th>食品名称</th>
                    <th>食品规格</th>
                    <th>食品生产日期</th>
                    <th>食品保质期</th>
                    <th>食品批次号</th>
                    <th>食品生产许可证编号</th>
                    <th>食品生产商名称</th>
                    <th>食品生产价格</th>
                    <th>食品生产所在地</th>
                </tr>
                <tr>
                        <td>{{query_source.FoodName}}</td>
                        <td>{{query_source.FoodSpec}}</td>
                        <td>{{query_source.FoodMFGDate}}</td>
                        <td>{{query_source.FoodEXPDate}}</td>
                    <td>{{query_source.FoodLOT}}</td>
                    <td>{{query_source.FoodQSID}}</td>
                    <td>{{query_source.FoodMFRSName}}</td>
                    <td>{{query_source.FoodProPrice}}</td>
                    <td>{{query_source.FoodProPlace}}</td>
                    </tr>
            </table>
        </body>
    

    AngularJs脚本编写

    source-app/public/js/app.js

    var app = angular.module('application', []);
    
    // Angular Controller
    app.controller('appController', function($scope, appFactory){
    
        $("#success_holder").hide();
        $("#success_create").hide();
        $("#error_holder").hide();
        $("#error_query").hide();
        $scope.querySource = function(){
            var id = $scope.query_id;
            appFactory.querySource(id, function(data){
                $scope.query_source = data;
                if ($scope.query_tuna == "Could not locate tuna"){
                    console.log()
                    $("#error_query").show();
                } else{
                    $("#error_query").hide();
                }
            });
        }
    });
    
    app.factory('appFactory', function($http){
        var factory = {};
        factory.querySource = function(id, callback){
            $http.get('/source/'+id).success(function(output){
                callback(output)
            });
        }
    });
    

    可以不用搞懂basic-network文件夹(我自己也不懂,不过并不影响写项目,如果读者懂,欢迎指导)

    如果有什么不懂的地方或想搞懂更细节的地方,请在评论区留言,下一篇文章会依据评论来定方向

    参考链接
    Education
    Writing Your First Application

    欢迎star😉

    相关文章

      网友评论

      本文标题:基于Hyperledger Fabric构建一个简易 Web A

      本文链接:https://www.haomeiwen.com/subject/vkrcwftx.html