diff --git a/TODOs.md b/TODOs.md index 151f142..397afec 100644 --- a/TODOs.md +++ b/TODOs.md @@ -3,4 +3,6 @@ 更多: 完成项目代码中的 TODO 部分 -身份码后端接口考虑与其他系统的集成逻辑 \ No newline at end of file +身份码后端接口考虑与其他系统的集成逻辑 + +Java代码中小程序AppID、密钥处理,小程序代码中小程序AppID处理 \ No newline at end of file diff --git a/backend/microservice-provider-access-8002/pom.xml b/backend/microservice-provider-access-8002/pom.xml index a816e77..5cd9249 100644 --- a/backend/microservice-provider-access-8002/pom.xml +++ b/backend/microservice-provider-access-8002/pom.xml @@ -98,6 +98,11 @@ fastjson2 + + + com.squareup.okhttp3 + okhttp + diff --git a/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/controller/WeChatTokenController.java b/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/controller/WeChatTokenController.java index 7965355..872e8e4 100644 --- a/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/controller/WeChatTokenController.java +++ b/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/controller/WeChatTokenController.java @@ -1,11 +1,15 @@ package com.cxyxiaomo.epp.access.controller; +import com.cxyxiaomo.epp.access.pojo.UnlimitedQRCodeParam; import com.cxyxiaomo.epp.access.service.WeChatTokenServiceImpl; import com.cxyxiaomo.epp.common.response.Res; +import lombok.SneakyThrows; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @Controller @@ -21,4 +25,24 @@ public class WeChatTokenController { String accessToken = weChatTokenService.getAccessToken(); 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(); + } } diff --git a/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/pojo/UnlimitedQRCodeParam.java b/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/pojo/UnlimitedQRCodeParam.java new file mode 100644 index 0000000..83b0b05 --- /dev/null +++ b/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/pojo/UnlimitedQRCodeParam.java @@ -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; +} diff --git a/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/service/WeChatTokenServiceImpl.java b/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/service/WeChatTokenServiceImpl.java index 13fb784..88d0c73 100644 --- a/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/service/WeChatTokenServiceImpl.java +++ b/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/service/WeChatTokenServiceImpl.java @@ -1,14 +1,20 @@ package com.cxyxiaomo.epp.access.service; +import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; 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.common.pojo.Setting; +import lombok.SneakyThrows; +import okhttp3.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Service; import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; @Service public class WeChatTokenServiceImpl implements WeChatTokenService { @@ -20,8 +26,12 @@ public class WeChatTokenServiceImpl implements WeChatTokenService { 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 public String getAccessToken() { @@ -74,11 +84,35 @@ public class WeChatTokenServiceImpl implements WeChatTokenService { String jsonStr = RestUtil.doGetRequest(url, headers); 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 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}" -} -*/ diff --git a/backend/pom.xml b/backend/pom.xml index 5882bef..fbcdbd0 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -108,19 +108,26 @@ runtime ${mysql.version} - + com.alibaba druid ${druid.version} - + org.mybatis.spring.boot mybatis-spring-boot-starter 2.2.2 + + + com.squareup.okhttp3 + okhttp + 4.10.0 + + com.alibaba.fastjson2 diff --git a/client-entrance-guard/html/index.css b/client-entrance-guard/html/index.css new file mode 100644 index 0000000..603786b --- /dev/null +++ b/client-entrance-guard/html/index.css @@ -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; +} */ diff --git a/client-entrance-guard/html/index.html b/client-entrance-guard/html/index.html index a6f4a90..890bc26 100644 --- a/client-entrance-guard/html/index.html +++ b/client-entrance-guard/html/index.html @@ -3,17 +3,36 @@ - - - Hello World! + + + + 社区疫情防控系统 - 门禁端 + -

Hello World!

- We are using Node.js , - Chromium , - and Electron . - +
+
+

进出社区请扫码

+ +

社区居民

+

1. 打开 微信 > 扫一扫,扫描右侧小程序码

+

2. 点击确认进入,门即开启

+ +

外来访客

+

1. 打开 微信 > 扫一扫,扫描右侧小程序码

+

2. 填写进入申请表

+ +

长期租客

+

1. 请联系管理员为你添加进出权限

+
+
+ +

+
+
+ + diff --git a/client-entrance-guard/html/renderer.js b/client-entrance-guard/html/renderer.js index a21dc4c..981ec32 100644 --- a/client-entrance-guard/html/renderer.js +++ b/client-entrance-guard/html/renderer.js @@ -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 = ` ` + } else { + refreshTimeCountDown.textContent = `${refreshTime - i}秒后刷新` + } + i = i % refreshTime + 1; +} + +updateQRCode(); +setInterval(updateQRCode, 1000); diff --git a/client-entrance-guard/main.js b/client-entrance-guard/main.js index 9495b08..72e7fec 100644 --- a/client-entrance-guard/main.js +++ b/client-entrance-guard/main.js @@ -6,16 +6,23 @@ console.log(`This platform is ${platform}`); const createWindow = () => { const mainWindow = new BrowserWindow({ - width: 800, - height: 600, + width: 1080, + minWidth: 760, + height: 720, + minHeight: 480, webPreferences: { preload: path.join(__dirname, 'preload.js') }, }) mainWindow.loadFile('html/index.html') + // mainWindow.fullScreen = true; + //配置ESC键退出全屏 + globalShortcut.register('ESC', () => { + mainWindow.setFullScreen(false); + }) - mainWindow.webContents.openDevTools() + // mainWindow.webContents.openDevTools() } app.whenReady().then(() => { diff --git a/client-entrance-guard/preload.js b/client-entrance-guard/preload.js index 8439f65..60b43e5 100644 --- a/client-entrance-guard/preload.js +++ b/client-entrance-guard/preload.js @@ -1,10 +1,13 @@ -window.addEventListener('DOMContentLoaded', () => { - const replaceText = (selector, text) => { - const element = document.getElementById(selector) - if (element) element.innerText = text - } +// import { remote } from 'electron' - for (const dependency of ['chrome', 'node', 'electron']) { - replaceText(`${dependency}-version`, process.versions[dependency]) - } +// const setFullScreen = remote.getCurrentWindow().setFullScreen +// 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) + // } }) diff --git a/miniprogram/project.config.json b/miniprogram/project.config.json index f0652cb..04b9f45 100644 --- a/miniprogram/project.config.json +++ b/miniprogram/project.config.json @@ -2,7 +2,7 @@ "miniprogramRoot": "dist/weapp/", "projectname": "epp", "description": "基于微服务的社区疫情防控系统", - "appid": "wxa70348746d2b562f", + "appid": "wx332e2e578f09873a", "setting": { "urlCheck": true, "es6": false, diff --git a/miniprogram/src/app.config.js b/miniprogram/src/app.config.js index 4db4d44..b89512f 100644 --- a/miniprogram/src/app.config.js +++ b/miniprogram/src/app.config.js @@ -8,6 +8,11 @@ export default defineAppConfig({ */ 'pages/residents/code', // 进出码 'pages/residents/report', // 体温上报 + + /** + * 扫码跳转 + */ + 'pages/scan/entrance', // 门禁码扫码跳转页 ], window: { backgroundTextStyle: 'dark', diff --git a/miniprogram/src/components/DebugComp.vue b/miniprogram/src/components/DebugComp.vue index e90f415..9de4d2a 100644 --- a/miniprogram/src/components/DebugComp.vue +++ b/miniprogram/src/components/DebugComp.vue @@ -37,6 +37,7 @@ export default { this.debugText = JSON.stringify({ TARO_ENV: process.env.TARO_ENV, globalData: Taro.getApp().globalData, + "启动参数": Taro.getCurrentInstance().router.params, params: this.params, storage: storage, ENUM: ENUM, diff --git a/miniprogram/src/pages/index/index.vue b/miniprogram/src/pages/index/index.vue index 516c0d8..a6b2bbd 100644 --- a/miniprogram/src/pages/index/index.vue +++ b/miniprogram/src/pages/index/index.vue @@ -15,6 +15,7 @@ + @@ -51,13 +52,13 @@ export default { onLoad() { // 开发模式下自动跳转到指定页面,节省开发时间 - if (this.debugMode) { - Taro.switchTab({ url: '/pages/residents/report' }) - } + // if (this.debugMode) { + // Taro.switchTab({ url: '/pages/residents/report' }) + // } }, - onShow() { - console.log('onShow') + onShow(options) { + console.log('onShow', options) console.log("menuItemDict", menuItemDict) this.userInfo = Taro.getStorageSync("userInfo"); this.displayUsername = this.userInfo?.username ?? "请登录"; @@ -67,8 +68,21 @@ export default { url: '/pages/index/login' }) } else { + // 用户已登录 this.userGroup = getUserGroupByRole(this.userInfo.role); 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: { @@ -92,6 +106,11 @@ export default { }, toggleDot(iconName, status = true) { this.menuItemDict[iconName].addDot = status; + }, + magicButton() { + Taro.navigateTo({ + url: "/pages/scan/entrance" + }) } } } diff --git a/miniprogram/src/pages/residents/code.css b/miniprogram/src/pages/residents/code.css index c8a6bf2..ee3386b 100644 --- a/miniprogram/src/pages/residents/code.css +++ b/miniprogram/src/pages/residents/code.css @@ -28,3 +28,9 @@ color: grey; font-size: small; } + +#scan-btn { + margin-top: 50px; + padding: 9px 60px; + font-size: initial; +} diff --git a/miniprogram/src/pages/residents/code.vue b/miniprogram/src/pages/residents/code.vue index bf3d4fe..9807aaa 100644 --- a/miniprogram/src/pages/residents/code.vue +++ b/miniprogram/src/pages/residents/code.vue @@ -5,6 +5,8 @@ {{ showText }} 下拉可刷新 + + @@ -12,6 +14,7 @@ import Taro from '@tarojs/taro' import md5 from 'blueimp-md5' import drawQrcode from '../../utils/qrcode/index' +import scanQRCode from '../../utils/scanQRCode' import utils from '../../utils/utils' import './code.css' @@ -158,6 +161,9 @@ export default { updateTime() { this.time = utils.formatTime(new Date()) console.log("刷新时间") + }, + scan() { + scanQRCode(Taro) } } } diff --git a/miniprogram/src/pages/scan/entrance.config.js b/miniprogram/src/pages/scan/entrance.config.js new file mode 100644 index 0000000..d15ca3c --- /dev/null +++ b/miniprogram/src/pages/scan/entrance.config.js @@ -0,0 +1,3 @@ +export default definePageConfig({ + navigationBarTitleText: '进入社区', +}) diff --git a/miniprogram/src/pages/scan/entrance.vue b/miniprogram/src/pages/scan/entrance.vue new file mode 100644 index 0000000..1a35025 --- /dev/null +++ b/miniprogram/src/pages/scan/entrance.vue @@ -0,0 +1,38 @@ + + + diff --git a/miniprogram/src/utils/scanQRCode.js b/miniprogram/src/utils/scanQRCode.js new file mode 100644 index 0000000..ff8a63f --- /dev/null +++ b/miniprogram/src/utils/scanQRCode.js @@ -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;