最近接触了Hyperledger Fabric,官网给的app并没有界面,不过有一个单页版的项目,在此基础上做出了一个简易的食品溯源项目
github项目地址 <https://github.com/zhazhalaila/hyperledger-simple-app>
功能:
* 发布食品
* 添加配料信息
* 中转食品
* 查询食品信息
* 查询配料信息
* 查询中转信息(只可以溯源整个食品信息,不能溯源配料信息)
前言
在此之前,本文章的标题为 基于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与单页页面
如果我们没有进行查询,我们希望页面呈现为这样

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

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 _
热门工具 换一换