1
0
Code Issues Pull Requests Packages Projects Releases Wiki Activity GitHub Gitee

[门禁端&后端&小程序] 完成门禁端;完成小程序扫码跳转页面;完成后端获取不限制的小程序场景码接口

This commit is contained in:
程序员小墨 2022-11-28 18:36:52 +08:00
parent 42e3952b83
commit 8640c2b413
20 changed files with 365 additions and 38 deletions

View File

@ -3,4 +3,6 @@
更多: 更多:
完成项目代码中的 TODO 部分 完成项目代码中的 TODO 部分
身份码后端接口考虑与其他系统的集成逻辑 身份码后端接口考虑与其他系统的集成逻辑
Java代码中小程序AppID、密钥处理小程序代码中小程序AppID处理

View File

@ -98,6 +98,11 @@
<artifactId>fastjson2</artifactId> <artifactId>fastjson2</artifactId>
</dependency> </dependency>
<!-- OkHttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
</dependencies> </dependencies>

View File

@ -1,11 +1,15 @@
package com.cxyxiaomo.epp.access.controller; package com.cxyxiaomo.epp.access.controller;
import com.cxyxiaomo.epp.access.pojo.UnlimitedQRCodeParam;
import com.cxyxiaomo.epp.access.service.WeChatTokenServiceImpl; import com.cxyxiaomo.epp.access.service.WeChatTokenServiceImpl;
import com.cxyxiaomo.epp.common.response.Res; import com.cxyxiaomo.epp.common.response.Res;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@Controller @Controller
@ -21,4 +25,24 @@ public class WeChatTokenController {
String accessToken = weChatTokenService.getAccessToken(); String accessToken = weChatTokenService.getAccessToken();
return Res.success(accessToken); return Res.success(accessToken);
} }
@GetMapping(value = "/getUnlimitedQRCode", produces = MediaType.IMAGE_PNG_VALUE)
@ResponseBody
@SneakyThrows
public byte[] getUnlimitedQRCode(@RequestParam(value = "envVersion", required = false, defaultValue = "develop") String envVersion,
@RequestParam(value = "page", required = false, defaultValue = "pages/index/index") String page,
@RequestParam(value = "autoColor", required = false, defaultValue = "false") Boolean autoColor,
@RequestParam(value = "isHyaline", required = false, defaultValue = "false") Boolean isHyaline,
@RequestParam(value = "scene", required = true) String scene) {
String accessToken = weChatTokenService.getAccessToken();
UnlimitedQRCodeParam unlimitedQRCodeParam = new UnlimitedQRCodeParam();
unlimitedQRCodeParam.setScene(scene);
unlimitedQRCodeParam.setPage(page);
unlimitedQRCodeParam.setEnvVersion(envVersion);
unlimitedQRCodeParam.setAutoColor(autoColor);
unlimitedQRCodeParam.setIsHyaline(isHyaline);
okhttp3.ResponseBody responseBody = weChatTokenService.getUnlimitedQRCodeFromApi(accessToken, unlimitedQRCodeParam);
return responseBody.bytes();
}
} }

View File

@ -0,0 +1,35 @@
package com.cxyxiaomo.epp.access.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@NoArgsConstructor
@Accessors(chain = true) // 链式写法
public class UnlimitedQRCodeParam implements Serializable {
// 最大32个可见字符只支持数字大小写英文以及部分特殊字符!#$&'()*+,/:;=?@-._~
private String scene;
// 页面 page
private String page = "pages/index/index";
// 检查 page 是否存在
private Boolean checkPath = true;
// 要打开的小程序版本
// 正式版为 "release"体验版为 "trial"开发版为 "develop"默认是正式版
private String envVersion;
// 二维码的宽度单位 px最小 280px最大 1280px
private Integer width = 430;
// 自动配置线条颜色如果颜色依然是黑色则说明不建议配置主色调默认 false
private Boolean autoColor;
// 是否需要透明底色 true 生成透明底色的小程序默认是false
private Boolean isHyaline;
}

View File

@ -1,14 +1,20 @@
package com.cxyxiaomo.epp.access.service; package com.cxyxiaomo.epp.access.service;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.cxyxiaomo.epp.access.dao.AccessDao; import com.cxyxiaomo.epp.access.dao.AccessDao;
import com.cxyxiaomo.epp.access.pojo.UnlimitedQRCodeParam;
import com.cxyxiaomo.epp.access.utils.RestUtil; import com.cxyxiaomo.epp.access.utils.RestUtil;
import com.cxyxiaomo.epp.common.pojo.Setting; import com.cxyxiaomo.epp.common.pojo.Setting;
import lombok.SneakyThrows;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@Service @Service
public class WeChatTokenServiceImpl implements WeChatTokenService { public class WeChatTokenServiceImpl implements WeChatTokenService {
@ -20,8 +26,12 @@ public class WeChatTokenServiceImpl implements WeChatTokenService {
final String SETTING_KEY = "wechat_access_token"; final String SETTING_KEY = "wechat_access_token";
// 小程序信息 // 小程序信息
final String APPID = "wxa70348746d2b562f"; // 小程序测试号
final String APPSECRET = "7da57703136fefb0584a5a6ab1aca152"; // final String APPID = "wxa70348746d2b562f";
// final String APPSECRET = "7da57703136fefb0584a5a6ab1aca152";
// 小程序个人号
final String APPID = "wx332e2e578f09873a";
final String APPSECRET = "ebcd79d303e37983d691fe1dfdfb8818";
@Override @Override
public String getAccessToken() { public String getAccessToken() {
@ -74,11 +84,35 @@ public class WeChatTokenServiceImpl implements WeChatTokenService {
String jsonStr = RestUtil.doGetRequest(url, headers); String jsonStr = RestUtil.doGetRequest(url, headers);
return JSONObject.parseObject(jsonStr); return JSONObject.parseObject(jsonStr);
} }
@SneakyThrows
public ResponseBody getUnlimitedQRCodeFromApi(String accessToken, UnlimitedQRCodeParam unlimitedQRCodeParam) {
// refer: https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/qr-code/getUnlimitedQRCode.html
String url = String.format("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s", accessToken);
//添加参数
Map<String, Object> params = new HashMap<>();
params.put("scene", unlimitedQRCodeParam.getScene());
params.put("page", unlimitedQRCodeParam.getPage());
params.put("check_path", unlimitedQRCodeParam.getCheckPath());
params.put("env_version", unlimitedQRCodeParam.getEnvVersion());
params.put("width", unlimitedQRCodeParam.getWidth());
params.put("auto_color",unlimitedQRCodeParam.getAutoColor());
params.put("is_hyaline",unlimitedQRCodeParam.getIsHyaline());
String paramsString = JSON.toJSONString(params);
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.addHeader("content-type", "application/json")
.url(url)
.post(RequestBody.create(paramsString, MediaType.parse("application/json; charset=utf-8")))
.build();
Response response = okHttpClient.newCall(request).execute();
// String result = response.body()response.body().string();
// System.out.println("headers: " + response.headers());
// System.out.println("body: " + response.body());
System.out.println("paramsString: " + paramsString);
return response.body();
}
} }
/*
{
"success": true,
"msg": "操作成功",
"data": "{\"access_token\":\"63_ez5i6A4ib1-kwi-g6n_3LLqdthenpZGIHTs1tXXcYqia8mnntJlTc_Mkl8CtuiXx0oplHbEVXrkVycvNwkyXhbA2Yj5K1kCan-AjOjH_-zaleYfg-S3zDWO9uoUQWVbACALRQ\",\"expires_in\":7200}"
}
*/

View File

@ -108,19 +108,26 @@
<scope>runtime</scope> <scope>runtime</scope>
<version>${mysql.version}</version> <version>${mysql.version}</version>
</dependency> </dependency>
<!-- druid --> <!-- Druid -->
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>druid</artifactId> <artifactId>druid</artifactId>
<version>${druid.version}</version> <version>${druid.version}</version>
</dependency> </dependency>
<!-- mybatis --> <!-- MyBatis -->
<dependency> <dependency>
<groupId>org.mybatis.spring.boot</groupId> <groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId> <artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version> <version>2.2.2</version>
</dependency> </dependency>
<!-- OkHttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.10.0</version>
</dependency>
<!-- Fastjson --> <!-- Fastjson -->
<dependency> <dependency>
<groupId>com.alibaba.fastjson2</groupId> <groupId>com.alibaba.fastjson2</groupId>

View File

@ -0,0 +1,65 @@
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
color: white;
}
.container {
width: 100%;
height: 100%;
background-color: #0556c6;
display: grid;
grid-template-columns: 3fr 4fr;
/* align-items: center; */
place-items: center;
/* justify-items: center; */
/* place-content: center; */
}
.left-container {
text-shadow: 2px 2px 2px #00000066;
}
h1 {
margin-bottom: 30px;
font-size: 4vw;
}
h3 {
margin-bottom: 10px;
margin-top: 22px;
font-size: 2vw;
}
p {
font-size: 1.4vw;
line-height: 1.5em;
}
.right-container {
}
#qrcode {
box-shadow: 0px 0px 17px 12px rgb(0 0 0 / 50%);
border-radius: 50%;
display: block;
width: min(40vw, 68vh);
height: min(40vw, 68vh);
}
#refreshTimeCountDown {
text-align: center;
margin-top: 30px;
font-size: 2em;
color: #ffffff9c;
}
/* #fullscreen-button {
position: absolute;
right: 0;
top: 0;
} */

View File

@ -3,17 +3,36 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --> <!-- https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'"> <!-- <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'"> -->
<title>Hello World!</title> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src *; img-src *; connect-src *;">
<title>社区疫情防控系统 - 门禁端</title>
<link rel="stylesheet" href="./index.css" />
</head> </head>
<body> <body>
<h1>Hello World!</h1> <div class="container">
We are using Node.js <span id="node-version"></span>, <div class="left-container">
Chromium <span id="chrome-version"></span>, <h1>进出社区请扫码</h1>
and Electron <span id="electron-version"></span>.
<script src="./renderer.js"></script> <h3>社区居民</h3>
<p>1. 打开 微信 > 扫一扫,扫描右侧小程序码</p>
<p>2. 点击确认进入,门即开启</p>
<h3>外来访客</h3>
<p>1. 打开 微信 > 扫一扫,扫描右侧小程序码</p>
<p>2. 填写进入申请表</p>
<h3>长期租客</h3>
<p>1. 请联系管理员为你添加进出权限</p>
</div>
<div class="right-container">
<img id="qrcode" src="">
<p id="refreshTimeCountDown"></p>
</div>
</div>
<!-- <button id="fullscreen-button">全屏</button> -->
<script src="./renderer.js" type="module"></script>
</body> </body>
</html> </html>

View File

@ -1 +1,24 @@
// alert('111'); const url = "https://epp.only4.work/access/wechat/getUnlimitedQRCode";
const page = "pages/index/index"; // "pages/scan/entrance";
const envVersion = "develop"; // 正式版为 "release",体验版为 "trial",开发版为 "develop"
const autoColor = true;
const isHyaline = false;
const image = document.getElementById('qrcode');
const refreshTimeCountDown = document.getElementById('refreshTimeCountDown');
let i = 0, refreshTime = 10 + 1;
function updateQRCode() {
if (i % refreshTime == 0) {
let scene = encodeURIComponent(`guard&${Date.now()}`);
image.src = `${url}?page=${page}&scene=${scene}&envVersion=${envVersion}&autoColor=${autoColor}&isHyaline=${isHyaline}`;
console.log(image.src);
refreshTimeCountDown.innerHTML = `&nbsp;`
} else {
refreshTimeCountDown.textContent = `${refreshTime - i}秒后刷新`
}
i = i % refreshTime + 1;
}
updateQRCode();
setInterval(updateQRCode, 1000);

View File

@ -6,16 +6,23 @@ console.log(`This platform is ${platform}`);
const createWindow = () => { const createWindow = () => {
const mainWindow = new BrowserWindow({ const mainWindow = new BrowserWindow({
width: 800, width: 1080,
height: 600, minWidth: 760,
height: 720,
minHeight: 480,
webPreferences: { webPreferences: {
preload: path.join(__dirname, 'preload.js') preload: path.join(__dirname, 'preload.js')
}, },
}) })
mainWindow.loadFile('html/index.html') mainWindow.loadFile('html/index.html')
// mainWindow.fullScreen = true;
//配置ESC键退出全屏
globalShortcut.register('ESC', () => {
mainWindow.setFullScreen(false);
})
mainWindow.webContents.openDevTools() // mainWindow.webContents.openDevTools()
} }
app.whenReady().then(() => { app.whenReady().then(() => {

View File

@ -1,10 +1,13 @@
window.addEventListener('DOMContentLoaded', () => { // import { remote } from 'electron'
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const dependency of ['chrome', 'node', 'electron']) { // const setFullScreen = remote.getCurrentWindow().setFullScreen
replaceText(`${dependency}-version`, process.versions[dependency]) // const isFullScreen = remote.getCurrentWindow().isFullScreen
} // window.setFullScreen = setFullScreen
// window.isFullScreen = isFullScreen
window.addEventListener('DOMContentLoaded', () => {
// document.getElementById("fullscreen-button").onclick = function () {
// alert(1)
// window.setFullScreen(!window.isFullScreen)
// }
}) })

View File

@ -2,7 +2,7 @@
"miniprogramRoot": "dist/weapp/", "miniprogramRoot": "dist/weapp/",
"projectname": "epp", "projectname": "epp",
"description": "基于微服务的社区疫情防控系统", "description": "基于微服务的社区疫情防控系统",
"appid": "wxa70348746d2b562f", "appid": "wx332e2e578f09873a",
"setting": { "setting": {
"urlCheck": true, "urlCheck": true,
"es6": false, "es6": false,

View File

@ -8,6 +8,11 @@ export default defineAppConfig({
*/ */
'pages/residents/code', // 进出码 'pages/residents/code', // 进出码
'pages/residents/report', // 体温上报 'pages/residents/report', // 体温上报
/**
* 扫码跳转
*/
'pages/scan/entrance', // 门禁码扫码跳转页
], ],
window: { window: {
backgroundTextStyle: 'dark', backgroundTextStyle: 'dark',

View File

@ -37,6 +37,7 @@ export default {
this.debugText = JSON.stringify({ this.debugText = JSON.stringify({
TARO_ENV: process.env.TARO_ENV, TARO_ENV: process.env.TARO_ENV,
globalData: Taro.getApp().globalData, globalData: Taro.getApp().globalData,
"启动参数": Taro.getCurrentInstance().router.params,
params: this.params, params: this.params,
storage: storage, storage: storage,
ENUM: ENUM, ENUM: ENUM,

View File

@ -15,6 +15,7 @@
</view> </view>
<button type="primary" size="mini" @tap="toggleDot('code', true)">显示小红点</button> <button type="primary" size="mini" @tap="toggleDot('code', true)">显示小红点</button>
<button type="primary" size="mini" @tap="toggleDot('code', false)">隐藏小红点</button> <button type="primary" size="mini" @tap="toggleDot('code', false)">隐藏小红点</button>
<button type="warn" size="mini" @tap="magicButton">魔法按钮</button>
<DebugComp :params="debugObject" :userGroup="userGroup" /> <DebugComp :params="debugObject" :userGroup="userGroup" />
</view> </view>
</template> </template>
@ -51,13 +52,13 @@ export default {
onLoad() { onLoad() {
// //
if (this.debugMode) { // if (this.debugMode) {
Taro.switchTab({ url: '/pages/residents/report' }) // Taro.switchTab({ url: '/pages/residents/report' })
} // }
}, },
onShow() { onShow(options) {
console.log('onShow') console.log('onShow', options)
console.log("menuItemDict", menuItemDict) console.log("menuItemDict", menuItemDict)
this.userInfo = Taro.getStorageSync("userInfo"); this.userInfo = Taro.getStorageSync("userInfo");
this.displayUsername = this.userInfo?.username ?? "请登录"; this.displayUsername = this.userInfo?.username ?? "请登录";
@ -67,8 +68,21 @@ export default {
url: '/pages/index/login' url: '/pages/index/login'
}) })
} else { } else {
//
this.userGroup = getUserGroupByRole(this.userInfo.role); this.userGroup = getUserGroupByRole(this.userInfo.role);
this.debugObject = { userGroup: this.userGroup }; this.debugObject = { userGroup: this.userGroup };
//
let launchParams = Taro.getCurrentInstance().router.params;
if (launchParams.scene) {
//
if (launchParams.scene.split('%')[0] == "guard") {
Taro.getCurrentInstance().router.params.scene = null;
Taro.navigateTo({
url: "/pages/scan/entrance"
})
}
}
} }
}, },
methods: { methods: {
@ -92,6 +106,11 @@ export default {
}, },
toggleDot(iconName, status = true) { toggleDot(iconName, status = true) {
this.menuItemDict[iconName].addDot = status; this.menuItemDict[iconName].addDot = status;
},
magicButton() {
Taro.navigateTo({
url: "/pages/scan/entrance"
})
} }
} }
} }

View File

@ -28,3 +28,9 @@
color: grey; color: grey;
font-size: small; font-size: small;
} }
#scan-btn {
margin-top: 50px;
padding: 9px 60px;
font-size: initial;
}

View File

@ -5,6 +5,8 @@
<canvas type="2d" style="width: 250px; height: 250px;" id="myQrcode"></canvas> <canvas type="2d" style="width: 250px; height: 250px;" id="myQrcode"></canvas>
<view id="show-text"><text :style="{ color: showTextColor }">{{ showText }}</text></view> <view id="show-text"><text :style="{ color: showTextColor }">{{ showText }}</text></view>
<view id="small-text"><text>下拉可刷新</text></view> <view id="small-text"><text>下拉可刷新</text></view>
<button id="scan-btn" size="mini" @tap="scan">扫门禁码</button>
</view> </view>
</template> </template>
@ -12,6 +14,7 @@
import Taro from '@tarojs/taro' import Taro from '@tarojs/taro'
import md5 from 'blueimp-md5' import md5 from 'blueimp-md5'
import drawQrcode from '../../utils/qrcode/index' import drawQrcode from '../../utils/qrcode/index'
import scanQRCode from '../../utils/scanQRCode'
import utils from '../../utils/utils' import utils from '../../utils/utils'
import './code.css' import './code.css'
@ -158,6 +161,9 @@ export default {
updateTime() { updateTime() {
this.time = utils.formatTime(new Date()) this.time = utils.formatTime(new Date())
console.log("刷新时间") console.log("刷新时间")
},
scan() {
scanQRCode(Taro)
} }
} }
} }

View File

@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '进入社区',
})

View File

@ -0,0 +1,38 @@
<template>
<view>
您将要进入社区
<textarea maxlength="-1" disabled="true" auto-height="true" style="width: 100%" :value="debugText"></textarea>
</view>
</template>
<script>
import Taro from '@tarojs/taro'
import utils from '../../utils/utils'
export default {
data() {
return {
...Taro.getApp().globalData,
userInfo: null,
scene: null,
debugText: JSON.stringify(Taro.getCurrentInstance().router, null, 4),
}
},
onLaunch(option) {
console.log("onLaunch", option);
},
onShow() {
console.log('onShow')
this.userInfo = Taro.getStorageSync("userInfo")
},
onHide() {
console.log('onHide')
},
methods: {
}
}
</script>

View File

@ -0,0 +1,25 @@
function scanQRCode(Taro) {
// 只允许从相机扫码
Taro.scanCode({
onlyFromCamera: true,
success(res) {
console.log(res)
if (res.scanType == "WX_CODE" && res.path) {
let searchParams = res.path.split('?');
if (searchParams.length > 1 && searchParams[1].startsWith('scene=guard')) {
Taro.navigateTo({
url: "/pages/scan/entrance"
})
return
}
}
Taro.showToast({
title: "您扫描的不是门禁码",
icon: 'error',
duration: 2000
})
}
})
}
module.exports = scanQRCode;