Fabric 票据系统(五) Service

现在CLI,链码都有了,但是还不能将它们联系起来,上一篇调用链码还需要使用命令行来测试,这篇实现通过代码代用链码

一、安装初始化链码

安装、执行链码需要有一个客户端对象并指定链码名称,链码路径和版本,所以需要对FabricSetup结构体进行修改

打开startInit.go文件

修改FabricSetup结构体

type FabricSetup struct {
	//应用配置文件路径
	ConfigFile string
	//通道ID
	ChannelID string
	//sdk是否已初始化过,若已初始化,不再做初始化操作
	Initialized bool
	//通道配置文件路径
	ChannelConfig string
	//组织管理员账户名
	OrgAdmin string
	//组织名
	OrgName string
	//ResourceMgmtClient 使用'github.com/hyperledger/fabric-sdk-go/api/apitxn/resmgmtclient'包下的结构体,不要搞错
	Admin resmgmtclient.ResourceMgmtClient
	sdk   *fabsdk.FabricSDK
	//链码所需参数
	ChaincodeID     string //链码名称
	ChaincodeGoPath string //系统GOPATH路径
	ChaincodePath   string //链码所在路径
	ChaincodeVersion string //链码版本
	//执行链码的用户名
	UserName string
	//chclient.ChannelClient 使用的是 "github.com/hyperledger/fabric-sdk-go/api/apitxn/chclient" 包下的结构体
	Client  chclient.ChannelClient
}

添加方法InstallAndInstantiateCC安装和实例化链码

func (t *FabricSetup) InstallAndInstantiateCC()error {
	fmt.Println("开始安装链码...")
	//对链码进行打包
	ccPkg,err := gopackager.NewCCPackage(t.ChaincodePath,t.ChaincodeGoPath)
	if err != nil {
		return fmt.Errorf("创建指定的链码包失败%s",err.Error())
	}
	//指定安装链码时的所需参数
	installCCRequest := resmgmtclient.InstallCCRequest{
		Name:t.ChaincodeID,
		Path:t.ChaincodePath,
		Version:t.ChaincodeVersion,
		Package:ccPkg,
	}
	//安装链码
	_,err = t.Admin.InstallCC(installCCRequest)
	if err != nil {
		return fmt.Errorf("安装链码失败%s",err.Error())
	}
	fmt.Println("安装链码成功")
	fmt.Println("开始实例化链码...")
	//指定链码策略

	//cauthdsl.SignedByAnyMember 使用 "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/common/cauthdsl"包下的
	ccPolicy := cauthdsl.SignedByAnyMember([]string{"Org1MSP"})
	//指定实例化链码时的所需参数
	instantiateCCReq := resmgmtclient.InstantiateCCRequest{
		Name:t.ChaincodeID,
		Path:t.ChaincodePath,
		Version:t.ChaincodeVersion,
		Args:[][]byte{[]byte("init")},
		Policy:ccPolicy,
	}

	err = t.Admin.InstantiateCC(t.ChannelID,instantiateCCReq)
	if err != nil {
		return fmt.Errorf("实例化链码失败%s",err.Error())
	}
	fmt.Println("实例化链码成功")
	//创建客户端对象,能够通过该对象执行链码查询及事务执行
	t.Client,err = t.sdk.NewClient(fabsdk.WithUser(t.UserName)).Channel(t.ChannelID)
	if err != nil {
		return fmt.Errorf("创建新的通道客户端失败:%s",err.Error())
	}
	fmt.Println("链码安装实例化完成,且成功创建客户端对象")
	return nil
}

二、service

1、准备数据

新建domain.go

package Service

import "zhq/bill/Blockchain"

type FabricSetupService struct {
	Setup *Blockchain.FabricSetup
}
// 票据数据结构
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:"HodrCmID"` // 当前持票人证件号码
	HoldrAcct string `json:"HodrAcct"`  // 当前持票人名称

	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"`
}

  • FabricSetupService结构体存储的是初始化SDK时实例的FabricSetup对象,里面包含通道、节点等一些需要用到的数据

  • Bill结构体与链码中定义的结构体一致

2、调用链码

新建billService.go

  • 发布票据
func (t *FabricSetupService) IssueBill(bill Bill) (string, error) {
	//序列化票据
	b, err := json.Marshal(bill)
	if err != nil {
		return "", fmt.Errorf("指定的票据对象序列化错误:%s",err.Error())
	}
	//指定调用链码时的请求参数
	req := chclient.Request{
		ChaincodeID: t.Setup.ChaincodeID,
		Fcn:         "issue",
		Args:        [][]byte{b},
	}
	//使用实例化链码时创建的客户端对象执行链码
	response,err := t.Setup.Client.Execute(req)
	if err != nil {
		return "",fmt.Errorf("保存票据信息失败:%s",err.Error())
	}
	//返回交易ID和错误信息
	return response.TransactionID.ID,nil
}

  • req := chclient.Request{ ChaincodeID: t.Setup.ChaincodeID, Fcn: “issue”, Args: [][]byte{b}, } 设置请求参数,ChaincodeID:链码ID,Fcn:执行的方法名,Args:指定参数
  • response,err := t.Setup.Client.Execute(req)
    Execute对应的还有Query,一个相当于set一个是get,区别在于会不会生成新的交易
  • 查询用户所有订单
func (t *FabricSetupService)QueryBill(holderCmId string)([]byte,error)  {
	var args []string
	args = append(args,"queryMyBills")
	args = append(args,holderCmId)
	req := chclient.Request{
		ChaincodeID:t.Setup.ChaincodeID,
		Fcn:args[0],
		Args:[][]byte{[]byte(holderCmId)},
	}
	response,err := t.Setup.Client.Query(req)
	if err != nil {
		return nil,fmt.Errorf("根据持票人证件号查询票据失败:%s",err.Error())
	}
	b := response.Payload
	return b[:],nil
}

其余5个方法,跟这两个类似,只需要改Fcn和形参就可以了

三、测试

修改工程目录下的main.go

func main()  {
	setup := Blockchain.FabricSetup{
		//组织内管理员用户
		OrgAdmin:      "Admin",
		//组织ID
		OrgName:       "Org1",
		//通道ID
		ChannelID:     "mychannel",
		//应用配置文件路径
		ConfigFile:    "config.yaml",
		//通道配置文件路径
		ChannelConfig: os.Getenv("GOPATH") + "/src/zhq/bill/fixtures/artifacts/channel.tx",
		//链码相关
		ChaincodeID:     "bill",
		ChaincodeGoPath: os.Getenv("GOPATH"),
		ChaincodePath:   "zhq/bill/ChainCode",
		ChaincodeVersion: "1.0",
		UserName:        "User1",
	}
	err := setup.Initialize()
	if err != nil {
		fmt.Println(err)
	}
	//安装实例化链码
	err = setup.InstallAndInstantiateCC()
	if err != nil {
		fmt.Printf("链码安装实例化发生错误:%s",err.Error())
	}
	serv := new(Service.FabricSetupService)
	serv.Setup = &setup

	//测试数据
	bill := Service.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:   "",
		WaitEndorseAcct:   "",
		RejectEndorseCmID: "",
		RejectEndorseAcct: "",
		State:             "",
		History:           nil,
	}
	response, err := serv.IssueBill(bill)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("交易ID:", response)
	}

	b, err := serv.QueryBill("120xxxxxxx")
	if err != nil {
		fmt.Errorf(err.Error())
	} else {
		var bills = []Service.Bill{}
		json.Unmarshal(b, &bills)
		for _, temp := range bills {
			fmt.Println(temp)
		}
	}

	b, err = serv.QueryBillByNo("123456")
	if err != nil {
		fmt.Errorf(err.Error())
	} else {
		var result Service.Bill
		json.Unmarshal(b, &result)
		fmt.Println(result)
		for _, history := range result.History {

			fmt.Println(history)
		}
	}
	//发起背书
	res, err := serv.Endorse("123456", "pei120xxxxxx", "pei")
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(res)
	}
	//
	b, err = serv.QueryMyWaitBills("pei120xxxxxx")
	if err != nil {
		fmt.Println(err.Error())
	} else {
		var bills = []Service.Bill{}
		json.Unmarshal(b, &bills)
		for _, temp := range bills {
			fmt.Println(temp)
		}
	}
	//签收票据
	res, err = serv.Accept("123456", "pei120xxxxxx", "pei")
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(res)
	}
	//拒签票据
	res, err = serv.Accept("123456", "pei120xxxxxx", "pei")
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(res)
	}
}

cd $GOPATH/src/zhq/bill go build make

如果运行没有问题将会输出

Fabric 票据项目五

源码下载 https://github.com/sweetMegan/bill

感谢韩晓东(老韩)指导

打赏一个呗

取消

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

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

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