文章目錄
- 前言
- 一、核心功能
- 1.頁面展示
- 1.抽獎介面
- 3.定時觸發智能合約抽獎,并自動公布結果
- 4.專案整體代碼地址
- 總結
前言
上一章節我們已經撰寫了核心的預言機合約,并且部署測驗成功,現在我們就來寫基于區塊鏈的自動抽獎系統DAPP,java語言開發,專案功能比較少,所以采用框架springboot+thymeleaf,
一、核心功能
1.頁面展示
頁面展示主要是顯示實時的投票排名的串列資訊,還有就是對抽獎中獎資訊的展示,核心代碼如下:
<div class="detail-title"></div>
<div class="time">基于區塊鏈的自動抽獎系統</div>
<div class="time">1月11日——1月24日投票貢獻排行榜,投票結束!當博主進入top100,會觸發智能合約,系統自動抽獎一位用戶中獎88現金紅包</div>
<div class="time"><a href="https://bss.csdn.net/m/topic/blog_star2020/detail?username=ws327443752" >點我進入投票</a></div>
<div class="time" th:text="${userName}"></div>
</div>
<div class="vote-leaderboard-wrapper page-container">
<div class="title"></div>
<ul id="voteLeaderboardList">
<li class="best-blogger-wrapper" th:each="cs ,csStat: ${lst}">
<div class="left">
<span class="text" th:text="${cs.number}"></span>
<span class="text" th:text="${cs.name}"></span>
</div>
<div class="right">
<span class="code-age" th:text="碼齡+${cs.codeLevel}+年"></span>
<span class="vote-num"th:text="中獎概率 +${cs.vote} +'/'+ ${sum}"></span>
</div>
</li>
</ul>
</div>
/**
* 展示頁面
* @param request
* @return
*/
@RequestMapping("blockchain")
public String index(HttpServletRequest request){
csdnService.getCsdnContent();
request.setAttribute("lst",csdnService.csdnList);
request.setAttribute("sum",csdnService.sum);
request.setAttribute("userName",csdnService.userName);
return "2020csdn";
}
/**
* 獲取csdn投票相關資料
*/
public void getCsdnContent() {
csdnList=new ArrayList<Csdn>();
sum=0;
Map<String, Object> params = new HashMap<String, Object>();
params.put("username" , "ws327443752");
params.put("page" , "1");
params.put("pageSize" , "200");
String json = HttpUtil.post("https://bss.csdn.net/m/topic/blog_star2020/getRanking", params);
JSONObject jsonObject= JSONUtil.parseObj(json);
JSONArray ja= jsonObject.getJSONArray("data");
for (int i = 0; i < ja.size(); i++) {
Csdn csdn=new Csdn();
JSON jsonStr = (JSON) ja.get(i);
String uName = jsonStr.getByPath("nick_name").toString();
csdn.setName(uName);
String voteNum = jsonStr.getByPath("voteNum").toString();
csdn.setVote(Integer.parseInt(voteNum));
sum+=csdn.getVote();
String level = jsonStr.getByPath("level").toString();
csdn.setLevel(Integer.parseInt(level));
String codeLevel = jsonStr.getByPath("codeLevel").toString();
csdn.setCodeLevel(Integer.parseInt(codeLevel));
int number = Integer.parseInt(jsonStr.getByPath("number").toString());
csdn.setNumber(number);
csdnList.add(csdn);
}
}
1.抽獎介面
抽獎介面主要是保證投票數和中獎率的正比關系,代碼如下所示:
/**
* 抽獎介面
* @return
*/
@RequestMapping("lottery")
@ResponseBody
public String lottery(){
String id = csdnService.handleLottery();
return id;
}
/**
* 抽獎方法
* @param orignalRates 商品中獎概率串列,保證順序和實際物品對應
* @return 中獎商品索引
*/
public int lottery(List<Double> orignalRates) {
if (orignalRates == null || orignalRates.isEmpty()) {
return -1;
}
int size = orignalRates.size();
// 計算總概率,這樣可以保證不一定總概率是1
double sumRate = 0d;
for (double rate : orignalRates) {
sumRate += rate;
}
// 計算每個物品在總概率的基礎下的概率情況
List<Double> sortOrignalRates = new ArrayList<>(size);
Double tempSumRate = 0d;
for (double rate : orignalRates) {
tempSumRate += rate;
sortOrignalRates.add(tempSumRate / sumRate);
}
// 根據區塊值來獲取抽取到的物品索引
double nextDouble = Math.random();
sortOrignalRates.add(nextDouble);
Collections.sort(sortOrignalRates);
return sortOrignalRates.indexOf(nextDouble);
}
/**
* 觸發抽獎邏輯
* @return
*/
public String handleLottery() {
List<NameDTO> nameDTOS = new ArrayList<>();
for (int i = 0; i < csdnList.size(); i++) {
double f1 = new BigDecimal((float)csdnList.get(i).getVote()/sum).setScale(4, BigDecimal.ROUND_HALF_UP).doubleValue();
nameDTOS.add(new NameDTO(i + 1, csdnList.get(i).getName(), csdnList.get(i).getNumber() + "", f1));
}
// 存盤概率
List<Double> orignalRates = new ArrayList<>(nameDTOS.size());
for (NameDTO gift : nameDTOS) {
double probability = gift.getProbability();
if (probability < 0) {
probability = 0;
}
orignalRates.add(probability);
}
// 統計
Map<Integer, Integer> count = new HashMap<>();
// 次數
double num = 1;
for (int i = 0; i < num; i++) {
int orignalIndex = lottery(orignalRates);
Integer value = count.get(orignalIndex);
count.put(orignalIndex, value == null ? 1 : value + 1);
}
String id=null;
for (Map.Entry<Integer, Integer> entry : count.entrySet()) {
id=nameDTOS.get(entry.getKey()).getNo();
}
return id;
}
3.定時觸發智能合約抽獎,并自動公布結果
這里是結合寶塔的定時器進行時間的調度,不單獨在專案中進行時間調度作業,注意我們這里的url為bclottery_wa7ndmvt2,相當于加了一個密碼,來定時觸發智能合約抽獎,并自動公布結果,如下圖所示:

我們寶塔設定指定的時間來訪問url觸發智能合約,寶塔安裝教程參考: https://blog.csdn.net/ws327443752/article/details/108134423
/**
* 定時觸發智能合約抽獎,并自動公布結果
* 這里是結合寶塔的定時器進行時間的調度,不單獨在專案中進行時間調度作業,注意我們這里的url為bclottery_wa7ndmvt2,相當于加了一個密碼
* @return
*/
@RequestMapping("bclottery_wa7ndmvt2")
@ResponseBody
public void bcLottery(HttpServletRequest request){
// 先判斷是否進入top100
if(csdnService.getRanking()) {
// 觸發智能合約抽獎
String res = csdnService.handle("request");
if (res.contains("transactionHash")) {
// 獲取合約結果
res = csdnService.handle("get");
// 拿到結果進行對比后進行頁面公布
res = csdnService.getUserNameById(Integer.parseInt(res));
csdnService.userName = "中獎用戶為:"+res;
}
}else {
csdnService.userName="博主未進top100,沒有觸發抽獎合約,本次抽獎失效!";
}
}
/**
* 執行合約
*
* @param param 合約引數
* @return
*/
public String handle(String param) {
String url =WEBASE_FRONT_URL+"/WeBASE-Front/trans/handle";
String body = "{\n" +
" \"user\":\"0xf0d04e0cc9b16528207027f1d5020e402096b44e\",\n" +
" \"contractName\":\"APISampleOracle\",\n" +
" \"contractAddress\":\"0x5ffbf18cfbe8c5b8fee09ccde4f5165007a6043e\",\n" +
" \"funcName\":\"" + param + "\",\n" +
" \"contractAbi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"oracleAddress\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"Fulfilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"Requested\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"EXPIRY_TIME\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_requestId\",\"type\":\"bytes32\"},{\"internalType\":\"int256\",\"name\":\"_result\",\"type\":\"int256\"}],\"name\":\"__callback\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"checkIdFulfilled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"getById\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getUrl\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"request\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"result\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_url\",\"type\":\"string\"}],\"name\":\"setUrl\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}] ,\n" +
" \"groupId\" :\"1\",\n" +
" \"useCns\": false\n" +
"}";
String res = HttpUtil.post(url, body);
if (param.equals("get")) {
// 處理格式
res = res.replace("[", "").replace("]", "").replace("000000000000000000", "");
}
return res;
}
/**
* 通過用戶id獲取用戶名(csdn)
* @param id 用戶的排名id
* @return
*/
public String getUserNameById(int id) {
String[] name = {null};
csdnList.stream().filter(csdn -> csdn.getNumber() == id).forEach(csdn -> {
name[0] = csdn.getName();
});
return name[0];
}
/**
* 獲取排名,看看是否進top100
* @return
*/
public boolean getRanking() {
String html = HttpUtil.get("http://csdn.itrhx.com/");
Document doc = Jsoup.parse(html);
Elements els = doc.getElementsByTag("tr");
for (int i=0;i<els.size();i++
) {
if (els.get(i).html().contains("ws327443752")&&i<=100) {
return true;
}
}
return false;
}
下圖是模擬測驗的抽獎結果展示:


如果合約被定時執行了,那么我們可以在Truora中看到如下圖所示的資訊:

4.專案整體代碼地址
https://gitee.com/bckj_1/csdn
總結
我們的抽獎的專案核心就是抽獎的邏輯和頁面的展示以及最關鍵的智能合約的呼叫,到本篇文章就完結了,專案代碼已經開源到了gitee, 方便大家研究,有興趣的小伙伴可以關注一下!轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/274457.html
標籤:區塊鏈
上一篇:CyberVein(CVT)登陸Coinsuper交易所 助力智慧城市通證經濟合規化
下一篇:Golang開發-包宣告和包引入
