现在CLI
已经准备好了,接下来需要的是编写链码,实现功能
实现链码必要结构
- 新建目录
ChainCode
- 在
ChainCode
目录下新建main.go
在main.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
status:200 表示成功
peer chaincode invoke -n bill -v 0 -c ‘{“Args”:[“queryMyBills”,”123456”]}’ -C myc
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)
}
七个方法只实现了前两个,其他的意思都跟这个差不多,就不多说了