最近接触了Hyperledger Fabric,官网给的app并没有界面,不过有一个单页版的项目,在此基础上做出了一个简易的食品溯源项目
github项目地址
功能:
基于Hyperledger Fabric开发一个简易的Web App(一)
但是没有什么阅读量,这一次把标题取得大一点来看看阅读量能否增加
本篇文章只会对我认为比较重要地部分进行大致地讲解,不会讲解细节,如果有什么疑惑的地方请在评论区进行评论,会根据评论情况进行下一篇文章的编写
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);
app.get('/', function(req, res) {
tuna.index(req, res);
});
...
表单提交流程
填写表单->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)
}
返回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":"郑州"}
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 _