├── EcommerceStore.sol ├── Migrations.sol ├── README.md ├── app.js └── index.html /EcommerceStore.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.13; 2 | 3 | contract EcommerceStore { 4 | enum ProductStatus { Open, Sold, Unsold } 5 | enum ProductCondition { New, Used } 6 | 7 | struct Product { 8 | uint id; 9 | string name; 10 | string category; 11 | string imageLink; 12 | string descLink; 13 | uint auctionStartTime; 14 | uint auctionEndTime; 15 | uint startPrice; 16 | address highestBidder; 17 | uint highestBid; 18 | uint secondHighestBid; 19 | uint totalBids; 20 | ProductStatus status; 21 | ProductCondition condition; 22 | } 23 | 24 | mapping (address => mapping(uint => Product)) stores; 25 | mapping (uint => address) productIdInStore; 26 | uint public productIndex; 27 | 28 | function EcommerceStore() public { 29 | productIndex = 0; 30 | } 31 | 32 | function addProductToStore(string _name, string _category, string _imageLink, 33 | string _descLink, uint _auctionStartTime, 34 | uint _auctionEndTime, uint _startPrice, uint _productCondition) public { 35 | require (_auctionStartTime < _auctionEndTime); 36 | productIndex += 1; 37 | Product memory product = Product(productIndex, _name, _category, _imageLink, _descLink, _auctionStartTime, _auctionEndTime, 38 | _startPrice, 0, 0, 0, 0, ProductStatus.Open, ProductCondition(_productCondition)); 39 | 40 | stores[msg.sender][productIndex] = product; 41 | productIdInStore[productIndex] = msg.sender; 42 | } 43 | 44 | function getProduct(uint _productId) view public returns (uint, string, string, string, string, uint, uint, uint, ProductStatus, ProductCondition) { 45 | Product memory product = stores[productIdInStore[_productId]][_productId]; 46 | return (product.id, product.name, product.category, product.imageLink, product.descLink, product.auctionStartTime, 47 | product.auctionEndTime, product.startPrice, product.status, product.condition); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ethereum-dapp-ipfs-node.js-mongodb 2 | 以太坊开发DApp实战教程——用区块链、星际文件系统(IPFS)、Node.js和MongoDB来构建电商平台 3 | 4 | [http://xc.hubwiz.com/course/5abbb7acc02e6b6a59171dd6](http://xc.hubwiz.com/course/5abbb7acc02e6b6a59171dd6/?affid=20180330github) 5 | 6 | ### 一、引言 7 | * 1.1 课程简介 8 | * 1.2 去中心化,Why? 9 | * 1.3 课程项目概述 10 | * 1.4 基础知识要求 11 | * 1.5 应用架构及区块链概述 12 | * 1.6 理解架构组件的作用 13 | * 1.7 敏捷开发 14 | ### 二、 电商智能合约:商品上架与商品信息读取 15 | * 2.1 冲刺目标 16 | * 2.2 项目初始化 17 | * 2.3 商品数据结构 18 | * 2.4 商品目录表 19 | * 2.5 商品上架 20 | * 2.6 查看商品信息 21 | * 2.7 部署电商智能合约 22 | * 2.8 控制台交互测试 23 | * 2.9 脚本交互测试 24 | * 2.10 模拟数据生成脚本 25 | ### 三、 电商合约:商品竞价与开标 26 | * 3.1 冲刺目标 27 | * 3.2 拍卖概述 28 | * 3.3 去中心化的维科瑞拍卖 29 | * 3.4 出价信息的数据结构 30 | * 3.5 提交密封出价 31 | * 3.6 揭示真实出价 32 | * 3.7 获取竞价结果 33 | * 3.8 控制台与脚本交互测试 34 | ### 四、 初识IPFS:星际文件系统 35 | * 4.1 IPFS:去中心化的文件系统 36 | * 4.2 IPFS节点软件安装与设置 37 | * 4.3 IPFS网络的文件上传与下载 38 | ### 五、 用户界面:商品展示页 39 | * 5.1 冲刺目标 40 | * 5.2 前端开发概述 41 | * 5.3 前端入口脚本 42 | * 5.4 商品展示网页 43 | * 5.5 webpack配置、构建与测试运行 44 | * 5.6 商品数据的提取与渲染 45 | ### 六、 用户界面:商品上架页 46 | * 6.1 冲刺目标 47 | * 6.2 商品上架流程 48 | * 6.3 商品数据采集 49 | * 6.4 上传资源到IPFS 50 | * 6.5 上传商品信息到区块链 51 | ### 七、用户界面:商品详情页 52 | * 7.1 冲刺目标 53 | * 7.2 商品详情页 54 | * 7.3 渲染商品详情 55 | * 7.4 出价表单 56 | * 7.5 揭示出价表单 57 | ### 八、 托管合约:多重签名托管 58 | * 8.1 冲刺目标 59 | * 8.2 为什么需要托管合约 60 | * 8.3 托管合约的状态设计 61 | * 8.4 释放资金给卖家 62 | * 8.5 返还资金给买家 63 | ### 九、 托管资金管理 64 | * 9.1 冲刺目标 65 | * 9.2 托管阶段概述 66 | * 9.3 电商合约:结束拍卖方法 67 | * 9.4 商品详情页:结束拍卖表单 68 | * 9.5 商品详情页:显示拍卖结果 69 | * 9.6 电商合约:封装托管合约访问接口 70 | * 9.7 商品详情页:托管信息显示 71 | * 9.8 用户界面:资金流向投票 72 | ### 十、 链下数据存储 73 | * 10.1 冲刺目标 74 | * 10.2 为什么需要链下存储 75 | * 10.3 MongoDB概述 76 | * 10.4 Mongoose概述 77 | * 10.5 商品模型的架构定义 78 | * 10.6 express概述 79 | * 10.7 Solidity事件 80 | * 10.8 监听商品上架事件 81 | * 10.9 商品查询API 82 | * 10.10 渲染商品展示页 83 | ### 十一、 课程总结 84 | * 11.1 部署简明方案 85 | * 11.2 进一步学习 86 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | // Import the page's CSS. Webpack will know what to do with it. 2 | import "../stylesheets/app.css"; 3 | 4 | // Import libraries we need. 5 | import { default as Web3} from 'web3'; 6 | import { default as contract } from 'truffle-contract' 7 | 8 | // Import our contract artifacts and turn them into usable abstractions. 9 | import metacoin_artifacts from '../../build/contracts/MetaCoin.json' 10 | 11 | // MetaCoin is our usable abstraction, which we'll use through the code below. 12 | var MetaCoin = contract(metacoin_artifacts); 13 | 14 | // The following code is simple to show off interacting with your contracts. 15 | // As your needs grow you will likely need to change its form and structure. 16 | // For application bootstrapping, check out window.addEventListener below. 17 | var accounts; 18 | var account; 19 | 20 | window.App = { 21 | start: function() { 22 | var self = this; 23 | 24 | // Bootstrap the MetaCoin abstraction for Use. 25 | MetaCoin.setProvider(web3.currentProvider); 26 | 27 | // Get the initial account balance so it can be displayed. 28 | web3.eth.getAccounts(function(err, accs) { 29 | if (err != null) { 30 | alert("There was an error fetching your accounts."); 31 | return; 32 | } 33 | 34 | if (accs.length == 0) { 35 | alert("Couldn't get any accounts! Make sure your Ethereum client is configured correctly."); 36 | return; 37 | } 38 | 39 | accounts = accs; 40 | account = accounts[0]; 41 | 42 | self.refreshBalance(); 43 | }); 44 | }, 45 | 46 | setStatus: function(message) { 47 | var status = document.getElementById("status"); 48 | status.innerHTML = message; 49 | }, 50 | 51 | refreshBalance: function() { 52 | var self = this; 53 | 54 | var meta; 55 | MetaCoin.deployed().then(function(instance) { 56 | meta = instance; 57 | return meta.getBalance.call(account, {from: account}); 58 | }).then(function(value) { 59 | var balance_element = document.getElementById("balance"); 60 | balance_element.innerHTML = value.valueOf(); 61 | }).catch(function(e) { 62 | console.log(e); 63 | self.setStatus("Error getting balance; see log."); 64 | }); 65 | }, 66 | 67 | sendCoin: function() { 68 | var self = this; 69 | 70 | var amount = parseInt(document.getElementById("amount").value); 71 | var receiver = document.getElementById("receiver").value; 72 | 73 | this.setStatus("Initiating transaction... (please wait)"); 74 | 75 | var meta; 76 | MetaCoin.deployed().then(function(instance) { 77 | meta = instance; 78 | return meta.sendCoin(receiver, amount, {from: account}); 79 | }).then(function() { 80 | self.setStatus("Transaction complete!"); 81 | self.refreshBalance(); 82 | }).catch(function(e) { 83 | console.log(e); 84 | self.setStatus("Error sending coin; see log."); 85 | }); 86 | } 87 | }; 88 | 89 | window.addEventListener('load', function() { 90 | // Checking if Web3 has been injected by the browser (Mist/MetaMask) 91 | if (typeof web3 !== 'undefined') { 92 | console.warn("Using web3 detected from external source. If you find that your accounts don't appear or you have 0 MetaCoin, ensure you've configured that source properly. If using MetaMask, see the following link. Feel free to delete this warning. :) http://truffleframework.com/tutorials/truffle-and-metamask") 93 | // Use Mist/MetaMask's provider 94 | window.web3 = new Web3(web3.currentProvider); 95 | } else { 96 | console.warn("No web3 detected. Falling back to http://127.0.0.1:9545. You should remove this fallback when you deploy live, as it's inherently insecure. Consider switching to Metamask for development. More info here: http://truffleframework.com/tutorials/truffle-and-metamask"); 97 | // fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail) 98 | window.web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:9545")); 99 | } 100 | 101 | App.start(); 102 | }); 103 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |