关于区块链的公共医疗应用开发
区块链的养老保险平台应用开发
任务一:环境准备
1.编译区块链网络
目录:/root/xuperchain/ 在区块链网络目录下执行make命令,编译网络,编译成功后输出compile done!

启动区块链网络

2.创建钱包账户
创建普通钱包账户userTest,命令如下
bin/xchain-cli account newkeys --output data/userTest

切换到/root/xuperchain/output/data/userTest,查看用户地址

3.向钱包账户转账
//转账
bin/xchain-cli transfer --to (粘贴的地址)--amount 1000000 --keys data/keys/ -H 127.0.0.1:37101

任务二:前端工程编码
1.下载前端工程并解压
wget http://res.zhonghui.vip/blockchain-4/blockchain-device-Part/10/front.zip
unzip front.zip -d front
2.启动前端项目
安装依赖:
cd front
npm install --unsafe-perm

启动:
npm run serve


3.创建数据库
创建test数据库,查看所有数据库
create database test;
show databases;

4.创建数据表
进入test数据库
use test;
创建表
CREATE TABLE `pension_account` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`wallet_address` varchar(255) DEFAULT NULL COMMENT '钱包地址',
`personal_balance` bigint(20) DEFAULT NULL COMMENT '个人账户余额',
`overall_balance` bigint(20) DEFAULT NULL COMMENT '总账户余额',
`is_sponsor` tinyint(1) DEFAULT NULL COMMENT '雇主是否为职工的赞助商',
`insured_person_name` varchar(255) DEFAULT NULL COMMENT '参保人姓名',
`insured_person_identity_card` varchar(255) DEFAULT NULL COMMENT '参保人身份证',
`insured_person_work_unit` varchar(255) DEFAULT NULL COMMENT '参保人工作单位',
`working_year` bigint(20) DEFAULT NULL COMMENT '工作年限',
`salary` bigint(20) DEFAULT NULL COMMENT '薪资',
`payment_base` bigint(20) DEFAULT NULL COMMENT '缴费基数',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='养老账户表';

任务三:编写后端程序
1.构建 Java 项目
打开Intellij IDEA, 点击新建项目,设置项目名为:pension,项目位置:~/Desktop,语言:Java, 构建系统:Maven,JDK: 11, 点击创建。

修改包路径,将[org.example]改为[src],右键选中包名,依次选中【重构】–>【重命名】,重命 名软件包,如下图所示:


2.修改Maven依赖
文件地址: /root/Desktop/pension/pom.xml
修改内容为:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.11</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>pension</groupId>
<artifactId>pension</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>pension</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baidu.xuper</groupId>
<artifactId>xuper-java-sdk</artifactId>
<version>0.3.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
编辑项目配置文件 /root/Desktop/pension/src/main/resources 目录下创建application.properties文件
server.port=8090
spring.datasource.url: jdbc:mysql://localhost:3306/test
spring.datasource.username: root
spring.datasource.password: 123456
spring.datasource.driver-class-name: com.mysql.cj.jdbc.Driver
mybatis.type-aliases-package: src/entity
mybatis.mapper-locations: classpath:/mapper/*.xml
最后将桌面上的合约和编译文件拖入/root/Desktop/pension/src/main/resources目录下,如下图 所示:

使用solc编译合约,进入 /root/Desktop/pension/src/main/resources目录下执行如下命令:
./solc --abi --bin pensionInsurance.sol -o --output-dir

3.编写代码
以下路径均为后端工程 /root/Desktop/pension下的相对路径,完整的项目文件如下图所示。

代码一 代码文件路径:java/src/PensionApplication.java
新建PensionApplication.java文件,实现构建xuperchain客户端, 获取账户,解决跨域请求, 时间格式 化等功能。代码如下:
package src;
import com.baidu.xuper.api.Account;
import com.baidu.xuper.api.XuperClient;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import
org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustom
izer;
import org.springframework.context.annotation.Bean;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
@SpringBootApplication
public class PensionApplication {
@Bean
public XuperClient createXuperClient(){
return new XuperClient("127.0.0.1:37101");
}
@Bean
public Account createAccount(){
return
Account.getAccountFromPlainFile("/root/xuperchain/output/data/userTest");
}
@Bean
public LocalDateTimeSerializer localDateTimeDeserializer() {
return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MMdd HH:mm:ss"));
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer
jackson2ObjectMapperBuilderCustomizer() {
return builder -> builder.serializerByType(LocalDateTime.class,
localDateTimeDeserializer());
}
public static void main(String[] args) {
SpringApplication.run(PensionApplication.class, args);
}
}
代码二 代码文件路径:java/src/Config.java
创建Config.java文件,配置合约名称和账户名称。代码如下
package src;
public class Config {
//每次启动需要更换
public static String CONTRACT_NAME = "Person10100";
public static String ACCOUNT_NAME = "4233456589011002";
}
代码三 代码文件路径:java/src/StartTask.java
创建StartTask.java文件,部署合约并完成调用。 代码如下:
package src;
import com.alibaba.fastjson.JSON;
import com.baidu.xuper.api.Account;
import com.baidu.xuper.api.Transaction;
import com.baidu.xuper.api.XuperClient;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
@Component
public class StartTask {
@Resource
private XuperClient client;
@Resource
private Account account;
@PostConstruct
public void createContract() {
//前面命令行已经给该账户转了钱
BigInteger balance = client.getBalance(account.getAddress());
System.out.println("balance:" + balance);
//创建合约账户, 长度必须是16
Transaction contractAccount = client.createContractAccount(account,
Config.ACCOUNT_NAME);
//转账给合约账户
Transaction transfer = client.transfer(account, "XC" +
Config.ACCOUNT_NAME + "@xuper", BigInteger.valueOf(100), "1");
// 查询余额7BCD3C3BAD8152CB1A2CFBDB55D0AC7F92FF7B19
BigInteger result = client.getBalance("XC" + Config.ACCOUNT_NAME +
"@xuper");
System.out.println("contract balance:" + result);
//设置合约帐号
account.setContractAccount("XC" + Config.ACCOUNT_NAME + "@xuper");
try {
// 合约编译文件
byte[] abi =
Files.readAllBytes(Paths.get("/root/Desktop/pension/src/main/resources/--outputdir/PensionAccount.abi"));
byte[] bin =
Files.readAllBytes(Paths.get("/root/Desktop/pension/src/main/resources/--outputdir/PensionAccount.bin"));
Map<String, String> params = new HashMap<>();
params.put("creator", "XC" + Config.ACCOUNT_NAME + "@xuper");
Transaction tx1 = client.deployEVMContract(account, bin, abi,
Config.CONTRACT_NAME, params);
System.out.println("tx: " + tx1.getContractResponse().getStatus());
} catch (IOException e) {
e.printStackTrace();
}
}
}
代码四 代码文件路径:java/src/mapper/PensionAccountMapper.java
创建PensionAccountMapper.java文件,编写入库接口。代码如下:
package src.mapper;
import src.entity.PensionAccount;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface PensionAccountMapper {
void savePensionAccount(PensionAccount pensionInfo);
}
代码五 代码文件路径:java/src/entity/PensionAcoount.java
创建PensionAcoount.java文件,编写养老保险账户实体类 代码如下:
package src.entity;
public class PensionAccount {
private static final long serialVersionUID = 1L;
private String walletAddress; //钱包地址(walletAddress)
private Long personalBalance; //个人账户余额(personalBalance)
private Long overallBalance; //总账户余额(overallBalance)
private Boolean isSponsor; //雇主是否为职工的赞助商(isSponsor)
private String insuredPersonName; //参保人姓名(insuredPersonName)
private String insuredPersonIdentityCard; //参保人身份证
(insuredPersonIdentityCard)
private String insuredPersonWorkUnit; //参保人工作单位
(insuredPersonWorkUnit)
private Long workingYear; // 工作年限(workingYear)
private Long salary; // 薪资(salary)
private Long paymentBase; //缴费基数(paymentBase)
public String getWalletAddress() {
return walletAddress;
}
public void setWalletAddress(String walletAddress) {
this.walletAddress = walletAddress;
}
public Long getPersonalBalance() {
return personalBalance;
}
public void setPersonalBalance(Long personalBalance) {
this.personalBalance = personalBalance;
}
public Long getOverallBalance() {
return overallBalance;
}
public void setOverallBalance(Long overallBalance) {
this.overallBalance = overallBalance;
}
public Boolean getSponsor() {
return isSponsor;
}
public void setSponsor(Boolean sponsor) {
isSponsor = sponsor;
}
public String getInsuredPersonName() {
return insuredPersonName;
}
public void setInsuredPersonName(String insuredPersonName) {
this.insuredPersonName = insuredPersonName;
}
public String getInsuredPersonIdentityCard() {
return insuredPersonIdentityCard;
}
public void setInsuredPersonIdentityCard(String insuredPersonIdentityCard) {
this.insuredPersonIdentityCard = insuredPersonIdentityCard;
}
public String getInsuredPersonWorkUnit() {
return insuredPersonWorkUnit;
}
public void setInsuredPersonWorkUnit(String insuredPersonWorkUnit) {
this.insuredPersonWorkUnit = insuredPersonWorkUnit;
}
public Long getWorkingYear() {
return workingYear;
}
public void setWorkingYear(Long workingYear) {
this.workingYear = workingYear;
}
public Long getSalary() {
return salary;
}
public void setSalary(Long salary) {
this.salary = salary;
}
public Long getPaymentBase() {
return paymentBase;
}
public void setPaymentBase(Long paymentBase) {
this.paymentBase = paymentBase;
}
}
创建AddPensionAccountResp.java文件,代码如下:
package src.entity;
public class AddPensionAccountResp {
private String address;
private Integer balance;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getBalance() {
return balance;
}
public void setBalance(Integer balance) {
this.balance = balance;
}
}
代码六 代码文件路径:java/src/controller/Controller.java 创建Controller.java文件,完成接口编写。代码如下:
package src.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baidu.xuper.api.*;
import com.baidu.xuper.pb.XchainOuterClass;
import com.google.protobuf.ByteString;
import src.Config;
import src.entity.AddPensionAccountResp;
import src.entity.PensionAccount;
import src.mapper.PensionAccountMapper;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@CrossOrigin(origins="*")
public class Controller {
@Resource
private XuperClient xuperClient;
@Resource
private Account account;
@Resource
private PensionAccountMapper mapper;
@RequestMapping(value = "/getBlockInformation")
public Map<String, Object> getBlockInformation() {
Map<String, Object> resMap = new HashMap<>();
try {
Map<String, Object> data = new HashMap<>();
XchainOuterClass.BCStatus node1 =
xuperClient.getBlockchainStatus("xuper");
XchainOuterClass.InternalBlock block1 = node1.getBlock();
long height = block1.getHeight();
System.out.println("最新区块高度:" + height);
data.put("height", height);
// 获取最新区块交易Hash
List<XchainOuterClass.Transaction> transactionsList =
block1.getTransactionsList();
if (transactionsList.size() > 0) {
ByteString txid = transactionsList.get(transactionsList.size() -
1).getTxid();
System.out.println("最新区块交易Hash:" +
Base64Utils.encodeToString(txid.toByteArray()));
data.put("Hash",
Base64Utils.encodeToString(txid.toByteArray()));
}
resMap.put("code", 200);
resMap.put("message", "请求成功");
resMap.put("data", data);
} catch (Exception e) {
resMap.put("code", 500);
resMap.put("message", "请求失败");
}
return resMap;
}
@RequestMapping(value = "/addPensionAccount")
@ResponseBody
public Map<String, Object> addPensionAccount(@RequestBody PensionAccount
pensionAccount) {
Map<String, Object> resMap = new HashMap<>();
try{
Map<String, String> addPensionAccount = new HashMap<>();
Account account1 = Account.create();
AddressTrans evmAddress =
AddressTrans.xChainToEvmAddress(account1.getAddress());
addPensionAccount.put("walletAddress",evmAddress.getAddr());
addPensionAccount.put("insuredPersonName",pensionAccount.getInsuredPersonName()
);
addPensionAccount.put("insuredPersonIdentityCard",
pensionAccount.getInsuredPersonIdentityCard());
addPensionAccount.put("insuredPersonWorkUnit",
pensionAccount.getInsuredPersonWorkUnit());
addPensionAccount.put("workingYear",
pensionAccount.getWorkingYear().toString());
addPensionAccount.put("salary",
pensionAccount.getSalary().toString());
addPensionAccount.put("paymentBase",
pensionAccount.getPaymentBase().toString());
Transaction getFlightInfo = xuperClient.invokeEVMContract(account,
Config.CONTRACT_NAME, "addPensionAccount", addPensionAccount, BigInteger.ZERO);
ContractResponse contractResponse =
getFlightInfo.getContractResponse();
String bodyStr = contractResponse.getBodyStr();
AddPensionAccountResp resp=buildEntity(bodyStr);
pensionAccount.setWalletAddress(account.getAddress());
mapper.savePensionAccount(pensionAccount);
resMap.put("code", 200);
resMap.put("message", "请求成功");
resMap.put("data", resp);
} catch (Exception e){
resMap.put("code", 500);
resMap.put("message", "请求失败");
}
return resMap;
}
private AddPensionAccountResp buildEntity(String bodyStr){
AddPensionAccountResp addPensionAccountResp = new
AddPensionAccountResp();
JSONArray objects = JSONObject.parseArray(bodyStr);
String address = ((JSONObject)objects.get(0)).get(0).toString();
Integer balance=
Integer.decode(((JSONObject)objects.get(1)).get(1).toString());
addPensionAccountResp.setAddress(address);
addPensionAccountResp.setBalance(balance);
return addPensionAccountResp;
}
}
代码七 代码文件路径:resources/mapper/PensionAccountMapper.xml 创建PensionAccountMapper.xml文件,编写入库sql。代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="src.mapper.PensionAccountMapper">
<insert id="savePensionAccount">INSERT INTO pension_account
(wallet_address, personal_balance, overall_balance, is_sponsor,
insured_person_name, insured_person_identity_card, insured_person_work_unit,
working_year, salary, payment_base)
VALUES
(#{walletAddress}, #{personalBalance}, #{overallBalance}, #{sponsor}, #
{insuredPersonName}, #{insuredPersonIdentityCard}, #{insuredPersonWorkUnit}, #
{workingYear}, #{salary}, #{paymentBase})
</insert>
</mapper>
4.引入依赖
右侧导航栏-Maven-重新加载所有Maven项目

5.启动项目
目录: java/src/PensionApplication.java

任务四:测试
1.测试 getBlockInformation 接口
浏览器访问:http://127.0.0.1:8090/getBlockInformation
2.测试前端页面
浏览器访问:http://172.25.103.2:8080/

