概述
我们通过几个示例项目来阐述一个完整的Dapp开发过程我们通过三个项目实例来看下商品管理项目
通讯录项目
投票系统项目
我们这里详细以商品管理项目来阐述整个开发过程写在最后的是我们的项目代码示例仓库以太坊商品管理项目
1)简单的设计
商品管理(简单示例)
商品登记查询上架/下架
2)相关智能合约的编写
// 指定编译环境版本的pragma solidity >=0.4.21 <0.6.0;/*** owned 是合约的管理者*/contract owned {address public owner; // 变量账户地址/*** 初始化构造函数* 部署到以太坊节点会被执行一次* 把部署者的账户地址赋值给一个变量值* 这个变量值会被存储到区块链的账本中*/contructor() public {owner = msg.sender;}/*** 这里是一个函数修改器,用于限制方法的行为* 判断当前合约的调用者是否是合约的所有者* 必须使用当时部署合约的地址才可以调用*/modifier onlyOwner {require(msg.sender == owner, "sender is not authorized");_;}/*** 合约的所有者指派一个新的管理员* 也就是更改管理员地址* @param newOwner address 新的管理账户地址*/function transferOwnership(address newOwner) public onlyOwner {if (newOwner != address(0)) {owner = newOwner;}}}/** 接下来是业务逻辑的合约 **/// 这里继承了前面权限管理的合约contract Commodity is owned {// 这个结构体包含了商品的各个属性struct Comminfo {string account_name; // 商品名称string code; // 商品编号string number; // 商品数量uint8 status; // 1是上架,2是下架}// 商品信息不止登记一条// 所有商品信息保存在这个mapping这个映射中// 记录所有数据映射mapping(uint => Comminfo) commof;// 整个商品信息有多少条的统计uint[] lengths;// 表明有多少条商品信息登记了// 获取长度function getLength() public view returns (uint len) {return lengths.length;}// 这里是一个登记的方法,也是保存// 在这个方法里包含的各个参数属性// 相当于在mapping状态变量里面继续新增一条商品信息记录// 实现新增功能function saveinfo(string memory account_names,string memory codes,string memory numbers,uint8 status statusw) public {uint les = lengths.length;commOf[les].account_name = account_names;commOf[les].code = codes;commOf[les].number = numbers;commOf[les].status = statusw;lengths.push(les);}// 查询数据function selectAll(uint key) public view returns (string memory account_name,string memory code,string memory number,uint8 status,uint id) {account_name = commOf[key].account_name;code = commOf[key].code;number = commOf[key].number;status = commOf[key].status;id = key;return (account_name, code, number, status, id);}// 判断商品信息是否已经存在,重复性判断// 两个string比较function utilCompareInternal(string memory a, string memory b) internal pure returns (bool succ) {if(bytes(a).length != bytes(b).length) {return false;}for (uint i = 0; i< bytes(a).length; i ++) {if(bytes(a)[i] != bytes(b)[i]) {return false;}}return true;}// 根据商品代码获取商品信息function selectOne(uint key, string memory code) public view returns (bool result) {return utilCompareInternal(commOf[key].code, code);}// 设置商品的上架、下架状态function update(uint key) public {if(commOf[key].status == 1) {commOf[key].status = 2;} else {commOf[key].status = 1;}}}
3 )nodejs后端
项目文件:/app/src/index.js
import "../css/index.css"; // 前端样式,与前端交互的css文件import Web3 from "web3"; // 用于与以太坊节点通信import metaCoinArtifact from "../../build/contracts/Commodity.json"; // 引入合约的ABI文件// 关键代码片段save: async function() {const {web3} = this;// 商品代码var codes = document.geElementById('codes').value// 名称var name = document.geElementById('name').value// 数量var count = document.geElementById('count').value// 上下架var status = document.geElementById('switch').value// 为空校验if(!code || !name || !count) {alert("Parameter cannot be null!");return}const {saveinfo} = this.meta.methods;await saveinfo(name, codes, count, status).send({from: this.account, gas: 3141592}, function() {location.reload();});}
4 )web前端
这里使用最简单的方式来编码
<!-- 关键代码 伪代码 --><script src="引入jQuery"></script><link rel='stylesheet' type='text/css' href='引入样式文件'><script src="index.js"></script><button onclick="App.update()">上架/下架</button><button onclick="App.selectOne()">查询</button><div class='content'><button onclick="App.save()">保存</button><label>商品代码: <input type='text' id="codes" /></label><label>商品名称: <input type='text' id="name" /></label><label>商品数量: <input type='text' id="count" /></label><label>上下架:<select id="switch"><option value="1">上架</option><option value="2">下架</option></select></label></div>