2024-09-16
区块链学习笔记
00
请注意,本文编写于 215 天前,最后修改于 210 天前,其中某些信息可能已经过时。

目录

区块链商店系统开发实操(设计流程图+区块链搭建+智能合约+后端)
一. 前置准备
二.设计PetStore应用的注册与登录原型
1.设计注册与登录的流程图
2.设计注册与登录的原型
三.开发PetStore宠物领养商店系统的智能合约
1.启动联盟链节点与WeBASE-Front
2.编写智能合约
四.PetStore 区块链宠物领养商店系统的后端代码编写
1.开发注册与登录功能
2.开发宠物领养与查看功能
下载

区块链商店系统开发实操(设计流程图+区块链搭建+智能合约+后端)

注意

注意 本文章仅为作者的学习记录。

概述
掌握区块链宠物领养商店系统的智能合约和后端开发。

一. 前置准备

概述
熟悉区块链宠物领养商店实验案例的流程。
任务步骤
1.阅读以下内容并熟悉实验的整体流程
实验采用前后端开发相关技术(后端采用Java+Springboot,MVC架构),基于实验“区块链应用原型设计“以及实验“外部应用程序与区块链交互”作为开发基础,在实现与FISCOBCOS交互的基础之上开发PetStore去中心化的“宠物领养商店”系统,其中涉及功能包括:登录、注册、领养、查看4个内容,如下图所示。

image.png
为了简化应用开发流程,本实验简化了应用开发中涉及的“中心化”,应用中涉及的 用户登录和用户注册部分的功能将通过智能合约中实现,如下图为整体应用框架。

image.png 项目流程:

image.png

二.设计PetStore应用的注册与登录原型

概述
直接采用区块链账户实现PetStore应用的注册和登录。在本任务中将设计基于“去中心化的注册与登录流程”。掌握使用ProcessOn完成开发应用系统的注册与登录流程设计。
任务步骤

1.设计注册与登录的流程图

(1)登录ProcessOn网站(https://www.processon.com/) 设计如下的注册流程图,首先通过WeBASE-Front创建测试账户,再复制测试账户地址在注册界面填写,点击确认后完成注册跳转至登录页。

image.png

(2)设计如下登录流程,在登录界面输入区块链账户地址的用户信息,判断登录用户 是否已经存在,如果存在则用户可登录至“主页”。

image.png

(3)设计整体注册和登录流程。

image.png

2.设计注册与登录的原型

(1)登录墨刀(https://modao.cc) ,参照区块链宠物商店系统设计原型,修改注册 页面原型。

image.png (2)参照区块链宠物商店系统设计原型,修改登录界面原型。

image.png
(3)参照“应用需求分析与设计”,给“注册页”和“登录页”中的按钮添加对应事件的跳转、至指定页面。

三.开发PetStore宠物领养商店系统的智能合约

概述
用Solidity 语言完成Adoption智能合约的开发。
任务步骤

1.启动联盟链节点与WeBASE-Front

相关信息

相关信息 如果没搭建联盟链 请参考该文章 FISCO-BCOS 2.0搭建教程

(1)打开终端,该步骤在区块链供应链金融平台搭建与运维案例中已经讲解过,不再赘述。
(2)启动fiscobcos的单群祖四节点联盟链;
(2)进入WeBASE-Front所在的文件夹,并启动WeBASE-Front。
(3)登录WeBASE-Front,打开火狐浏览器,输入网址 http://localhost:5002/WeBASE-Front ,选择右上角的中文进行切换。

image.png

2.编写智能合约

(1)进入WeBASE-Front 的”合约IDE“,新建一个Adoption.sol的合约文件,可以使用命令"ctrl+-"缩放浏览器页面,确保可以看到完整代码。如下图所示。

image.png

(2)分析合约要是实现的功能
根据区块链宠物领养系统前置的设计需求,首先要能实现登录和注册的功能,领养宠物以及查看领养宠物的信息。在这之前先完成合约的初始化,即如下所示的宠物领养者的个数,以及领养宠物的账户地址与Index的映射,存放领养者地址的数组等。

image.png (3)用户注册,即根据用户的账户地址,实现用户编号以及与地址的映射关系,并返回账户的编号userIndex。此方法的公开性为public。

image.png

(4)用户登录判定,即根据用户账户地址是否存在判定登录,并返回其账户地址映射的账户编号。此方法的公开性为public,用view进行修饰。

image.png

(5)领养宠物,根据前面的设计需求必须要能在登录后进行宠物领养。具体包含的功 能如下图所示。公开性为public。

image.png (6)查看领养者领养的宠物信息,即返回数组。 image.png (7)编译,部署合约,如下图所示得到编译后的代码信息,合约地址,名称,ABI以及bytecode。在编译部署之前需添加测试用户test。

image.png 然后编译部署,选择test用户进行部署。如下图所示。

image.png

四.PetStore 区块链宠物领养商店系统的后端代码编写

概述
利用WeBASE-Front API 功能开发PetStore 应用注册与登录功能、宠物领养与查看功能,并进行相关验证。基于Adoption智能合约中login和register方法,在PetStore 后端项目中添加对应的登录和注册功能以及宠物领养和查看功能。 任务步骤

1.开发注册与登录功能

(1)创建项目

image.png

image.png

pom.xml 中导入依赖

xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- Springdoc OpenAPI --> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-ui</artifactId> <version>1.6.14</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.22</version> </dependency> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-models</artifactId> <version>1.5.22</version> </dependency> </dependencies>

(2)下载依赖 image.png

(3)创建OpenApiConfig.java类文件。
选中目录文件config,右键新建java类,名字为OpenApiConfig.java

java
import org.springdoc.core.GroupedOpenApi; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class OpenApiConfig { @Bean public GroupedOpenApi publicApi() { return GroupedOpenApi.builder().group("public").pathsToMatch("/**").build(); } }

(4)完成WeBASEUtils.java类文件。
在utils 目录下新建一个Java类文件,取名为WeBASEUtils.java,首先复制以下代码到文件中。此方法是添加调用WeBASE-Front中编译部署后的智能合约的统一方法。此防范需要的具体参数包括用户地址userAddress,名称,参数,合约ABI,合约名字以及合约地址。

java
import cn.hutool.core.lang.Dict; import cn.hutool.http.Header; import cn.hutool.http.HttpRequest; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.List; @Service public class WeBASEUtils { @Value("${webase.url}") String url; public Dict commonReq(String userAddress, String contactName, String contractAddress, String funcName, List<Object> funcParam, String ABI ){ //将ABI转换成JSON数组 JSONArray abiJSON = JSONUtil.parseArray(ABI); //创建请求数据 JSONObject data = JSONUtil.createObj(); data.set("groupId","1"); //使用群组 data.set("user",userAddress); //用户地址 data.set("contractName",contactName); //合约名字 data.set("contractAddress",contractAddress); //合约地址 data.set("funcName",funcName); //调用合约内的函数 data.set("funcParam",funcParam); // 传入的参数 data.set("contractAbi",abiJSON); //调用ABI data.set("version","v1.0"); data.set("useAes",false); data.set("useCns",false); data.set("cnsName","Adoption"); //请求数据转换成字符串 String dataString = JSONUtil.toJsonStr(data); //发送请求 String requestBody = HttpRequest.post(url). header(Header.CONTENT_TYPE, "application/json") .body(dataString) .execute() .body(); Dict retDict = new Dict(); retDict.set("result",requestBody); return retDict; } }

(5)修改配置文件。
打开application.properties 文件,因为 channel_port的起始端口号为20200,因此,我修改请求地址为:127.0.0.1:20200,组ID为1(如果为2即需要修改),其次是证书地址以及十六进制表示的私钥。之后SpringBoot的配置信息,复制以下代码。如下图所示。

image.png 此时需要补充完整请求的channel地址和合约地址(查看WeBASE-Front中自己 部署的合约地址)。

properties
### ### Springboot server config ###启动端口 server.port=8080 server.session.timeout=60 banner.charset=UTF-8 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=GMT+8 ### 请求地址 webase.url = http://192.168.59.50:5002/WeBASE-Front/trans/handle ### Required channel地址 system.Peers = 192.168.59.50:20200 ### Required system.groupId = 1 ### Optional. Default will search conf,config,src/main/conf/src/main/config system.certPath=conf,config,src/main/resources/conf,src/main/resources/config ### Optional. If don't specify a random private key will be used system.hexPrivateKey=14a704e5fd66a92049179e0c06bf778f51e84a96f0bc19f91b6773d46da25f65 ### Optional. Please fill this address if you want to use related service 部署的合约地址 system.contract.adoptionAddress=0xb7007b3ad877753c825833cfae4ce22e56516525

注意:合约的ABI要进行替换,打开resources/abi/目录下的Adoption.abi文件,复制WeBASE-Front中的合约ABI,粘贴到文件中即可,如下图所示。

image.png (6)创建UserService的类文件。
在service目录下新建一个UserService的类文件,此方法定义了用户登录和注册的具体执行逻辑。复制以下代码到文件中。这里需要自己创建一个IOUtil类编写readResourceAsString方法。

java
import cn.hutool.core.lang.Dict; import cn.hutool.core.util.HexUtil; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import top.suhm.petstore.utils.IOUtil; import top.suhm.petstore.utils.WeBASEUtils; import java.util.ArrayList; import java.util.List; @Service public class UserService { @Value("${system.contract.adoptionAddress}") String contractAddress; static final String ABI; static { ABI = top.suhm.petstore.utils.IOUtil.readResourceAsString("abi/Adoption.abi"); if (ABI == null) { throw new IllegalStateException("ABI 文件未找到: abi/Adoption.abi"); } } @Autowired WeBASEUtils weBASEUtils; public Dict register(String userAddress) { List funcParam = new ArrayList(); funcParam.add(userAddress); Dict result = weBASEUtils.commonReq(userAddress, "Adoption", contractAddress, "register", funcParam, ABI); String resultStr = result.getStr("result"); JSONObject respBody = JSONUtil.parseObj(resultStr); System.out.println(respBody); String output = (String) respBody.get("output"); long resInt = HexUtil.hexToLong(output.substring(2)); result.set("result",resInt); return result; } public Dict login(String userAddress){ List funcParam = new ArrayList(); funcParam.add(userAddress); Dict result = weBASEUtils.commonReq(userAddress, "Adoption", contractAddress, "login", funcParam, ABI); JSONArray requestBody = JSONUtil.parseArray(result.get("result")); if(requestBody.size() > 0){ result.set("result",requestBody.get(0)); } return result; } }

(7)创建UserController.java 文件
在controller 目录下新建一个UserController的类文件,复制以下代码到文件中。包含用户注册与登录的接口。都是通过地址进行操作。

java
import cn.hutool.core.lang.Dict; import io.swagger.annotations.Api; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import top.suhm.petstore.service.UserService; @Api(value = "用户 Controller", tags = {"User Api"}) @RestController @RequestMapping("/user") public class UserController { @Autowired UserService userService; @GetMapping("/register") public Dict register(@RequestParam("address") String address){ return userService.register(address); } @GetMapping("/login") public Dict login(@RequestParam("address") String address){ return userService.login(address); } }

(8)接口测试

至此,我们就完成了用户的登录和注册的基本逻辑,接下来需要进行接口测试。 首先点击运行按钮运行程序,即run“Application”。如下图所示。

image.png

打开浏览器,访问地址http://localhost:8080/swagger-ui.html ,如果正常即会出现如下图所示的Swagger接口文档。

image.png

测试注册register接口
点击 Try it out

image.png

随意复制一个测试地址

image.png

点击 Execute 进行测试接口

image.png

注册成功后尝试登录,将地址复制到login接口的address处,点击Tryitout!

image.png

2.开发宠物领养与查看功能

(1)开发宠物领养服务(adopt),查看可被领养服务。
假设目前有8个宠物可供领养,编号为0-7,那么就通过petId对宠物进行标识。回到IDEA,在service 包中创建MyAdoptionService.java 文件并在MyAdoptionService.java 中添加如下代码,具体代码如下(可去掉注释)。类实现中包含了宠物的领养adopt函数,查询宠物领养情况的listPetAdoption函数,以及已经领养宠物情况的listAdoptedPet函数。如下图所示。

java
import cn.hutool.core.lang.Dict; import cn.hutool.core.util.HexUtil; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import top.suhm.petstore.utils.WeBASEUtils; import java.util.ArrayList; import java.util.List; @Service public class MyAdoptionService { @Autowired WeBASEUtils weBASEUtils; @Value("${system.contract.adoptionAddress}") String contractAddress; public final static String ABI; static { ABI = top.suhm.petstore.utils.IOUtil.readResourceAsString("abi/Adoption.abi"); if (ABI == null) { throw new IllegalStateException("ABI 文件未找到: abi/Adoption.abi"); } } public Dict adopt(String userAddress, Integer petId) { if (petId < 0 || petId > 7) { Dict result = new Dict(); result.set("code", 500); result.set("result", "宠物编号不合规"); return result; } List funcParam = new ArrayList(); funcParam.add(petId); Dict result = weBASEUtils.commonReq(userAddress, "Adoption", contractAddress, "adopt", funcParam, ABI); JSONObject requestBody = JSONUtil.parseObj(result.get("result")); String output = (String) requestBody.get("output"); long resInt = HexUtil.hexToLong(output.substring(2)); result.set("result", resInt); if (resInt != petId) { result.set("code", resInt); } else { result.set("code", 200); } return result; } public Dict listPetAdoption(String userAddress) { JSONArray jsonArray2 = getPetAdoptAddresses(userAddress); List petStatusList = new ArrayList(); for (Object item : jsonArray2) { String parseAddress = (String) item; if (parseAddress.equals("0x0000000000000000000000000000000000000000")) { petStatusList.add(0); } else { petStatusList.add(1); } } Dict result = new Dict(); result.set("code",200); result.set("result",petStatusList); return result; } public Dict listAdoptedPet(String userAddress){ JSONArray jsonArray2 = getPetAdoptAddresses(userAddress); List petStatusList = new ArrayList(); for (Object item : jsonArray2) { String parseAddress = (String)item; if(parseAddress.equals(userAddress)){ petStatusList.add(1); }else{ petStatusList.add(0); } } Dict result = new Dict(); result.set("code",200); result.set("result",petStatusList); return result; } public JSONArray getPetAdoptAddresses(String userAddress) { Dict result = weBASEUtils.commonReq(userAddress, "Adoption", contractAddress, "getAdoptions", new ArrayList<>(), ABI); JSONArray jsonArray1 = JSONUtil.parseArray(result.get("result")); JSONArray jsonArray2 = JSONUtil.parseArray((jsonArray1.get(0))); return jsonArray2; } }

(2)创建MyAdoptionController.java 接口文件
在controller 目录创建MyAdoptionController.java 的接口文件,复制以下代码到文件中。其中,在接口文件中实现调用函数的方法。具体包括adopt函数进行宠物领养方法的调用,以及查询宠物领养情况的listPetAdoption函数的方法调用,已经领养宠物情况的listAdoptedPet函数的方法调用。

java
import cn.hutool.core.lang.Dict; import io.swagger.annotations.Api; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import top.suhm.petstore.service.MyAdoptionService; @Api(value = "领养 Controller", tags = {"领养 Api"}) @RestController @RequestMapping("myAdoption") public class MyAdoptionController { @Autowired MyAdoptionService myAdoptionService; @GetMapping("/adopt") public Dict adopt(@RequestParam("userAddress") String userAddress, @RequestParam("petId") Integer petId){ return myAdoptionService.adopt(userAddress,petId); } @GetMapping("/listPetAdoption") public Dict listPetAdoption(@RequestParam("userAddress") String userAddress){ return myAdoptionService.listPetAdoption(userAddress); } @GetMapping("/listAdoptedPet") public Dict listAdoptedPet(@RequestParam("userAddress") String userAddress){ return myAdoptionService.listAdoptedPet(userAddress); } }

(3)测试
重启服务 打开浏览器访问http://localhost:8080/swagger-ui.html 地址,点击最后一个领养API 的Expand Operations按钮。

image.png 正常即显示code200,表示成功领养一个宠物。

接着测试宠物领养情况的API image.png 可以看到petId为1的宠物已经被领养,这里可以看到所有宠物领养情况。 至此,宠物领养实战案例的智能合约和后端就都开发完成了。

下载

后端代码下载: PetStore
前端代码下载:PetStore-Front
合约代码下载:PetStore-contract

本文作者:苏皓明

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!