在上一小節對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())
}
}