From 4a62a4a5ca7ea2bd131d9ceb3119902a40e5a909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=84=E9=94=90=E6=B3=A2?= <10549902+zhuangruibo@user.noreply.gitee.com> Date: Wed, 22 Apr 2026 23:19:39 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...AppEnfEnterpriseInformationController.java | 122 ++++++++++ .../domain/vo/EnterpriseInformationVo.java | 11 + .../IEnterpriseInformationService.java | 8 + .../EnterpriseInformationServiceImpl.java | 17 ++ .../src/api/enforcement/index.js | 3 + .../router/enforcement-app/enf-main-router.js | 9 + .../enforcement/enterpriseScanPreview.vue | 229 ++++++++++++++++++ .../src/views/enforcement/scanEntQRCode.vue | 40 +-- 8 files changed, 422 insertions(+), 17 deletions(-) create mode 100644 ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfEnterpriseInformationController.java create mode 100644 zdxt-web-client/zdxt-enforcement-code-app/src/views/enforcement/enterpriseScanPreview.vue diff --git a/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfEnterpriseInformationController.java b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfEnterpriseInformationController.java new file mode 100644 index 00000000..1ea70ef2 --- /dev/null +++ b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfEnterpriseInformationController.java @@ -0,0 +1,122 @@ +package com.zdxt.enforcementcode.controller.app.enforcement; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zdxt.enforcementcode.domain.vo.EnterpriseInformationVo; +import com.zdxt.enforcementcode.domain.vo.QrCodeGenerateHistoryVo; +import com.zdxt.enforcementcode.service.IEnterpriseInformationService; +import com.zdxt.enforcementcode.service.IQrCodeGenerateHistoryService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.R; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/app-enf/enterpriseInfo") +public class AppEnfEnterpriseInformationController { + + private final IEnterpriseInformationService enterpriseInformationService; + private final IQrCodeGenerateHistoryService qrCodeGenerateHistoryService; + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + /** + * 扫描二维码后获取企业信息(多级降级查询) + *

+ * 查询优先级: + * 1. 通过 salt 查找企业信息 + * 2. 通过 codeId 查找二维码记录,再通过 enterpriseNumber 查找企业信息 + * 3. 通过二维码中的 enterpriseNumber 直接查找企业信息 + *

+ * + * @param qrContent 前端解析二维码获取的原始文本内容 + * @return 企业信息(含二维码状态) + */ + @SaIgnore + @GetMapping("/getEntInfoByQRCode") + public R getEntInfoByQRCode(@RequestParam("qrContent") String qrContent) { + log.info("执法端扫码获取企业信息,二维码内容: {}", qrContent); + + // 1. 判断 qrContent 是否为 JSON,是则转为 Map,否则打印日志 + Map qrMap = null; + try { + qrMap = OBJECT_MAPPER.readValue(qrContent, new TypeReference>() {}); + log.info("二维码内容解析为JSON: {}", qrMap); + } catch (Exception e) { + log.warn("二维码内容不是JSON格式,原始内容: {}", qrContent); + } + + EnterpriseInformationVo enterprise = null; + String salt = qrMap != null ? qrMap.get("salt") : null; + String codeId = qrMap != null ? qrMap.get("codeId") : null; + String enterpriseNumber = qrMap != null ? qrMap.get("enterpriseNumber") : null; + + // 2. 优先通过 salt 查找企业信息 + if (StrUtil.isNotBlank(salt)) { + enterprise = enterpriseInformationService.queryBySalt(salt); + if (enterprise != null) { + log.info("通过 salt 查询到企业信息: {}", enterprise.getEnterpriseName()); + } else { + log.info("通过 salt[{}] 未查询到企业信息,进入降级处理", salt); + } + } + + // 3. 降级:通过 codeId 查找二维码记录,再通过 enterpriseNumber 查找企业信息 + if (enterprise == null && StrUtil.isNotBlank(codeId)) { + QrCodeGenerateHistoryVo qrHistory = qrCodeGenerateHistoryService.queryById(codeId); + if (qrHistory != null && StrUtil.isNotBlank(qrHistory.getEnterpriseNumber())) { + enterprise = enterpriseInformationService.queryByEnterpriseNumber(qrHistory.getEnterpriseNumber()); + if (enterprise != null) { + log.info("通过 codeId[{}] -> enterpriseNumber[{}] 查询到企业信息: {}", + codeId, qrHistory.getEnterpriseNumber(), enterprise.getEnterpriseName()); + } else { + log.info("通过 codeId[{}] -> enterpriseNumber[{}] 未查询到企业信息", + codeId, qrHistory.getEnterpriseNumber()); + } + } else { + log.info("通过 codeId[{}] 未查询到二维码记录或记录中无 enterpriseNumber", codeId); + } + } + + // 4. 最终降级:通过二维码中的 enterpriseNumber 直接查找企业信息 + if (enterprise == null && StrUtil.isNotBlank(enterpriseNumber)) { + enterprise = enterpriseInformationService.queryByEnterpriseNumber(enterpriseNumber); + if (enterprise != null) { + log.info("通过二维码中的 enterpriseNumber[{}] 直接查询到企业信息: {}", + enterpriseNumber, enterprise.getEnterpriseName()); + } else { + log.info("通过 enterpriseNumber[{}] 未查询到企业信息", enterpriseNumber); + } + } + + if (enterprise == null) { + return R.fail("无法通过该二维码识别企业信息"); + } + + // 5. 校验二维码是否有效 + boolean qrCodeValid = false; + String checkCodeId = codeId; + // 如果二维码中没有 codeId,尝试用企业上的 qrCodeId + if (StrUtil.isBlank(checkCodeId) && StrUtil.isNotBlank(enterprise.getQrCodeId())) { + checkCodeId = enterprise.getQrCodeId(); + } + if (StrUtil.isNotBlank(checkCodeId)) { + QrCodeGenerateHistoryVo qrCode = qrCodeGenerateHistoryService.queryById(checkCodeId); + if (qrCode != null && qrCode.getStatus() != null && qrCode.getStatus() == 0L) { + qrCodeValid = true; + } + } + + // 6. 设置二维码状态并返回 + enterprise.setQrCodeValid(qrCodeValid); + enterprise.setQrCodeMessage(qrCodeValid ? "二维码正常" : "二维码已过期"); + + log.info("执法端扫码查询成功,企业: {},二维码状态: {}", enterprise.getEnterpriseName(), qrCodeValid ? "正常" : "过期"); + return R.ok(enterprise); + } +} diff --git a/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/EnterpriseInformationVo.java b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/EnterpriseInformationVo.java index b14800f6..d3d9f70b 100644 --- a/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/EnterpriseInformationVo.java +++ b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/EnterpriseInformationVo.java @@ -153,5 +153,16 @@ public class EnterpriseInformationVo implements Serializable { @ExcelProperty(value = "") private String operatingTermEnd; + // ========== 非持久化字段,用于扫码接口返回 ========== + + /** + * 二维码是否有效(非数据库字段,扫码接口动态赋值) + */ + private transient Boolean qrCodeValid; + + /** + * 二维码状态描述(非数据库字段,扫码接口动态赋值) + */ + private transient String qrCodeMessage; } diff --git a/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEnterpriseInformationService.java b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEnterpriseInformationService.java index fd6b3b3e..2a3cfb58 100644 --- a/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEnterpriseInformationService.java +++ b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEnterpriseInformationService.java @@ -76,6 +76,14 @@ public interface IEnterpriseInformationService { */ EnterpriseInformationVo queryByEnterpriseNumber(String enterpriseNumber); + /** + * 根据盐加密字符串查询唯一企业信息 + * + * @param salt 盐加密字符串 + * @return 企业信息,不存在返回 null + */ + EnterpriseInformationVo queryBySalt(String salt); + /** * 为企业生成蓝色二维码(含 LOGO) *

如果企业已存在有效二维码,则直接返回已有记录,不重复生成

diff --git a/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EnterpriseInformationServiceImpl.java b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EnterpriseInformationServiceImpl.java index d4781bd1..c63389a0 100644 --- a/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EnterpriseInformationServiceImpl.java +++ b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EnterpriseInformationServiceImpl.java @@ -177,6 +177,23 @@ public class EnterpriseInformationServiceImpl implements IEnterpriseInformationS return list.get(0); } + /** + * 根据盐加密字符串查询唯一企业信息 + */ + @Override + public EnterpriseInformationVo queryBySalt(String salt) { + if (StringUtils.isBlank(salt)) { + return null; + } + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(EnterpriseInformation::getSalt, salt); + List list = baseMapper.selectVoList(lqw); + if (list.isEmpty()) { + return null; + } + return list.get(0); + } + /** * 为企业生成蓝色二维码(含 LOGO) *

如果企业已存在有效二维码,则直接返回已有记录,不重复生成

diff --git a/zdxt-web-client/zdxt-enforcement-code-app/src/api/enforcement/index.js b/zdxt-web-client/zdxt-enforcement-code-app/src/api/enforcement/index.js index 9a35e6d7..3b63046e 100644 --- a/zdxt-web-client/zdxt-enforcement-code-app/src/api/enforcement/index.js +++ b/zdxt-web-client/zdxt-enforcement-code-app/src/api/enforcement/index.js @@ -7,4 +7,7 @@ let enforcementAppBaseUrl = "/app-enf"; // 执法端用户登录 export const enforcementLogin = (params, success, error) => _post(enforcementAppBaseUrl + "/login/do", params, success, error); +// 扫描二维码后获取企业信息(qrContent 为前端解析二维码获取的原始文本) +export const getEntInfoByQRCode = (qrContent, success, error) => _get(enforcementAppBaseUrl + "/enterpriseInfo/getEntInfoByQRCode", { qrContent }, success, error); + // endregion diff --git a/zdxt-web-client/zdxt-enforcement-code-app/src/router/enforcement-app/enf-main-router.js b/zdxt-web-client/zdxt-enforcement-code-app/src/router/enforcement-app/enf-main-router.js index a8c952ac..e5e4732f 100644 --- a/zdxt-web-client/zdxt-enforcement-code-app/src/router/enforcement-app/enf-main-router.js +++ b/zdxt-web-client/zdxt-enforcement-code-app/src/router/enforcement-app/enf-main-router.js @@ -27,6 +27,15 @@ const routes = [ isKeepAlive: false, }, }, + // 执法端扫码-企业详情预览 + { + path: "/enforcement/enterpriseScanPreview", + name: "enfEnterpriseScanPreview", + component: () => import("@/views/enforcement/enterpriseScanPreview.vue"), + meta: { + isKeepAlive: false, + }, + }, ]; export {routes}; diff --git a/zdxt-web-client/zdxt-enforcement-code-app/src/views/enforcement/enterpriseScanPreview.vue b/zdxt-web-client/zdxt-enforcement-code-app/src/views/enforcement/enterpriseScanPreview.vue new file mode 100644 index 00000000..7b3e0100 --- /dev/null +++ b/zdxt-web-client/zdxt-enforcement-code-app/src/views/enforcement/enterpriseScanPreview.vue @@ -0,0 +1,229 @@ + + + + + diff --git a/zdxt-web-client/zdxt-enforcement-code-app/src/views/enforcement/scanEntQRCode.vue b/zdxt-web-client/zdxt-enforcement-code-app/src/views/enforcement/scanEntQRCode.vue index e294f2bd..3b6774b8 100644 --- a/zdxt-web-client/zdxt-enforcement-code-app/src/views/enforcement/scanEntQRCode.vue +++ b/zdxt-web-client/zdxt-enforcement-code-app/src/views/enforcement/scanEntQRCode.vue @@ -51,6 +51,7 @@ import { ref, reactive, onMounted, onBeforeUnmount, nextTick } from 'vue'; import { useRouter } from 'vue-router'; import { showFailToast, showSuccessToast, showToast, showConfirmDialog } from 'vant'; import { baseUrl, appEnv } from '@/config/env.js'; +import { getEntInfoByQRCode } from '@/api/enforcement/index'; import wx from 'weixin-js-sdk'; const router = useRouter(); @@ -92,6 +93,7 @@ const openHtml5QrcodeScanner = async () => { ); } catch (err) { console.error('摄像头启动失败', err); + html5QrCode = null; // 启动失败,清除实例,避免销毁时调 stop 报错 showScanner.value = false; // 摄像头不可用时,提供上传二维码图片的后备方案 showConfirmDialog({ @@ -219,24 +221,28 @@ const handleScanResult = (resultStr) => { console.log('========== 扫码结果处理 =========='); console.log('原始内容:', resultStr); - if (isHttpLink(resultStr)) { - // HTTP 链接类型(市监二维码) - // TODO: 对接服务端 getEnterpriseInfoBySj 接口 - console.log('识别到 HTTP 链接,待对接服务端接口:', resultStr); - showToast('识别到企业链接,服务端接口待对接'); - } else { - // JSON 类型(执法码二维码) - try { - const scanObj = JSON.parse(resultStr); - console.log('解析到 JSON 二维码:', scanObj); - // TODO: 对接服务端 checkIsState 接口 - // checkIsState(scanObj.salt, scanObj.codeId, (res) => { ... }); - showToast('识别到执法码二维码,服务端接口待对接'); - } catch (e) { - console.warn('非 JSON 格式,原始内容:', resultStr); - showFailToast('无法识别该二维码'); + // 不管解码结果是 HTTP 链接还是纯文本,统一交给后端解析 + showToast({ message: '正在查询企业信息...', duration: 0 }); + getEntInfoByQRCode(resultStr, (res) => { + showToast({ message: '', duration: 1 }); // 关闭 loading + if (res.code === 200) { + const data = res.data; + console.log('企业信息查询成功:', data); + if (!data.qrCodeValid) { + showFailToast(data.qrCodeMessage || '二维码已过期'); + } + // 将企业信息存入 sessionStorage,预览页直接使用,无需再调接口 + sessionStorage.setItem('enterpriseScanData', JSON.stringify(data)); + // 跳转企业详情预览页 + router.push({ path: '/enforcement/enterpriseScanPreview' }); + } else { + showFailToast(res.msg || '查询企业信息失败'); } - } + }, (err) => { + showToast({ message: '', duration: 1 }); + console.error('getEntInfoByQRCode 调用失败', err); + showFailToast('查询企业信息失败,请重试'); + }); }; const isHttpLink = (url) => {