Fabric 票据系统(四) 链码

现在CLI已经准备好了,接下来需要的是编写链码,实现功能

实现链码必要结构

  • 新建目录ChainCode
  • ChainCode目录下新建main.gomain.go中实现链码的必要结构Init(),Invoke()

新建个链码结构体

type BillChainCode struct {

}

Init()

func (t *BillChainCode) Init(stub shim.ChaincodeStubInterface) peer.Response {
	return shim.Success(nil)
}

Invoke()


func (t *BillChainCode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
	function, args := stub.GetFunctionAndParameters()
	//票据操作的七个方法
	if function == "issue" {
		//发布票据
		return t.Issue(stub, args)
	}else if function == "queryMyBills" {
		//查看我的票据列表
		return t.QuerMyBills(stub, args)
	}else if function == "queryBillByNo" {
		//票据号查询票据
		return t.QueryBillByNo(stub, args)
	}else  if function == "queryMyWaitBills" {
		//查询我的待背书票据列表
		return t.QueryMyWaitBills(stub, args)
	}else if function == "endorse" {
		//发起背书
		return t.endorse(stub, args)
	}else if function == "accept" {
		//签名
		return t.Accept(stub, args)
	}else if function == "reject" {
		//拒签
		return t.Reject(stub, args)
	}

	return shim.Error("指定的函数名称错误")
}

启动链码

func main()  {
	chaincode := new(BillChainCode)
	err := shim.Start(chaincode)
	if err != nil {
		fmt.Println("启动链码错误: ", err)
	}
}

实现票据操作

在这之前首先创建票据结构体和一些常量必须票据状态 新建stucture.go

package main

// 票据状态
const (
	BillInfo_State_NewPublish	= "NewPublish"		// 新发布票据
	BillInfo_State_EndorseWaitSign = "EndorseWaitSign"	// 等待签收票据
	BillInfo_State_EndorseSigned = "EndorseSigned"		// 票据签收成功
	BillInfo_State_EndorseReject = "EndorseReject"		// 拒绝签收票据
)
const (
	IndexName   = "holderName~billNo" // search的映射名
)

// 票据数据结构
type Bill struct {
	BillInfoID			string		`json:"BillInfoID"`			// 票据号码
	BillInfoAmt			string		`json:"BillInfoAmt"`		// 票据金额
	BillInfoType		string		`json:"BillInfoType"`		// 票据类型

	BillInfoIsseDate	string		`json:"BillInfoIsseDate"`	// 票据出票日期
	BillInfoDueDate		string		`json:"BillInfoDueDate"`	// 票据到期日期

	DrwrCmID			string		`json:"DrwrCmID"`			// 出票人证件号码
	DrwrAcct			string		`json:"DrwrAcct"`			// 出票人名称

	AccptrCmID			string		`json:"AccptrCmID"`			// 承兑人证件号码
	AccptrAcct			string		`json:"AccptrAcct"`			// 承兑人名称

	PyeeCmID			string		`json:"PyeeCmID"`			// 收款人证件号码
	PyeeAcct			string		`json:"PyeeAcct"`			// 收款人名称

	HoldrCmID			string		`json:"HoldrCmID"`		// 当前持票人证件号码
	HoldrAcct			string		`json:"HoldrAcct`		// 当前持票人名称

	WaitEndorseCmID	string		`json:"WaitEndorseCmID"`	// 待背书人证件号码
	WaitEndorseAcct	string		`json:"WaitEndorseAcct"`	// 待背书人名称

	RejectEndorseCmID	string		`json:"RejectEndorseCmID"`	// 拒绝背书人证件号码
	RejectEndorseAcct	string		`json:"RejectEndorseAcct"`	// 拒绝背书人名称

	State				string		`json:"State"`				// 票据状态
	History				[]HistoryItem	`json:"History"`		// 票据背书历史
}

// 票据历史信息
type HistoryItem struct {
	TxId		string		`json:"TxId"`
	Bill		Bill		`json:"bill"`
}

数据结构有了,下面处理逻辑

新建billcc.go

发布票据 issue.go

1、检查请求参数是否合法


//args 为票据的json串
func (t *BillChainCode)Issue(stub shim.ChaincodeStubInterface,args []string)peer.Response  {
//1、检查请求参数是否合法
	if len(args) != 1 {
		return shim.Error("发布票据失败,指定的票据内容错误")
	}
	var bill Bill
	err := json.Unmarshal([]byte(args[0]),&bill)
	if err != nil {
		return shim.Error("反序列化票据对象时发生错误")
	}
}

2、查重 票据具有唯一性,不允许重复发布票据 根据票据号查询票据,如果票据已存在,不允许重复发布

//根据订单号查询订单是否存在
func (t *BillChainCode)getBill(stub shim.ChaincodeStubInterface,billNo string)(Bill,bool)  {
	var bill Bill
	b,err := stub.GetState(billNo)
	if err != nil {
		return bill,false
	}
	//判断查询到的结果是否为空
	err = json.Unmarshal(b,&bill)
	if err != nil {
		return bill,false
	}
	return bill,true
}

3、修改票据状态

//args 为票据的json串
func (t *BillChainCode)Issue(stub shim.ChaincodeStubInterface,args []string)peer.Response  {
	//1、检查请求参数是否合法
	if len(args) != 1 {
		return shim.Error("发布票据失败,指定的票据内容错误")
	}
	var bill Bill
	err := json.Unmarshal([]byte(args[0]),&bill)
	if err != nil {
		return shim.Error("反序列化票据对象时发生错误")
	}
	//2、查重
	//票据具有唯一性,不允许重复发布票据
	//根据票据号查询票据,如果票据已存在,不允许重复发布
	_,bl := t.getBill(stub,bill.BillInfoID)
	if bl {
		return shim.Error("发布的票据已存在")
	}
	//3、将票据状态保存为发布状态
	bill.State = BillInfo_State_NewPublish
	//4、将票据保存至账本
}

4、保存票据

//保存票据
func (t *BillChainCode)putBill(stub shim.ChaincodeStubInterface,bill Bill)([]byte,bool)  {
	b,err := json.Marshal(bill)
	if err != nil {
		return nil,false
	}
	err = stub.PutState(bill.BillInfoID,b)
	if err != nil {
		return nil,false
	}
	return b,true
	
}

5、根据当前持票人ID与票据号码,定义复合key,方便后期批量查询

//args 为票据的json串
func (t *BillChainCode)Issue(stub shim.ChaincodeStubInterface,args []string)peer.Response  {
	//1、检查请求参数是否合法
	if len(args) != 1 {
		return shim.Error("发布票据失败,指定的票据内容错误")
	}
	var bill Bill
	err := json.Unmarshal([]byte(args[0]),&bill)
	if err != nil {
		return shim.Error("反序列化票据对象时发生错误")
	}
	//2、查重
	//票据具有唯一性,不允许重复发布票据
	//根据票据号查询票据,如果票据已存在,不允许重复发布
	_,bl := t.getBill(stub,bill.BillInfoID)
	if bl {
		return shim.Error("发布的票据已存在")
	}
	//3、将票据状态保存为发布状态
	bill.State = BillInfo_State_NewPublish
	//4、将票据保存至账本
	_,bl = t.putBill(stub,bill)
	if !bl {
		return shim.Error("保存票据信息时发生错误")
	}
	//5、根据当前持票人ID与票据号码,定义复合key,方便后期批量查询
	holderCmIDBillIInfoIDIndexKey, err := stub.CreateCompositeKey(IndexName,[]string{bill.HoldrCmID,bill.BillInfoID})
	if err != nil {
		return shim.Error("创建复合键时发生错误")
	}
	err = stub.PutState(holderCmIDBillIInfoIDIndexKey,[]byte{0x00})
	if err != nil {
		return shim.Error("保存复合键时发生错误")
	}
	return shim.Success([]byte("指定的票据发布成功"))

}

查询我的票据列表

1、依据复合键查询,一个人的所有票据

//根据票据持有人ID,查询这个人的所有票据
func (t *BillChainCode) QueryMyBills(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 1 {
		return shim.Error("查询票据错误,非法的持票人号码")
	}
	iterator, err := stub.GetStateByPartialCompositeKey(IndexName, args)
	if err != nil {
		return shim.Error("根据指定的持票人证件号码查询信息时发生错误")
	}
	defer iterator.Close()
	//迭代处理
	var bills []Bill
	for iterator.HasNext() {
		kv, _ := iterator.Next()
		//kv
		//k:bill.HoldrCmID+bill.BillInfoID 
		//v:[]byte{0x00}
		//所以要找到订单号要拆分k就够了
		//分割查询到的复合键
		_, compositeKey, err := stub.SplitCompositeKey(kv.Key)
		if err != nil {
			return shim.Error("分割指定复合键时发生错误")
		}
		//从复合键中获取到的票据号码
		bill, bl := t.getBill(stub, compositeKey[1])
		if !bl {
			return shim.Error("根据指定的票据号码查询票据信息时发生错误")
		}
		//将查询到的订单,添加到查询结果数组中
		bills = append(bills, bill)
	}
	bs, err := json.Marshal(bills)
	if err != nil {
		return shim.Error("序列化票据时发生错误")
	}
	return shim.Success(bs)
}

这里需要注意的是package 包名为 main 因为链码是可以单独运行的

测试

将测试数据

	//测试数据
	bill := Bill{
		BillInfoID:        "123456",
		BillInfoAmt:       "10",
		BillInfoType:      "liangpiao",
		BillInfoIsseDate:  "20180702",
		BillInfoDueDate:   "20190702",
		DrwrCmID:          "120xxxxxxx",
		DrwrAcct:          "zhq",
		AccptrCmID:        "accp120xxxxx",
		AccptrAcct:        "AccpName",
		PyeeCmID:          "Pyee120xxxxx",
		PyeeAcct:          "PyeeName",
		HoldrCmID:         "120xxxxxxx",
		HoldrAcct:         "zhq",
		WaitEndorseCmID:   "pei120xxxxxx",
		WaitEndorseAcct:   "pei",
		RejectEndorseCmID: "",
		RejectEndorseAcct: "",
		State:             "",
		History:           nil,
	}
	

copy到Issue方法中,就不需要在终端输入那么多东西了

ChainCode目录拷贝到hyfa/fabric-samples/chaincode目录下

使用开发模式测试链码

发布票据:

peer chaincode invoke -n bill -v 0 -c ‘{“Args”:[“issue”,””]}’ -C myc

Fabric 票据项目四 1

status:200 表示成功

peer chaincode invoke -n bill -v 0 -c ‘{“Args”:[“queryMyBills”,”123456”]}’ -C myc

Fabric 票据项目四 2

status:200 表示成功 payload:为查询结果

//票据操作的七个方法
	if function == "issue" {
		//发布票据
		return t.Issue(stub, args)
	}else if function == "queryMyBills" {
		//查看我的票据列表
		return t.QuerMyBills(stub, args)
	}else if function == "queryBillByNo" {
		//票据号查询票据
		return t.QueryBillByNo(stub, args)
	}else  if function == "queryMyWaitBills" {
		//查询我的待背书票据列表
		return t.QueryMyWaitBills(stub, args)
	}else if function == "endorse" {
		//发起背书
		return t.endorse(stub, args)
	}else if function == "accept" {
		//签名
		return t.Accept(stub, args)
	}else if function == "reject" {
		//拒签
		return t.Reject(stub, args)
	}

七个方法只实现了前两个,其他的意思都跟这个差不多,就不多说了

源码下载

感谢韩晓东(老韩)指导

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦