注意
注意 本文章仅为作者的学习记录。
概述:
掌握区块链宠物领养商店系统的智能合约和后端开发。
概述:
熟悉区块链宠物领养商店实验案例的流程。
任务步骤:
1.阅读以下内容并熟悉实验的整体流程
实验采用前后端开发相关技术(后端采用Java+Springboot,MVC架构),基于实验“区块链应用原型设计“以及实验“外部应用程序与区块链交互”作为开发基础,在实现与FISCOBCOS交互的基础之上开发PetStore去中心化的“宠物领养商店”系统,其中涉及功能包括:登录、注册、领养、查看4个内容,如下图所示。
为了简化应用开发流程,本实验简化了应用开发中涉及的“中心化”,应用中涉及的
用户登录和用户注册部分的功能将通过智能合约中实现,如下图为整体应用框架。
项目流程:
概述:
直接采用区块链账户实现PetStore应用的注册和登录。在本任务中将设计基于“去中心化的注册与登录流程”。掌握使用ProcessOn完成开发应用系统的注册与登录流程设计。
任务步骤:
(1)登录ProcessOn网站(https://www.processon.com/) 设计如下的注册流程图,首先通过WeBASE-Front创建测试账户,再复制测试账户地址在注册界面填写,点击确认后完成注册跳转至登录页。
(2)设计如下登录流程,在登录界面输入区块链账户地址的用户信息,判断登录用户 是否已经存在,如果存在则用户可登录至“主页”。
(3)设计整体注册和登录流程。
(1)登录墨刀(https://modao.cc) ,参照区块链宠物商店系统设计原型,修改注册 页面原型。
(2)参照区块链宠物商店系统设计原型,修改登录界面原型。
(3)参照“应用需求分析与设计”,给“注册页”和“登录页”中的按钮添加对应事件的跳转、至指定页面。
概述:
用Solidity 语言完成Adoption智能合约的开发。
任务步骤:
相关信息
相关信息 如果没搭建联盟链 请参考该文章 FISCO-BCOS 2.0搭建教程
(1)打开终端,该步骤在区块链供应链金融平台搭建与运维案例中已经讲解过,不再赘述。
(2)启动fiscobcos的单群祖四节点联盟链;
(2)进入WeBASE-Front所在的文件夹,并启动WeBASE-Front。
(3)登录WeBASE-Front,打开火狐浏览器,输入网址 http://localhost:5002/WeBASE-Front ,选择右上角的中文进行切换。
(1)进入WeBASE-Front 的”合约IDE“,新建一个Adoption.sol的合约文件,可以使用命令"ctrl+-"缩放浏览器页面,确保可以看到完整代码。如下图所示。
(2)分析合约要是实现的功能。
根据区块链宠物领养系统前置的设计需求,首先要能实现登录和注册的功能,领养宠物以及查看领养宠物的信息。在这之前先完成合约的初始化,即如下所示的宠物领养者的个数,以及领养宠物的账户地址与Index的映射,存放领养者地址的数组等。
(3)用户注册,即根据用户的账户地址,实现用户编号以及与地址的映射关系,并返回账户的编号userIndex。此方法的公开性为public。
(4)用户登录判定,即根据用户账户地址是否存在判定登录,并返回其账户地址映射的账户编号。此方法的公开性为public,用view进行修饰。
(5)领养宠物,根据前面的设计需求必须要能在登录后进行宠物领养。具体包含的功 能如下图所示。公开性为public。
(6)查看领养者领养的宠物信息,即返回数组。
(7)编译,部署合约,如下图所示得到编译后的代码信息,合约地址,名称,ABI以及bytecode。在编译部署之前需添加测试用户test。
然后编译部署,选择test用户进行部署。如下图所示。
概述:
利用WeBASE-Front API 功能开发PetStore 应用注册与登录功能、宠物领养与查看功能,并进行相关验证。基于Adoption智能合约中login和register方法,在PetStore 后端项目中添加对应的登录和注册功能以及宠物领养和查看功能。
任务步骤:
(1)创建项目
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)下载依赖
(3)创建OpenApiConfig.java类文件。
选中目录文件config,右键新建java类,名字为OpenApiConfig.java
javaimport 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,合约名字以及合约地址。
javaimport 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的配置信息,复制以下代码。如下图所示。
此时需要补充完整请求的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,粘贴到文件中即可,如下图所示。
(6)创建UserService的类文件。
在service目录下新建一个UserService的类文件,此方法定义了用户登录和注册的具体执行逻辑。复制以下代码到文件中。这里需要自己创建一个IOUtil类编写readResourceAsString方法。
javaimport 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的类文件,复制以下代码到文件中。包含用户注册与登录的接口。都是通过地址进行操作。
javaimport 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”。如下图所示。
打开浏览器,访问地址http://localhost:8080/swagger-ui.html ,如果正常即会出现如下图所示的Swagger接口文档。
测试注册register接口
点击 Try it out
随意复制一个测试地址
点击 Execute 进行测试接口
注册成功后尝试登录,将地址复制到login接口的address处,点击Tryitout!
(1)开发宠物领养服务(adopt),查看可被领养服务。
假设目前有8个宠物可供领养,编号为0-7,那么就通过petId对宠物进行标识。回到IDEA,在service 包中创建MyAdoptionService.java 文件并在MyAdoptionService.java 中添加如下代码,具体代码如下(可去掉注释)。类实现中包含了宠物的领养adopt函数,查询宠物领养情况的listPetAdoption函数,以及已经领养宠物情况的listAdoptedPet函数。如下图所示。
javaimport 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函数的方法调用。
javaimport 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按钮。
正常即显示code200,表示成功领养一个宠物。
接着测试宠物领养情况的API
可以看到petId为1的宠物已经被领养,这里可以看到所有宠物领养情况。
至此,宠物领养实战案例的智能合约和后端就都开发完成了。
后端代码下载: PetStore
前端代码下载:PetStore-Front
合约代码下载:PetStore-contract
本文作者:苏皓明
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!