基于Hyperledger Fabric开发一个食品溯源项目(一)

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

  1. 发布食品
  2. 添加配料信息
  3. 中转食品
  4. 查询食品信息
  5. 查询配料信息
  6. 查询中转信息(只可以溯源整个食品信息,不能溯源配料信息)

前言

在此之前,本文章的标题为
基于Hyperledger Fabric开发一个简易的Web App(一)

但是没有什么阅读量,这一次把标题取得大一点来看看阅读量能否增加

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

项目架构

前端使用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与单页页面

如果我们没有进行查询,我们希望页面呈现为这样 ![这里写图片描述](https://img-blog.csdn.net/201808281721147?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2V5ZV93YXRlcg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 当我们查询之后,我们希望页面呈现为这样 ![这里写图片描述](https://img-blog.csdn.net/20180828172158650?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2V5ZV93YXRlcg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 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文件夹(我自己也不懂,不过并不影响写项目,如果读者懂,欢迎指导)

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

欢迎star _

你可能感兴趣的:(基于Hyperledger Fabric开发一个食品溯源项目(一))