diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/CrawlerUtil.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/CrawlerUtil.java new file mode 100644 index 00000000..a41ac3ef --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/CrawlerUtil.java @@ -0,0 +1,59 @@ +package com.zdxt.enforcementcode.common.util; + +import com.gargoylesoftware.htmlunit.BrowserVersion; +import com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.WebClientOptions; +import com.gargoylesoftware.htmlunit.html.DomElement; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import net.sourceforge.htmlunit.corejs.javascript.EcmaError; + +import java.util.Objects; +import java.util.logging.LogManager; + +/** + * 爬虫 读取页面工具 + * @author 陈瑞 + * @date 2024-11-11 18:33 + */ +public class CrawlerUtil { + + public static WebClient webClient; + + static { + webClient = new WebClient(BrowserVersion.CHROME); + WebClientOptions options = webClient.getOptions(); + options.setUseInsecureSSL(true); + options.setCssEnabled(false); + options.setThrowExceptionOnScriptError(false); + options.setThrowExceptionOnFailingStatusCode(false); + options.setJavaScriptEnabled(true); + options.setTimeout(10000); + webClient.setAjaxController(new NicelyResynchronizingAjaxController()); + LogManager.getLogManager().reset(); + } + + + /** + * 爬取页面固定数据 + * @param url + * @return + * @throws Exception + */ + public static String getCreditCode(String url) throws Exception { + HtmlPage htmlPage = null; + try { + htmlPage = webClient.getPage(url); + } catch (EcmaError e) { + } + if (Objects.nonNull(htmlPage)) { + DomElement codeElement = htmlPage.getElementById("lblEntSCCode"); + if (codeElement != null) { + // HtmlUnit 2.55+ 已移除 asText(),改用 asNormalizedText() + String code = codeElement.asNormalizedText(); + return code; + } + } + return null; + } +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfEnterpriseInformationController.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfEnterpriseInformationController.java index a29f3a9a..4c6b6f03 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfEnterpriseInformationController.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfEnterpriseInformationController.java @@ -1,23 +1,34 @@ package com.zdxt.enforcementcode.controller.app.enforcement; import cn.dev33.satoken.annotation.SaIgnore; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.zdxt.enforcementcode.common.util.CrawlerUtil; import com.zdxt.enforcementcode.domain.EnterpriseInformation; import com.zdxt.enforcementcode.domain.bo.EnterpriseInformationBo; 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 com.zdxt.enforcementcode.service.ISsztJbxx110497NewService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.domain.R; +import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.beans.BeanUtils; import org.springframework.web.bind.annotation.*; +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -32,6 +43,7 @@ public class AppEnfEnterpriseInformationController { private final IEnterpriseInformationService enterpriseInformationService; private final IQrCodeGenerateHistoryService qrCodeGenerateHistoryService; + private final ISsztJbxx110497NewService iSsztJbxx110497NewService; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); /** @@ -51,6 +63,56 @@ public class AppEnfEnterpriseInformationController { public R getEntInfoByQRCode(@RequestParam("qrContent") String qrContent) { log.info("执法端扫码获取企业信息,二维码内容: {}", qrContent); + // 按二维码原文分流:http/https 开头为市监公示 URL,否则为本系统 JSON + if (StrUtil.startWith(qrContent, "http://") || StrUtil.startWith(qrContent, "https://")) { + //解析路由获取企业统一信用代码 + String cid = null; + try { + cid = getUrlParams(qrContent, "cid"); + if(StringUtils.isNotEmpty(cid)) { + //初始化对象 + EnterpriseInformationVo enterpriseVo = null; + //查询企业表中是否存在企业信息 + enterpriseVo = enterpriseInformationService.queryByEnterpriseNumber(cid); + log.warn("企业信息表中查询企业信息,企业:{}", JSONObject.toJSONString(enterpriseVo)); + + //判断企业信息为空 + if(BeanUtil.isEmpty(enterpriseVo)){ + enterpriseVo = iSsztJbxx110497NewService.queryByTyshxydmOne(cid); + if(BeanUtil.isNotEmpty(enterpriseVo) && + StringUtils.isNotEmpty(enterpriseVo.getEnterpriseNumber())){ + //默认ID + enterpriseVo.setId(IdUtil.simpleUUID()); + //企业表中新增 + EnterpriseInformation enterprise = new EnterpriseInformation(); + BeanUtils.copyProperties(enterpriseVo, enterprise); + int i = enterpriseInformationService.insertEnterpriseInformation(enterprise); + if(i > 0){ + log.info("企业信息表中新增成功,新增企业:{}", JSONObject.toJSONString(enterpriseVo)); + }else{ + log.info("企业信息表中新增失败,失败企业:{}", JSONObject.toJSONString(enterpriseVo)); + } + } + } + return R.ok(enterpriseVo); + } else { + log.error("企业营业执照二维码中不存在企业统一信用代码,访问地址:{}",qrContent); + //爬取页面统一信用代码 + String s = CrawlerUtil.getCreditCode(qrContent); + if (StringUtils.isNotEmpty(s)) { + EnterpriseInformationVo enterpriseVo = iSsztJbxx110497NewService.queryByTyshxydmOne(s); + return R.ok(enterpriseVo); + } + } + } catch (Exception e) { + log.warn("营业执照解析错误: {}", cid); + } + + log.info("企业信息表中查询企业信息,统一信用代码:{}", cid); + log.info("识别为市监公示系统二维码,后续解析待补充,原始内容: {}", qrContent); + return R.fail("无法通过该二维码识别企业信息"); + } + // 1. 判断 qrContent 是否为 JSON,是则转为 Map,否则打印日志 Map qrMap = null; try { @@ -211,4 +273,39 @@ public class AppEnfEnterpriseInformationController { log.info("执法端查询企业信息详情成功,企业: {}", enterprise.getEnterpriseName()); return R.ok(enterprise); } + + public String getUrlParams(String urlStr,String paramKey) throws IOException { + URL url = new URL(urlStr); + URLConnection connection = url.openConnection(); + + // 获取查询字符串,即URL中"?"后面的部分 + String query = url.getQuery(); + + // 将查询字符串拆分为键值对 + Map queryParams = new HashMap<>(); + if (query != null) { + String split_flag = "&"; + if (query.indexOf("&") < 0) { + if (query.indexOf("&") < 0) { + split_flag = "%26"; + } else { + split_flag = "&"; + } + } else { + split_flag = "&"; + } +// String split_flag = query.indexOf("&") < 0 ?"%26":"&"; // 有些链接的 & 会被转义成 %26 + String[] pairs = query.split(split_flag); + for (String pair : pairs) { + int idx = pair.indexOf("="); + String key = pair.substring(0, idx); + String value = pair.substring(idx + 1); + queryParams.put(key, value); + } + } + // 输出参数 + queryParams.forEach((k, v) -> System.out.println(k + " = " + v)); + return queryParams.get(paramKey); + + } } diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/ISsztJbxx110497NewService.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/ISsztJbxx110497NewService.java index bc33c88f..1475e095 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/ISsztJbxx110497NewService.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/ISsztJbxx110497NewService.java @@ -1,5 +1,6 @@ package com.zdxt.enforcementcode.service; +import com.zdxt.enforcementcode.domain.vo.EnterpriseInformationVo; import com.zdxt.enforcementcode.domain.vo.SsztJbxx110497NewVo; import com.zdxt.enforcementcode.domain.bo.SsztJbxx110497NewBo; import org.dromara.common.mybatis.core.page.TableDataInfo; @@ -48,6 +49,7 @@ public interface ISsztJbxx110497NewService { * @return 企业信息 */ List queryByTyshxydm(String tyshxydm); + EnterpriseInformationVo queryByTyshxydmOne(String tyshxydm); /** * 新增【请填写功能名称】 diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/SsztJbxx110497NewServiceImpl.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/SsztJbxx110497NewServiceImpl.java index 98e29336..37e30a6e 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/SsztJbxx110497NewServiceImpl.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/SsztJbxx110497NewServiceImpl.java @@ -1,6 +1,7 @@ package com.zdxt.enforcementcode.service.impl; import com.zdxt.enforcementcode.domain.bean.SsztJbxx110497New; +import com.zdxt.enforcementcode.domain.vo.EnterpriseInformationVo; import com.zdxt.enforcementcode.mapper.SsztJbxx110497NewMapper; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; @@ -96,6 +97,55 @@ public class SsztJbxx110497NewServiceImpl implements ISsztJbxx110497NewService { return result; } + @Override + public EnterpriseInformationVo queryByTyshxydmOne(String tyshxydm) { + if (StringUtils.isBlank(tyshxydm)) { + log.warn("统一社会信用代码不能为空"); + return null; + } + + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(SsztJbxx110497New::getTyshxydm, tyshxydm.trim()); + // 市监可能有重复数据,取最新一条 + lqw.last("ORDER BY updatetime DESC LIMIT 1"); + + SsztJbxx110497NewVo ssztJbxx110497 = baseMapper.selectVoOne(lqw); + if (ssztJbxx110497 == null) { + log.info("根据统一社会信用代码[{}]未查询到企业信息", tyshxydm); + return null; + } + + log.info("根据统一社会信用代码[{}]查询到企业信息", tyshxydm); + EnterpriseInformationVo enterpriseInformation = new EnterpriseInformationVo(); + //企业名称 + enterpriseInformation.setEnterpriseName(ssztJbxx110497.getQymc()); + //统一信用代码 + enterpriseInformation.setEnterpriseNumber(ssztJbxx110497.getTyshxydm()); + //法定代表人 + enterpriseInformation.setLegalPerson(ssztJbxx110497.getFddbr()); + //成立日期 + if (StringUtils.isNotBlank(ssztJbxx110497.getClrq())) { + try { + java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd"); + enterpriseInformation.setEstablishmentTime(sdf.parse(ssztJbxx110497.getClrq())); + } catch (Exception e) { + log.warn("成立日期格式转换失败: {}", ssztJbxx110497.getClrq(), e); + } + } + //经营期限-开始 + enterpriseInformation.setOperatingTerm(ssztJbxx110497.getJyqxq()); + //经营期限-结束 + enterpriseInformation.setOperatingTermEnd(ssztJbxx110497.getJyqxz()); + //注册资本 + enterpriseInformation.setRegisteredCapital(ssztJbxx110497.getZczb() != null ? String.valueOf(ssztJbxx110497.getZczb()) : null); + //经营范围 + enterpriseInformation.setBusinessScope(ssztJbxx110497.getJyfw()); + //注册地址 + enterpriseInformation.setEnterpriseAddress(ssztJbxx110497.getJycs()); + //TODO 企业类型 + + return enterpriseInformation; + } private LambdaQueryWrapper buildQueryWrapper(SsztJbxx110497NewBo bo) { Map params = bo.getParams(); diff --git a/zdxt-web-client/zdxt-admin-plus-ui/package.json b/zdxt-web-client/zdxt-admin-plus-ui/package.json index a40b8f62..65d6eb88 100644 --- a/zdxt-web-client/zdxt-admin-plus-ui/package.json +++ b/zdxt-web-client/zdxt-admin-plus-ui/package.json @@ -12,6 +12,7 @@ "vite-server-web-supervision-dev-wmy": "vite serve --mode development-wmy-cust", "build:prod": "vite build --mode production --outDir dist/prod", "build:dev": "vite build --mode development --outDir dist/dev", + "build-zdxt-admin-plus-ui:dev": "vite build --mode development --outDir dist/dev", "build-zdxt-admin-plus-ui:prod": "vite build --mode production --outDir dist/prod", "zdxt-admin-plus-ui-preview": "vite preview --outDir dist/prod", "zdxt-admin-plus-ui-lint:eslint": "eslint", diff --git a/zdxt-web-client/zdxt-efcode-enterprise-app/package.json b/zdxt-web-client/zdxt-efcode-enterprise-app/package.json index 416b0897..11aa98a5 100644 --- a/zdxt-web-client/zdxt-efcode-enterprise-app/package.json +++ b/zdxt-web-client/zdxt-efcode-enterprise-app/package.json @@ -10,6 +10,7 @@ "vite-server-app:enterprise-dev-wmy": "vite --mode enterprise-dev-wmy", "build:dev": "vite build --mode enterprise-dev --outDir dist/enterprise-dev", "build:prod": "vite build --mode enterprise-prod --outDir dist/enterprise-prod", + "build-app:enterprise-dev": "vite build --mode enterprise-dev --outDir dist/enterprise-dev", "build-app:enterprise-prod": "vite build --mode enterprise-prod --outDir dist/enterprise-prod" }, "dependencies": { diff --git a/zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml b/zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml index a73ec9b1..f0f055da 100644 --- a/zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml +++ b/zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml @@ -1,8 +1,8 @@ zdxt: file: # Windows/Linux 本地开发均可用;生产环境在 application-prod.yml 覆盖 -# upload-path: D:\zfjdm\uploadPath - upload-path: /usr/local/uploadPath + upload-path: D:\zfjdm\uploadPath +# upload-path: /usr/local/uploadPath # upload-path: /Users/vincent/uploadPath url-prefix: /profile qr_code_url: /usr/local/uploads/zfjd_logo.png @@ -110,14 +110,14 @@ spring.data: redis: # 地址 # host: 139.186.148.21 - host: 172.16.20.72 + host: 127.0.0.1 # 端口,默认为6379 port: 6379 # 数据库索引 database: 0 # redis 密码必须配置 # password: zdxt@123 - password: + # password: # 连接超时时间 timeout: 10s # 是否开启ssl