在上一小节对Fabric 2.x链码有了基本了解之后,本节以学生信息上链为例,说明Fabric 2.x链码的综合应用。代码如下,内容包括:学生信息上链、查询学生信息(依据key)、查询key区间的学生信息、修改学生信息、获取历史信息等内容。
package main
import (
"encoding/json"
"fmt"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
"strconv"
"time"
)
// 定义一个对象,继承合约对象
type Student struct {
contractapi.Contract
}
// 上链信息(对象)
type StudentInfo struct {
Number string `json:"number"`
Name string `json:"name"`
Age string `json:"age"`
Address string `json:"address"`
}
// QueryResult structure used for handling result of query
type QueryResult struct {
Key string `json:"Key"`
Record *StudentInfo
}
type QueryHistoryResult struct {
TxId string `json:"tx_id"`
Value string `json:"value"`
IsDel string `json:"is_del"`
OnChainTime string `json:"on_chain_time"`
}
// 初始化账本
func (s *Student) InitLedger(ctx contractapi.TransactionContextInterface) error {
StudentInfos := []StudentInfo{
{Number: "2020001", Name: "张三", Age: "23", Address: "北京"},
{Number: "2020002", Name: "李四", Age: "24", Address: "上海"},
{Number: "2020003", Name: "王五", Age: "25", Address: "广州"},
{Number: "2020004", Name: "赵六", Age: "26", Address: "深圳"},
{Number: "2020005", Name: "田七", Age: "27", Address: "天津"},
{Number: "2020006", Name: "阿香", Age: "28", Address: "重庆"},
{Number: "2020007", Name: "阿凯", Age: "29", Address: "杭州"},
{Number: "2020008", Name: "阿兰", Age: "30", Address: "南京"},
{Number: "2020009", Name: "旺财", Age: "31", Address: "成都"},
{Number: "2020010", Name: "小明", Age: "32", Address: "西安"},
}
for _, StudentInfo := range StudentInfos {
StudentInfoAsBytes, _ := json.Marshal(StudentInfo)
err := ctx.GetStub().PutState(StudentInfo.Number, StudentInfoAsBytes)
if err != nil {
return fmt.Errorf("Failed to put to world state. %s", err.Error())
}
}
return nil
}
// 上链学生信息
func (s *Student) CreateStudentInfo(ctx contractapi.TransactionContextInterface, number string, name string, age string, address string) error {
StudentInfo := StudentInfo{
Number: number,
Name: name,
Age: age,
Address: address,
}
StudentInfoAsBytes, _ := json.Marshal(StudentInfo)
return ctx.GetStub().PutState(StudentInfo.Number, StudentInfoAsBytes)
}
//查询学生信息
func (s *Student) QueryStudentInfo(ctx contractapi.TransactionContextInterface, StudentInfoNumber string) (*StudentInfo, error) {
StudentInfoAsBytes, err := ctx.GetStub().GetState(StudentInfoNumber)
if err != nil {
return nil, fmt.Errorf("Failed to read from world state. %s", err.Error())
}
if StudentInfoAsBytes == nil {
return nil, fmt.Errorf("%s does not exist", StudentInfoNumber)
}
stuInfo := new(StudentInfo)
//注意: Unmarshal(data []byte, v interface{})的第二个参数为指针类型(结构体地址)
err = json.Unmarshal(StudentInfoAsBytes, stuInfo) //stuInfo := new(StudentInfo),stuInfo本身就是指针
if err != nil {
return nil, fmt.Errorf("Failed to read from world state. %s", err.Error())
}
return stuInfo, nil
}
// 查询学生信息(查询的key末尾是数字,有对应的区间)
func (s *Student) QueryAllStudentInfos(ctx contractapi.TransactionContextInterface, startNum, endNum string) ([]QueryResult, error) {
resultsIterator, err := ctx.GetStub().GetStateByRange(startNum, endNum)
if err != nil {
return nil, err
}
defer resultsIterator.Close()
results := []QueryResult{}
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}
StudentInfo := new(StudentInfo)
_ = json.Unmarshal(queryResponse.Value, StudentInfo)
queryResult := QueryResult{Key: queryResponse.Key, Record: StudentInfo}
results = append(results, queryResult)
}
return results, nil
}
// 修改学生信息
func (s *Student) ChangeStudentInfo(ctx contractapi.TransactionContextInterface, number string, name string, age string, address string) error {
stuInfo, err := s.QueryStudentInfo(ctx, number)
if err != nil {
return err
}
stuInfo.Number = number
stuInfo.Name = name
stuInfo.Age = age
stuInfo.Address = address
StudentInfoAsBytes, _ := json.Marshal(stuInfo)
return ctx.GetStub().PutState(number, StudentInfoAsBytes)
}
//获取历史信息
func (s *Student) GetHistory(ctx contractapi.TransactionContextInterface, number string) ([]QueryHistoryResult, error) {
resultsIterator, err := ctx.GetStub().GetHistoryForKey(number)
if err != nil {
return nil, err
}
defer resultsIterator.Close()
//results := []QueryResult{}
//results := make([]QueryResult, 0)
results := make([]QueryHistoryResult, 0)
for resultsIterator.HasNext() {
if queryResponse, err := resultsIterator.Next();err==nil{
res := QueryHistoryResult{}
res.TxId=queryResponse.TxId
res.Value=string(queryResponse.Value)
res.IsDel=strconv.FormatBool(queryResponse.IsDelete)
res.OnChainTime=time.Unix(queryResponse.Timestamp.Seconds,0).Format("2006-01-02 15:04:05")
results= append(results, res)
}
if err!=nil {
return nil,err
}
}
return results, nil
}
func main() {
chaincode, err := contractapi.NewChaincode(new(Student))
if err != nil {
fmt.Printf("Error create fabStudentInfo chaincode: %s", err.Error())
return
}
if err := chaincode.Start(); err != nil {
fmt.Printf("Error starting fabStudentInfo chaincode: %s", err.Error())
}
}