commit 23166b08db36087285220cc91ec1d6af99c48bf9 Author: hejiayang <296714699@qq.com> Date: Mon Sep 29 16:41:23 2025 +0800 六厂扣费系统 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..58102be --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +*.js linguist-language=Java +*.css linguist-language=Java +*.html linguist-language=Java +*.vue linguist-language=Java +*.sql linguist-language=Java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d9949e6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +## ide +**/.idea +*.iml +rebel.xml + +## backend +**/target +**/logs + +## front +**/*.lock diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8eecf84 --- /dev/null +++ b/LICENSE @@ -0,0 +1,213 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2019 Jeecg Boot All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + In any case, you must not make any such use of this software as to develop software which may be considered competitive with this software. + + 开源协议补充 + JeecgBoot 是由 北京国炬信息技术有限公司 发行的软件。 总部位于北京,地址:中国·北京·朝阳区科荟前街1号院奥林佳泰大厦。邮箱:jeecgos@163.com + 本软件受适用的国家软件著作权法(包括国际条约)和双重保护许可。 + + 1.允许基于本平台软件开展业务系统开发。 + 2.不得基于该平台软件的基础,修改包装成一个与JeecgBoot平台软件功能类似的产品进行发布、销售,或与JeecgBoot参与同类软件产品市场的竞争。 + 违反此条款属于侵权行为,须赔偿侵权经济损失,同时立即停止著作权侵权行为。 + 解释权归:http://www.jeecg.com + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ef4f6b0 --- /dev/null +++ b/README.md @@ -0,0 +1,463 @@ + + +![JEECG](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/site/jeecgfengm.png "JeecgBoot低代码开发平台") + + + +JEECG BOOT 低代码开发平台(前后端分离版本) +=============== + +当前最新版本 : 3.4.1(发布日期:2022-08-12) 1 + + +[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE) +[![](https://img.shields.io/badge/Author-北京国炬软件-orange.svg)](http://www.jeecg.com) +[![](https://img.shields.io/badge/Blog-官方博客-blue.svg)](https://my.oschina.net/jeecg) +[![](https://img.shields.io/badge/version-3.4.1-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot) +[![GitHub stars](https://img.shields.io/github/stars/zhangdaiscott/jeecg-boot.svg?style=social&label=Stars)](https://github.com/zhangdaiscott/jeecg-boot) +[![GitHub forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/zhangdaiscott/jeecg-boot) + + + +项目介绍 +----------------------------------- + +

Java Low Code Platform for Enterprise web applications

+ +JeecgBoot 是一款基于代码生成器的`低代码开发平台`!前后端分离架构 SpringBoot2.x,SpringCloud,Ant Design&Vue,Mybatis-plus,Shiro,JWT,支持微服务。强大的代码生成器让前后端代码一键生成,实现低代码开发! JeecgBoot 引领新的低代码开发模式(OnlineCoding-> 代码生成器-> 手工MERGE), 帮助解决Java项目70%的重复工作,让开发更多关注业务。既能快速提高效率,节省研发成本,同时又不失灵活性! + +JeecgBoot 提供了一系列`低代码模块`,实现在线开发`真正的零代码`:Online表单开发、Online报表、报表配置能力、在线图表设计、大屏设计、移动配置能力、表单设计器、在线设计流程、流程自动化配置、插件能力(可插拔)等等! + + +`JEECG宗旨是:` 简单功能由OnlineCoding配置实现,做到`零代码开发`;复杂功能由代码生成器生成进行手工Merge 实现`低代码开发`,既保证了`智能`又兼顾`灵活`;实现了低代码开发的同时又支持灵活编码,解决了当前低代码产品普遍不灵活的弊端! + +`JEECG业务流程:` 采用工作流来实现、扩展出任务接口,供开发编写业务逻辑,表单提供多种解决方案: 表单设计器、online配置表单、编码表单。同时实现了流程与表单的分离设计(松耦合)、并支持任务节点灵活配置,既保证了公司流程的保密性,又减少了开发人员的工作量。 + + +项目源码 +----------------------------------- +| 仓库 |前端 Vue3版 | 前端 Vue2版 | 后端源码 | +|-|-|-|-| +| Github | [jeecgboot-vue3](https://github.com/jeecgboot/jeecgboot-vue3) | [ant-design-vue-jeecg](https://github.com/jeecgboot/ant-design-vue-jeecg) | [jeecg-boot](https://github.com/jeecgboot/jeecg-boot) | +| 码云 | [jeecgboot-vue3](https://gitee.com/jeecg/jeecgboot-vue3) | [ant-design-vue-jeecg](https://gitee.com/jeecg/ant-design-vue-jeecg) | [jeecg-boot](https://gitee.com/jeecg/jeecg-boot) | + + +##### 项目说明 + +| 项目名 | 说明 | +|--------------------|------------------------| +| `jeecg-boot` | SpringBoot后台源码(支持微服务) | +| `ant-design-vue-jeecg` |Vue2版前端源码 | +| `jeecgboot-vue3` | Vue3+Ts版前端源码 | +| `jeecg-boot-starter` | stater依赖项目单独维护 [下载地址](https://gitee.com/jeecg/jeecg-boot-starter) | + + +适用项目 +----------------------------------- +Jeecg-Boot低代码开发平台,可以应用在任何J2EE项目的开发中,尤其适合SAAS项目、企业信息管理系统(MIS)、内部办公系统(OA)、企业资源计划系统(ERP)、客户关系管理系统(CRM)等,其半智能手工Merge的开发方式,可以显著提高开发效率70%以上,极大降低开发成本。 + + + +技术文档 +----------------------------------- + +- 技术官网: [http://www.jeecg.com](http://www.jeecg.com) + +- 在线演示 : [Vue2版本](http://boot.jeecg.com) | [Vue3版本](http://boot3.jeecg.com) + +- 开发文档: [主项目文档](http://doc.jeecg.com) | [Vue3文档](http://vue3.jeecg.com) + +- 新手指南: [快速入门](http://www.jeecg.com/doc/quickstart) | [视频教程](https://space.bilibili.com/454617261/channel/series) | [常见问题 ](http://www.jeecg.com/doc/qa) | [技术支持](http://jeecg.com/doc/help) | [1分钟体验低代码](https://my.oschina.net/jeecg/blog/3083313) + +- 微服务开发: [单体升级为微服务](http://doc.jeecg.com/3043471) + +- QQ交流群 : ⑥730954414、VUE3群683903138、⑤860162132(满)、④774126647(满)、③816531124(满)、②769925425(满)、①284271917(满) +> ` 提醒:【QQ群是自助服务群,建议给帮助您解决问题的同学发送指定红包,表示感谢!】 ` + + + + +后台目录结构 +----------------------------------- +``` +项目结构 +├─jeecg-boot-parent(父POM: 项目依赖、modules组织) +│ ├─jeecg-boot-base-core(共通模块: 工具类、config、权限、查询过滤器、注解等) +│ ├─jeecg-module-demo 示例代码 +│ ├─jeecg-module-system System系统管理目录 +│ │ ├─jeecg-system-biz System系统管理权限等功能 +│ │ ├─jeecg-system-start System单体启动项目(8080) +│ │ ├─jeecg-system-api System系统管理模块对外api +│ │ │ ├─jeecg-system-cloud-api System模块对外提供的微服务接口 +│ │ │ ├─jeecg-system-local-api System模块对外提供的单体接口 +│ ├─jeecg-server-cloud --微服务模块 + ├─jeecg-cloud-gateway --微服务网关模块(9999) + ├─jeecg-cloud-nacos --Nacos服务模块(8848) + ├─jeecg-system-cloud-start --System微服务启动项目(7001) + ├─jeecg-demo-cloud-start --Demo微服务启动项目(7002) + ├─jeecg-visual + ├─jeecg-cloud-monitor --微服务监控模块 (9111) + ├─jeecg-cloud-xxljob --微服务xxljob定时任务服务端 (9080) + ├─jeecg-cloud-sentinel --sentinel服务端 (9000) + ├─jeecg-cloud-test -- 微服务测试示例(各种例子) + ├─jeecg-cloud-test-more -- 微服务测试示例(feign、熔断降级、xxljob、分布式锁) + ├─jeecg-cloud-test-rabbitmq -- 微服务测试示例(rabbitmq) + ├─jeecg-cloud-test-seata -- 微服务测试示例(seata分布式事务) + ├─jeecg-cloud-test-shardingsphere -- 微服务测试示例(分库分表) +``` + +Docker启动项目 +----------------------------------- +- [Docker启动单体后台](http://doc.jeecg.com/2043889) +- [Docker启动微服务后台](http://doc.jeecg.com/3043472) +- [Docker启动Vue3前端](http://vue3.jeecg.com/3028878) +- [Docker启动Vue2前端](http://doc.jeecg.com/3043612) + + + +为什么选择JEECG-BOOT? +----------------------------------- +* 1.采用最新主流前后分离框架(Springboot+Mybatis+antd),容易上手; 代码生成器依赖性低,灵活的扩展能力,可快速实现二次开发; +* 2.支持微服务SpringCloud Alibaba(Nacos、Gateway、Sentinel、Skywalking),提供切换机制支持单体和微服务自由切换 +* 3.开发效率高,采用代码生成器,单表、树列表、一对多、一对一等数据模型,增删改查功能一键生成,菜单配置直接使用; +* 4.代码生成器提供强大模板机制,支持自定义模板,目前提供四套风格模板(单表两套、树模型一套、一对多三套) +* 5.代码生成器非常智能,在线业务建模、在线配置、所见即所得支持23种类控件,一键生成前后端代码,大幅度提升开发效率,不再为重复工作发愁。 +* 6.低代码能力:Online在线表单(无需编码,通过在线配置表单,实现表单的增删改查,支持单表、树、一对多、一对一等模型,实现人人皆可编码) +* 7.低代码能力:Online在线报表(无需编码,通过在线配置方式,实现数据报表,可以快速抽取数据,减轻开发压力,实现人人皆可编码) +* 8.低代码能力:Online在线图表(无需编码,通过在线配置方式,实现曲线图,柱状图,数据报表等,支持自定义排版布局,实现人人皆可编码) +* 9.封装完善的用户、角色、菜单、组织机构、数据字典、在线定时任务等基础功能,支持访问授权、按钮权限、数据权限等功能 +* 10.常用共通封装,各种工具类(定时任务,短信接口,邮件发送,Excel导入导出等),基本满足80%项目需求 +* 11.简易Excel导入导出,支持单表导出和一对多表模式导出,生成的代码自带导入导出功能 +* 12.集成简易报表工具,图像报表和数据导出非常方便,可极其方便的生成图形报表、pdf、excel、word等报表; +* 13.采用前后分离技术,页面UI风格精美,针对常用组件做了封装:时间、行表格控件、截取显示控件、报表组件,编辑器等等 +* 14.查询过滤器:查询功能自动生成,后台动态拼SQL追加查询条件;支持多种匹配方式(全匹配/模糊查询/包含查询/不匹配查询); +* 15.数据权限(精细化数据权限控制,控制到行级,列表级,表单字段级,实现不同人看不同数据,不同人对同一个页面操作不同字段 +* 16.页面校验自动生成(必须输入、数字校验、金额校验、时间空间等); +* 17.支持SAAS服务模式,提供SaaS多租户架构方案。 +* 18.分布式文件服务,集成minio、阿里OSS等优秀的第三方,提供便捷的文件上传与管理,同时也支持本地存储。 +* 19.主流数据库兼容,一套代码完全兼容Mysql、Postgresql、Oracle、Sqlserver、MariaDB、达梦等主流数据库。 +* 20.集成工作流activiti,并实现了只需在页面配置流程转向,可极大的简化bpm工作流的开发;用bpm的流程设计器画出了流程走向,一个工作流基本就完成了,只需写很少量的java代码; +* 21.低代码能力:在线流程设计,采用开源Activiti流程引擎,实现在线画流程,自定义表单,表单挂靠,业务流转 +* 22.多数据源:及其简易的使用方式,在线配置数据源配置,便捷的从其他数据抓取数据; +* 23.提供单点登录CAS集成方案,项目中已经提供完善的对接代码 +* 24.低代码能力:表单设计器,支持用户自定义表单布局,支持单表,一对多表单、支持select、radio、checkbox、textarea、date、popup、列表、宏等控件 +* 25.专业接口对接机制,统一采用restful接口方式,集成swagger-ui在线接口文档,Jwt token安全验证,方便客户端对接 +* 26.接口安全机制,可细化控制接口授权,非常简便实现不同客户端只看自己数据等控制 +* 27.高级组合查询功能,在线配置支持主子表关联查询,可保存查询历史 +* 28.提供各种系统监控,实时跟踪系统运行情况(监控 Redis、Tomcat、jvm、服务器信息、请求追踪、SQL监控) +* 29.消息中心(支持短信、邮件、微信推送等等) +* 30.集成Websocket消息通知机制 +* 31.移动自适应效果优秀,提供APP发布方案: +* 32.支持多语言,提供国际化方案; +* 33.数据变更记录日志,可记录数据每次变更内容,通过版本对比功能查看历史变化 +* 34.平台UI强大,实现了移动自适应 +* 35.平台首页风格,提供多种组合模式,支持自定义风格 +* 36.提供简单易用的打印插件,支持谷歌、火狐、IE11+ 等各种浏览器 +* 37.示例代码丰富,提供很多学习案例参考 +* 38.采用maven分模块开发方式 +* 39.支持菜单动态路由 +* 40.权限控制采用 RBAC(Role-Based Access Control,基于角色的访问控制) +* 41.提供新行编辑表格JVXETable,轻松满足各种复杂ERP布局,拥有更高的性能、更灵活的扩展、更强大的功能 + + + + +技术架构: +----------------------------------- +#### 开发环境 + +- 语言:Java 8+ (小于17) + +- IDE(JAVA): IDEA (必须安装lombok插件 ) + +- IDE(前端): Vscode、WebStorm、IDEA + +- 依赖管理:Maven + +- 缓存:Redis + +- 数据库脚本:MySQL5.7+ & Oracle 11g & Sqlserver2017(其他数据库,[需要自己转](https://my.oschina.net/jeecg/blog/4905722)) + + +#### 后端 + +- 基础框架:Spring Boot 2.6.6 + +- 微服务框架: Spring Cloud Alibaba 2021.0.1.0 + +- 持久层框架:MybatisPlus 3.5.1 + +- 报表工具: JimuReport 1.5.2 + +- 安全框架:Apache Shiro 1.8.0,Jwt 3.11.0 + +- 微服务技术栈:Spring Cloud Alibaba、Nacos、Gateway、Sentinel、Skywalking + +- 数据库连接池:阿里巴巴Druid 1.1.22 + +- 日志打印:logback + +- 其他:autopoi, fastjson,poi,Swagger-ui,quartz, lombok(简化代码)等。 + + +#### 前端 + +- Vue2版本:`Vue2.6+@vue/cli+AntDesignVue+Viser-vue+Vuex等` [详细查看](https://github.com/jeecgboot/ant-design-vue-jeecg) +- Vue3版本:`Vue3.0+TypeScript+Vite+AntDesignVue+pinia+echarts等新方案` [详细查看](https://github.com/jeecgboot/jeecgboot-vue3) + +#### 支持库 + +| 数据库 | 支持 | +| --- | --- | +| MySQL | √ | +| Oracle11g | √ | +| Sqlserver2017 | √ | +| PostgreSQL | √ | +| MariaDB | √ | +| 达梦、人大金仓 | √ | + + + +## 微服务解决方案 + + +1、服务注册和发现 Nacos √ + +2、统一配置中心 Nacos √ + +3、路由网关 gateway(三种加载方式) √ + +4、分布式 http feign √ + +5、熔断降级限流 Sentinel √ + +6、分布式文件 Minio、阿里OSS √ + +7、统一权限控制 JWT + Shiro √ + +8、服务监控 SpringBootAdmin√ + +9、链路跟踪 Skywalking [参考文档](http://doc.jeecg.com/2350293) + +10、消息中间件 RabbitMQ √ + +11、分布式任务 xxl-job √ + +12、分布式事务 Seata + +13、分布式日志 elk + kafka + +14、支持 docker-compose、k8s、jenkins + +15、CAS 单点登录 √ + +16、路由限流 √ + + +#### 微服务架构图 +![微服务架构图](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/jeecgboot_springcloud2022.png "在这里输入图片标题") + +### Jeecg Boot 产品功能蓝图 +![功能蓝图](https://jeecgos.oss-cn-beijing.aliyuncs.com/upload/test/Jeecg-Boot-lantu202005_1590912449914.jpg "在这里输入图片标题") + + + + +### 功能模块 +``` +├─系统管理 +│ ├─用户管理 +│ ├─角色管理 +│ ├─菜单管理 +│ ├─权限设置(支持按钮权限、数据权限) +│ ├─表单权限(控制字段禁用、隐藏) +│ ├─部门管理 +│ ├─我的部门(二级管理员) +│ └─字典管理 +│ └─分类字典 +│ └─系统公告 +│ └─职务管理 +│ └─通讯录 +│ └─多租户管理 +├─消息中心 +│ ├─消息管理 +│ ├─模板管理 +├─代码生成器(低代码) +│ ├─代码生成器功能(一键生成前后端代码,生成后无需修改直接用,绝对是后端开发福音) +│ ├─代码生成器模板(提供4套模板,分别支持单表和一对多模型,不同风格选择) +│ ├─代码生成器模板(生成代码,自带excel导入导出) +│ ├─查询过滤器(查询逻辑无需编码,系统根据页面配置自动生成) +│ ├─高级查询器(弹窗自动组合查询条件) +│ ├─Excel导入导出工具集成(支持单表,一对多 导入导出) +│ ├─平台移动自适应支持 +├─系统监控 +│ ├─Gateway路由网关 +│ ├─性能扫描监控 +│ │ ├─监控 Redis +│ │ ├─Tomcat +│ │ ├─jvm +│ │ ├─服务器信息 +│ │ ├─请求追踪 +│ │ ├─磁盘监控 +│ ├─定时任务 +│ ├─系统日志 +│ ├─消息中心(支持短信、邮件、微信推送等等) +│ ├─数据日志(记录数据快照,可对比快照,查看数据变更情况) +│ ├─系统通知 +│ ├─SQL监控 +│ ├─swagger-ui(在线接口文档) +│─报表示例 +│ ├─曲线图 +│ └─饼状图 +│ └─柱状图 +│ └─折线图 +│ └─面积图 +│ └─雷达图 +│ └─仪表图 +│ └─进度条 +│ └─排名列表 +│ └─等等 +│─大屏模板 +│ ├─作战指挥中心大屏 +│ └─物流服务中心大屏 +│─常用示例 +│ ├─自定义组件 +│ ├─对象存储(对接阿里云) +│ ├─JVXETable示例(各种复杂ERP布局示例) +│ ├─单表模型例子 +│ └─一对多模型例子 +│ └─打印例子 +│ └─一对多TAB例子 +│ └─内嵌table例子 +│ └─常用选择组件 +│ └─异步树table +│ └─接口模拟测试 +│ └─表格合计示例 +│ └─异步树列表示例 +│ └─一对多JEditable +│ └─JEditable组件示例 +│ └─图片拖拽排序 +│ └─图片翻页 +│ └─图片预览 +│ └─PDF预览 +│ └─分屏功能 +│─封装通用组件 +│ ├─行编辑表格JEditableTable +│ └─省略显示组件 +│ └─时间控件 +│ └─高级查询 +│ └─用户选择组件 +│ └─报表组件封装 +│ └─字典组件 +│ └─下拉多选组件 +│ └─选人组件 +│ └─选部门组件 +│ └─通过部门选人组件 +│ └─封装曲线、柱状图、饼状图、折线图等等报表的组件(经过封装,使用简单) +│ └─在线code编辑器 +│ └─上传文件组件 +│ └─验证码组件 +│ └─树列表组件 +│ └─表单禁用组件 +│ └─等等 +│─更多页面模板 +│ ├─各种高级表单 +│ ├─各种列表效果 +│ └─结果页面 +│ └─异常页面 +│ └─个人页面 +├─高级功能 +│ ├─系统编码规则 +│ ├─提供单点登录CAS集成方案 +│ ├─提供APP发布方案 +│ ├─集成Websocket消息通知机制 +├─Online在线开发(低代码) +│ ├─Online在线表单 - 功能已开放 +│ ├─Online代码生成器 - 功能已开放 +│ ├─Online在线报表 - 功能已开放 +│ ├─Online在线图表(未开源) +│ ├─Online图表模板配置(未开源) +│ ├─Online布局设计(未开源) +│ ├─多数据源管理 - 功能已开放 +├─积木报表设计器(低代码) +│ ├─打印设计器 +│ ├─数据报表设计 +│ ├─图形报表设计(支持echart) +│ ├─大屏设计器(未开源) +│─流程模块功能 (未开源) +│ ├─流程设计器 +│ ├─表单设计器 + ├─大屏设计器 + ├─门户设计/仪表盘设计器 +│ └─我的任务 +│ └─历史流程 +│ └─历史流程 +│ └─流程实例管理 +│ └─流程监听管理 +│ └─流程表达式 +│ └─我发起的流程 +│ └─我的抄送 +│ └─流程委派、抄送、跳转 +│ └─。。。 +│─OA办公组件 (未开源) +│ ├─更多功能 +│ └─。。。 +└─其他模块 + └─更多功能开发中。。 + +``` + + + + + +系统效果 +---- +##### 大屏模板 +![输入图片说明](https://static.oschina.net/uploads/img/201912/25133248_Ag1C.jpg "在这里输入图片标题") + +![输入图片说明](https://static.oschina.net/uploads/img/201912/25133301_k9Kc.jpg "在这里输入图片标题") + +##### PC端 +![输入图片说明](https://static.oschina.net/uploads/img/201904/14155402_AmlV.png "在这里输入图片标题") +![输入图片说明](https://static.oschina.net/uploads/img/201904/14160657_cHwb.png "在这里输入图片标题") +![输入图片说明](https://static.oschina.net/uploads/img/201904/14160813_KmXS.png "在这里输入图片标题") +![输入图片说明](https://static.oschina.net/uploads/img/201904/14160935_Nibs.png "在这里输入图片标题") +![输入图片说明](https://static.oschina.net/uploads/img/201904/14161004_bxQ4.png "在这里输入图片标题") + + +##### 在线接口文档 +![输入图片说明](https://static.oschina.net/uploads/img/201908/27095258_M2Xq.png "在这里输入图片标题") +![输入图片说明](https://static.oschina.net/uploads/img/201904/14160957_hN3X.png "在这里输入图片标题") + + +##### 报表 +![输入图片说明](https://static.oschina.net/uploads/img/201904/14160828_pkFr.png "在这里输入图片标题") +![输入图片说明](https://static.oschina.net/uploads/img/201904/14160834_Lo23.png "在这里输入图片标题") +![输入图片说明](https://static.oschina.net/uploads/img/201904/14160842_QK7B.png "在这里输入图片标题") +![输入图片说明](https://static.oschina.net/uploads/img/201904/14160849_GBm5.png "在这里输入图片标题") +![输入图片说明](https://static.oschina.net/uploads/img/201904/14160858_6RAM.png "在这里输入图片标题") + +##### 流程 +![输入图片说明](https://static.oschina.net/uploads/img/201904/14160623_8fwk.png "在这里输入图片标题") +![输入图片说明](https://static.oschina.net/uploads/img/201904/14160917_9Ftz.png "在这里输入图片标题") +![输入图片说明](https://static.oschina.net/uploads/img/201904/14160633_u59G.png "在这里输入图片标题") +![输入图片说明](https://static.oschina.net/uploads/img/201907/05165142_yyQ7.png "在这里输入图片标题") + + +##### 手机端 +![](https://oscimg.oschina.net/oscnet/da543c5d0d57baab0cecaa4670c8b68c521.jpg) +![](https://oscimg.oschina.net/oscnet/fda4bd82cab9d682de1c1fbf2060bf14fa6.jpg) + +##### PAD端 +![](https://oscimg.oschina.net/oscnet/e90fef970a8c33790ab03ffd6c4c7cec225.jpg) +![](https://oscimg.oschina.net/oscnet/d78218803a9e856a0aa82b45efc49849a0c.jpg) +![](https://oscimg.oschina.net/oscnet/0404054d9a12647ef6f82cf9cfb80a5ac02.jpg) +![](https://oscimg.oschina.net/oscnet/59c23b230f52384e588ee16309b44fa20de.jpg) + + +## 捐赠 + +如果觉得还不错,请作者喝杯咖啡吧 ☺ + +![](https://static.oschina.net/uploads/img/201903/08155608_0EFX.png) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..9820531 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,42 @@ +version: '2' +services: + jeecg-boot-mysql: + build: + context: ./db + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_ROOT_HOST: '%' + TZ: Asia/Shanghai + restart: always + container_name: jeecg-boot-mysql + image: jeecg-boot-mysql + command: + --character-set-server=utf8mb4 + --collation-server=utf8mb4_general_ci + --explicit_defaults_for_timestamp=true + --lower_case_table_names=1 + --max_allowed_packet=128M + --default-authentication-plugin=caching_sha2_password + ports: + - 3306:3306 + + jeecg-boot-redis: + image: redis:5.0 + ports: + - 6379:6379 + restart: always + hostname: jeecg-boot-redis + container_name: jeecg-boot-redis + + jeecg-boot-system: + build: + context: ./jeecg-module-system/jeecg-system-start + restart: on-failure + depends_on: + - jeecg-boot-mysql + - jeecg-boot-redis + container_name: jeecg-boot-system + image: jeecg-boot-system + hostname: jeecg-boot-system + ports: + - 8080:8080 \ No newline at end of file diff --git a/jeecg-boot-base-core/pom.xml b/jeecg-boot-base-core/pom.xml new file mode 100644 index 0000000..6135f37 --- /dev/null +++ b/jeecg-boot-base-core/pom.xml @@ -0,0 +1,249 @@ + + + jeecg-boot-parent + org.jeecgframework.boot + 3.4.0 + + 4.0.0 + + jeecg-boot-base-core + + + + + org.jeecgframework.boot + jeecg-boot-common + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-websocket + + + org.springframework.boot + spring-boot-starter-mail + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.boot + spring-boot-starter-validation + + + + io.micrometer + micrometer-registry-prometheus + + + + + commons-io + commons-io + ${commons.version} + + + commons-lang + commons-lang + ${commons.version} + + + + org.springframework.boot + spring-boot-starter-freemarker + + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + + com.alibaba + druid-spring-boot-starter + ${druid.version} + + + + + com.baomidou + dynamic-datasource-spring-boot-starter + ${dynamic-datasource-spring-boot-starter.version} + + + + + + mysql + mysql-connector-java + ${mysql-connector-java.version} + runtime + + + + com.microsoft.sqlserver + sqljdbc4 + ${sqljdbc4.version} + runtime + + + + com.oracle + ojdbc6 + ${ojdbc6.version} + runtime + + + + org.postgresql + postgresql + ${postgresql.version} + runtime + + + + + org.springframework.boot + spring-boot-starter-quartz + + + + + com.auth0 + java-jwt + ${java-jwt.version} + + + + + org.apache.shiro + shiro-spring-boot-starter + ${shiro.version} + + + + org.crazycake + shiro-redis + ${shiro-redis.version} + + + org.apache.shiro + shiro-core + + + + com.puppycrawl.tools + checkstyle + + + + + + + com.github.xiaoymin + knife4j-spring-boot-starter + ${knife4j-spring-boot-starter.version} + + + + + + org.jeecgframework.boot + codegenerate + ${codegenerate.version} + + + + + org.jeecgframework + autopoi-web + ${autopoi-web.version} + + + commons-codec + commons-codec + + + + + + + io.minio + minio + + + com.google.guava + guava + ${guava.version} + + + + + com.aliyun + aliyun-java-sdk-dysmsapi + ${aliyun-java-sdk-dysmsapi.version} + + + + com.aliyun.oss + aliyun-sdk-oss + ${aliyun.oss.version} + + + + com.xkcoding.justauth + justauth-spring-boot-starter + + + com.squareup.okhttp3 + okhttp + + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + + + org.springframework.boot + spring-boot-starter-activemq + + + + org.apache.activemq + activemq-pool + 5.15.0 + + + + net.coobird + thumbnailator + 0.4.17 + + + org.apache.httpcomponents + httpmime + + + + + + + \ No newline at end of file diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/CommonAPI.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/CommonAPI.java new file mode 100644 index 0000000..892ec6f --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/CommonAPI.java @@ -0,0 +1,124 @@ +package org.jeecg.common.api; + +import org.jeecg.common.system.vo.*; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface CommonAPI { + + /** + * 1查询用户角色信息 + * @param username + * @return + */ + Set queryUserRoles(String username); + + + /** + * 2查询用户权限信息 + * @param username + * @return + */ + Set queryUserAuths(String username); + + /** + * 3根据 id 查询数据库中存储的 DynamicDataSourceModel + * + * @param dbSourceId + * @return + */ +// DynamicDataSourceModel getDynamicDbSourceById(String dbSourceId); + + /** + * 4根据 code 查询数据库中存储的 DynamicDataSourceModel + * + * @param dbSourceCode + * @return + */ + DynamicDataSourceModel getDynamicDbSourceByCode(String dbSourceCode); + + /** + * 5根据用户账号查询用户信息 + * @param userId + * @return + */ + public LoginUser getUserById(String userId); + + + /** + * 6字典表的 翻译 + * @param table + * @param text + * @param code + * @param key + * @return + */ + //输入验证-重定向-高危 +// String translateDictFromTable(String table, String text, String code, String key); + +// /** +// * 7普通字典的翻译 +// * @param code +// * @param key +// * @return +// */ +// String translateDict(String code, String key); + + /** + * 8查询数据权限 + * @return + */ + List queryPermissionDataRule(String component, String requestPath, String userId); + + + /** + * 9查询用户信息 + * @param userId + * @return + */ + SysUserCacheInfo getCacheUser(String userId); + + /** + * 10获取数据字典 + * @param code + * @return + */ + public List queryDictItemsByCode(String code); + + /** + * 获取有效的数据字典项 + * @param code + * @return + */ +// public List queryEnableDictItemsByCode(String code); + + /** + * 13获取表数据字典 + * @param table + * @param text + * @param code + * @return + */ + List queryTableDictItemsByCode(String table, String text, String code); + + /** + * 14 普通字典的翻译,根据多个dictCode和多条数据,多个以逗号分割 + * @param dictCodes 例如:user_status,sex + * @param keys 例如:1,2,0 + * @return + */ + Map> translateManyDict(String dictCodes, String keys); + + /** + * 15 字典表的 翻译,可批量 + * @param table + * @param text + * @param code + * @param keys 多个用逗号分割 + * @return + */ + List translateDictFromTableByKeys(String table, String text, String code, String keys); + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/FileDownDTO.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/FileDownDTO.java new file mode 100644 index 0000000..552655c --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/FileDownDTO.java @@ -0,0 +1,31 @@ +package org.jeecg.common.api.dto; + +import lombok.Data; + +import javax.servlet.http.HttpServletResponse; +import java.io.Serializable; + +/** + * 文件下载 + * cloud api 用到的接口传输对象 + * @author: jeecg-boot + */ +@Data +public class FileDownDTO implements Serializable { + + private static final long serialVersionUID = 6749126258686446019L; + + private String filePath; + private String uploadpath; + private String uploadType; + private HttpServletResponse response; + + public FileDownDTO(){} + + public FileDownDTO(String filePath, String uploadpath, String uploadType,HttpServletResponse response){ + this.filePath = filePath; + this.uploadpath = uploadpath; + this.uploadType = uploadType; + this.response = response; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/FileUploadDTO.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/FileUploadDTO.java new file mode 100644 index 0000000..8862d35 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/FileUploadDTO.java @@ -0,0 +1,56 @@ +package org.jeecg.common.api.dto; + +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +import java.io.Serializable; + +/** + * 文件上传 + * cloud api 用到的接口传输对象 + * @author: jeecg-boot + */ +@Data +public class FileUploadDTO implements Serializable { + + private static final long serialVersionUID = -4111953058578954386L; + + private MultipartFile file; + + private String bizPath; + + private String uploadType; + + private String customBucket; + + public FileUploadDTO(){ + + } + + /** + * 简单上传 构造器1 + * @param file + * @param bizPath + * @param uploadType + */ + public FileUploadDTO(MultipartFile file,String bizPath,String uploadType){ + this.file = file; + this.bizPath = bizPath; + this.uploadType = uploadType; + } + + /** + * 申明桶 文件上传 构造器2 + * @param file + * @param bizPath + * @param uploadType + * @param customBucket + */ + public FileUploadDTO(MultipartFile file,String bizPath,String uploadType,String customBucket){ + this.file = file; + this.bizPath = bizPath; + this.uploadType = uploadType; + this.customBucket = customBucket; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/LogDTO.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/LogDTO.java new file mode 100644 index 0000000..e50ee7f --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/LogDTO.java @@ -0,0 +1,69 @@ +package org.jeecg.common.api.dto; +import lombok.Data; +import org.jeecg.common.system.vo.LoginUser; +import java.io.Serializable; +import java.util.Date; + +/** + * 日志对象 + * cloud api 用到的接口传输对象 + * @author: jeecg-boot + */ +@Data +public class LogDTO implements Serializable { + + private static final long serialVersionUID = 8482720462943906924L; + + /**内容*/ + private String logContent; + + /**日志类型(0:操作日志;1:登录日志;2:定时任务) */ + private Integer logType; + + /**操作类型(1:添加;2:修改;3:删除;) */ + private Integer operateType; + + /**登录用户 */ + private LoginUser loginUser; + + private String id; + private String createBy; + private Date createTime; + private Long costTime; + private String ip; + + /**请求参数 */ + private String requestParam; + + /**请求类型*/ + private String requestType; + + /**请求路径*/ + private String requestUrl; + + /**请求方法 */ + private String method; + + /**操作人用户名称*/ + private String username; + + /**操作人用户账户*/ + private String userid; + + public LogDTO(){ + + } + + public LogDTO(String logContent, Integer logType, Integer operatetype){ + this.logContent = logContent; + this.logType = logType; + this.operateType = operatetype; + } + + public LogDTO(String logContent, Integer logType, Integer operatetype, LoginUser loginUser){ + this.logContent = logContent; + this.logType = logType; + this.operateType = operatetype; + this.loginUser = loginUser; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/OnlineAuthDTO.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/OnlineAuthDTO.java new file mode 100644 index 0000000..5407a6d --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/OnlineAuthDTO.java @@ -0,0 +1,42 @@ +package org.jeecg.common.api.dto; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * online 拦截器权限判断 + * cloud api 用到的接口传输对象 + * @author: jeecg-boot + */ +@Data +public class OnlineAuthDTO implements Serializable { + private static final long serialVersionUID = 1771827545416418203L; + + + /** + * 用户名 + */ + private String username; + + /** + * 可能的请求地址 + */ + private List possibleUrl; + + /** + * online开发的菜单地址 + */ + private String onlineFormUrl; + + public OnlineAuthDTO(){ + + } + + public OnlineAuthDTO(String username, List possibleUrl, String onlineFormUrl){ + this.username = username; + this.possibleUrl = possibleUrl; + this.onlineFormUrl = onlineFormUrl; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/BusMessageDTO.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/BusMessageDTO.java new file mode 100644 index 0000000..28f2c39 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/BusMessageDTO.java @@ -0,0 +1,44 @@ +package org.jeecg.common.api.dto.message; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 带业务参数的消息 + * @author: jeecg-boot + */ +@Data +public class BusMessageDTO extends MessageDTO implements Serializable { + + private static final long serialVersionUID = 9104793287983367669L; + /** + * 业务类型 + */ + private String busType; + + /** + * 业务id + */ + private String busId; + + public BusMessageDTO(){ + + } + + /** + * 构造 带业务参数的消息 + * @param fromUser + * @param toUser + * @param title + * @param msgContent + * @param msgCategory + * @param busType + * @param busId + */ + public BusMessageDTO(String fromUser, String toUser, String title, String msgContent, String msgCategory, String busType, String busId){ + super(fromUser, toUser, title, msgContent, msgCategory); + this.busId = busId; + this.busType = busType; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/BusTemplateMessageDTO.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/BusTemplateMessageDTO.java new file mode 100644 index 0000000..bcc3476 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/BusTemplateMessageDTO.java @@ -0,0 +1,46 @@ +package org.jeecg.common.api.dto.message; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Map; + +/** + * 带业务参数的模板消息 + * @author: jeecg-boot + */ +@Data +public class BusTemplateMessageDTO extends TemplateMessageDTO implements Serializable { + + private static final long serialVersionUID = -4277810906346929459L; + + /** + * 业务类型 + */ + private String busType; + + /** + * 业务id + */ + private String busId; + + public BusTemplateMessageDTO(){ + + } + + /** + * 构造 带业务参数的模板消息 + * @param fromUser + * @param toUser + * @param title + * @param templateParam + * @param templateCode + * @param busType + * @param busId + */ + public BusTemplateMessageDTO(String fromUser, String toUser, String title, Map templateParam, String templateCode, String busType, String busId){ + super(fromUser, toUser, title, templateParam, templateCode); + this.busId = busId; + this.busType = busType; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/MessageDTO.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/MessageDTO.java new file mode 100644 index 0000000..777c2c4 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/MessageDTO.java @@ -0,0 +1,90 @@ +package org.jeecg.common.api.dto.message; + +import lombok.Data; +import org.jeecg.common.constant.CommonConstant; + +import java.io.Serializable; +import java.util.Map; + +/** + * 普通消息 + * @author: jeecg-boot + */ +@Data +public class MessageDTO implements Serializable { + + private static final long serialVersionUID = -5690444483968058442L; + + /** + * 发送人(用户登录账户) + */ + protected String fromUser; + + /** + * 发送给(用户登录账户) + */ + protected String toUser; + + /** + * 发送给所有人 + */ + protected Boolean toAll; + + /** + * 消息主题 + */ + protected String title; + + /** + * 消息内容 + */ + protected String content; + + /** + * 消息类型 1:消息 2:系统消息 + */ + protected String category; + + + public MessageDTO(){ + + } + + /** + * 构造器1 系统消息 + */ + public MessageDTO(String fromUser,String toUser,String title, String content){ + this.fromUser = fromUser; + this.toUser = toUser; + this.title = title; + this.content = content; + //默认 都是2系统消息 + this.category = CommonConstant.MSG_CATEGORY_2; + } + + /** + * 构造器2 支持设置category 1:消息 2:系统消息 + */ + public MessageDTO(String fromUser,String toUser,String title, String content, String category){ + this.fromUser = fromUser; + this.toUser = toUser; + this.title = title; + this.content = content; + this.category = category; + } + + /** + * 模板消息对应的模板编码 + */ + protected String templateCode; + /** + * 消息类型:org.jeecg.common.constant.enums.MessageTypeEnum + */ + protected String type; + + /** + * 解析模板内容 对应的数据 + */ + protected Map data; + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/TemplateDTO.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/TemplateDTO.java new file mode 100644 index 0000000..39e8f9b --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/TemplateDTO.java @@ -0,0 +1,38 @@ +package org.jeecg.common.api.dto.message; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Map; + +/** + * 消息模板dto + * @author: jeecg-boot + */ +@Data +public class TemplateDTO implements Serializable { + + private static final long serialVersionUID = 5848247133907528650L; + + /** + * 模板编码 + */ + protected String templateCode; + + /** + * 模板参数 + */ + protected Map templateParam; + + /** + * 构造器 通过设置模板参数和模板编码 作为参数获取消息内容 + */ + public TemplateDTO(String templateCode, Map templateParam){ + this.templateCode = templateCode; + this.templateParam = templateParam; + } + + public TemplateDTO(){ + + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/TemplateMessageDTO.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/TemplateMessageDTO.java new file mode 100644 index 0000000..4942717 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/dto/message/TemplateMessageDTO.java @@ -0,0 +1,49 @@ +package org.jeecg.common.api.dto.message; + +import lombok.Data; +import java.io.Serializable; +import java.util.Map; + +/** + * 模板消息 + * @author: jeecg-boot + */ +@Data +public class TemplateMessageDTO extends TemplateDTO implements Serializable { + + private static final long serialVersionUID = 411137565170647585L; + + + /** + * 发送人(用户登录账户) + */ + protected String fromUser; + + /** + * 发送给(用户登录账户) + */ + protected String toUser; + + /** + * 消息主题 + */ + protected String title; + + + public TemplateMessageDTO(){ + + } + + /** + * 构造器1 发模板消息用 + */ + public TemplateMessageDTO(String fromUser, String toUser,String title, Map templateParam, String templateCode){ + super(templateCode, templateParam); + this.fromUser = fromUser; + this.toUser = toUser; + this.title = title; + } + + + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/vo/ActiveMqEntityVO.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/vo/ActiveMqEntityVO.java new file mode 100644 index 0000000..090be4b --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/vo/ActiveMqEntityVO.java @@ -0,0 +1,27 @@ +package org.jeecg.common.api.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * @Description: ActiveMq传输实体类vo + * @Author: jeecg-boot + * @Date: 2022-04-28 + * @Version: V1.0 + */ +@Data +@ApiModel(value="ActiveMqEntityVO 对象", description="ActiveMq传输实体类vo") +public class ActiveMqEntityVO implements Serializable { + private static final long serialVersionUID = 1L; + + /** Entity 对象json字符串*/ + @ApiModelProperty(value = "Entity对象json字符串") + private String dataJsonString; + + /**对象反射路径:通过Entity.getClass().getName();获取*/ + @ApiModelProperty(value = "对象反射路径:通过Entity.getClass().getName();获取") + private String entityReference; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/vo/Result.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/vo/Result.java new file mode 100644 index 0000000..53b2397 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/vo/Result.java @@ -0,0 +1,177 @@ +package org.jeecg.common.api.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.jeecg.common.constant.CommonConstant; + +import java.io.Serializable; + +/** + * 接口返回数据格式 + * @author scott + * @email jeecgos@163.com + * @date 2019年1月19日 + */ +@Data +@ApiModel(value="接口返回对象", description="接口返回对象") +public class Result implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 成功标志 + */ + @ApiModelProperty(value = "成功标志") + private boolean success = true; + + /** + * 返回处理消息 + */ + @ApiModelProperty(value = "返回处理消息") + private String message = ""; + + /** + * 返回代码 + */ + @ApiModelProperty(value = "返回代码") + private Integer code = 0; + + /** + * 返回数据对象 data + */ + @ApiModelProperty(value = "返回数据对象") + private T result; + + /** + * 时间戳 + */ + @ApiModelProperty(value = "时间戳") + private long timestamp = System.currentTimeMillis(); + + public Result() { + } + + /** + * 兼容VUE3版token失效不跳转登录页面 + * @param code + * @param message + */ + public Result(Integer code, String message) { + this.code = code; + this.message = message; + } + + public Result success(String message) { + this.message = message; + this.code = CommonConstant.SC_OK_200; + this.success = true; + return this; + } + + public static Result ok() { + Result r = new Result(); + r.setSuccess(true); + r.setCode(CommonConstant.SC_OK_200); + return r; + } + + public static Result ok(String msg) { + Result r = new Result(); + r.setSuccess(true); + r.setCode(CommonConstant.SC_OK_200); + //Result OK(String msg)方法会造成兼容性问题 issues/I4IP3D + r.setResult((T) msg); + r.setMessage(msg); + return r; + } + + public static Result ok(T data) { + Result r = new Result(); + r.setSuccess(true); + r.setCode(CommonConstant.SC_OK_200); + r.setResult(data); + return r; + } + + public static Result OK() { + Result r = new Result(); + r.setSuccess(true); + r.setCode(CommonConstant.SC_OK_200); + return r; + } + + /** + * 此方法是为了兼容升级所创建 + * + * @param msg + * @param + * @return + */ + public static Result OK(String msg) { + Result r = new Result(); + r.setSuccess(true); + r.setCode(CommonConstant.SC_OK_200); + r.setMessage(msg); + //Result OK(String msg)方法会造成兼容性问题 issues/I4IP3D + r.setResult((T) msg); + return r; + } + + public static Result OK(T data) { + Result r = new Result(); + r.setSuccess(true); + r.setCode(CommonConstant.SC_OK_200); + r.setResult(data); + return r; + } + + public static Result OK(String msg, T data) { + Result r = new Result(); + r.setSuccess(true); + r.setCode(CommonConstant.SC_OK_200); + r.setMessage(msg); + r.setResult(data); + return r; + } + + public static Result error(String msg, T data) { + Result r = new Result(); + r.setSuccess(false); + r.setCode(CommonConstant.SC_INTERNAL_SERVER_ERROR_500); + r.setMessage(msg); + r.setResult(data); + return r; + } + + public static Result error(String msg) { + return error(CommonConstant.SC_INTERNAL_SERVER_ERROR_500, msg); + } + + public static Result error(int code, String msg) { + Result r = new Result(); + r.setCode(code); + r.setMessage(msg); + r.setSuccess(false); + return r; + } + + public Result error500(String message) { + this.message = message; + this.code = CommonConstant.SC_INTERNAL_SERVER_ERROR_500; + this.success = false; + return this; + } + + /** + * 无权限访问返回结果 + */ + public static Result noauth(String msg) { + return error(CommonConstant.SC_JEECG_NO_AUTHZ, msg); + } + + @JsonIgnore + private String onlTable; + +} \ No newline at end of file diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/AutoLogAspect.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/AutoLogAspect.java new file mode 100644 index 0000000..b3427ff --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/AutoLogAspect.java @@ -0,0 +1,280 @@ +package org.jeecg.common.aspect; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.serializer.PropertyFilter; +import org.apache.shiro.SecurityUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.jeecg.common.api.dto.LogDTO; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.aspect.annotation.AutoLog; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.constant.enums.ModuleType; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.util.IPUtils; +import org.jeecg.common.util.SpringContextUtils; +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.modules.base.service.BaseCommonService; +import org.springframework.core.LocalVariableTableParameterNameDiscoverer; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; +import java.util.Date; + + +/** + * 系统日志,切面处理类 + * + * @Author scott + * @email jeecgos@163.com + * @Date 2018年1月14日 + */ +@Aspect +@Component +public class AutoLogAspect { + + @Resource + private BaseCommonService baseCommonService; + + @Pointcut("@annotation(org.jeecg.common.aspect.annotation.AutoLog)") + public void logPointCut() { + + } + + @Around("logPointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable { + long beginTime = System.currentTimeMillis(); + //执行方法 + Object result = point.proceed(); + //执行时长(毫秒) + long time = System.currentTimeMillis() - beginTime; + + //保存日志 + saveSysLog(point, time, result); + + return result; + } + + private void saveSysLog(ProceedingJoinPoint joinPoint, long time, Object obj) { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + + LogDTO dto = new LogDTO(); + AutoLog syslog = method.getAnnotation(AutoLog.class); + if(syslog != null){ + //update-begin-author:taoyan date: + String content = syslog.value(); + if(syslog.module()== ModuleType.ONLINE){ + content = getOnlineLogContent(obj, content); + } + //注解上的描述,操作日志内容 + dto.setLogType(syslog.logType()); + dto.setLogContent(content); + if(syslog.operateType() == CommonConstant.OPERATE_TYPE_7){ + dto.setOperateType(CommonConstant.OPERATE_TYPE_7); + } + } + + //请求的方法名 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = signature.getName(); + dto.setMethod(className + "." + methodName + "()"); + + + //设置操作类型 + if (CommonConstant.LOG_TYPE_2 == dto.getLogType()) { + if(dto.getOperateType() == null){ + dto.setOperateType(getOperateType(methodName, syslog.operateType())); + } + } + + //获取request + HttpServletRequest request = SpringContextUtils.getHttpServletRequest(); + //请求的参数 + dto.setRequestParam(getReqestParams(request,joinPoint)); + //设置IP地址 + dto.setIp(IPUtils.getIpAddr(request)); + //获取登录用户信息 + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + if(sysUser!=null){ + dto.setUserid(sysUser.getUsername()); + dto.setUsername(sysUser.getRealname()); + + } + //耗时 + dto.setCostTime(time); + dto.setCreateTime(new Date()); + //保存系统日志 + baseCommonService.addLog(dto); + } + + + /** + * 获取操作类型 + */ + private int getOperateType(String methodName,int operateType) { + if (operateType > 0) { + return operateType; + } + if (methodName.startsWith("list")) { + return CommonConstant.OPERATE_TYPE_1; + } + if (methodName.startsWith("add")) { + return CommonConstant.OPERATE_TYPE_2; + } + if (methodName.startsWith("edit")) { + return CommonConstant.OPERATE_TYPE_3; + } + if (methodName.startsWith("delete")) { + return CommonConstant.OPERATE_TYPE_4; + } + if (methodName.startsWith("import")) { + return CommonConstant.OPERATE_TYPE_5; + } + if (methodName.startsWith("export")) { + return CommonConstant.OPERATE_TYPE_6; + } + return CommonConstant.OPERATE_TYPE_1; + } + + /** + * @Description: 获取请求参数 + * @author: scott + * @date: 2020/4/16 0:10 + * @param request: request + * @param joinPoint: joinPoint + * @Return: java.lang.String + */ + private String getReqestParams(HttpServletRequest request, JoinPoint joinPoint) { + String httpMethod = request.getMethod(); + String params = ""; + if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) || "PATCH".equals(httpMethod)) { + Object[] paramsArray = joinPoint.getArgs(); + // java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false) + // https://my.oschina.net/mengzhang6/blog/2395893 + Object[] arguments = new Object[paramsArray.length]; + for (int i = 0; i < paramsArray.length; i++) { + if (paramsArray[i] instanceof BindingResult || paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile) { + //ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false) + //ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response + continue; + } + arguments[i] = paramsArray[i]; + } + //update-begin-author:taoyan date:20200724 for:日志数据太长的直接过滤掉 + PropertyFilter profilter = new PropertyFilter() { + @Override + public boolean apply(Object o, String name, Object value) { + if(value==null){ + return false; + } + if(value!=null && value.toString().length()>500){ + return false; + } + + return true; + } + }; + params = JSONObject.toJSONString(arguments, profilter); + //update-end-author:taoyan date:20200724 for:日志数据太长的直接过滤掉 + } else { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + // 请求的方法参数值 + Object[] args = joinPoint.getArgs(); + // 请求的方法参数名称 + LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer(); + String[] paramNames = u.getParameterNames(method); + if (args != null && paramNames != null) { + for (int i = 0; i < args.length; i++) { + params += " " + paramNames[i] + ": " + args[i]; + } + } + } + return params; + } + + /** + * online日志内容拼接 + * @param obj + * @param content + * @return + */ + private String getOnlineLogContent(Object obj, String content){ + if (Result.class.isInstance(obj)){ + Result res = (Result)obj; + String msg = res.getMessage(); + String tableName = res.getOnlTable(); + if(oConvertUtils.isNotEmpty(tableName)){ + content+=",表名:"+tableName; + } + if(res.isSuccess()){ + content+= ","+(oConvertUtils.isEmpty(msg)?"操作成功":msg); + }else{ + content+= ","+(oConvertUtils.isEmpty(msg)?"操作失败":msg); + } + } + return content; + } + + + /* private void saveSysLog(ProceedingJoinPoint joinPoint, long time, Object obj) { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + + SysLog sysLog = new SysLog(); + AutoLog syslog = method.getAnnotation(AutoLog.class); + if(syslog != null){ + //update-begin-author:taoyan date: + String content = syslog.value(); + if(syslog.module()== ModuleType.ONLINE){ + content = getOnlineLogContent(obj, content); + } + //注解上的描述,操作日志内容 + sysLog.setLogContent(content); + sysLog.setLogType(syslog.logType()); + } + + //请求的方法名 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = signature.getName(); + sysLog.setMethod(className + "." + methodName + "()"); + + + //设置操作类型 + if (sysLog.getLogType() == CommonConstant.LOG_TYPE_2) { + sysLog.setOperateType(getOperateType(methodName, syslog.operateType())); + } + + //获取request + HttpServletRequest request = SpringContextUtils.getHttpServletRequest(); + //请求的参数 + sysLog.setRequestParam(getReqestParams(request,joinPoint)); + + //设置IP地址 + sysLog.setIp(IPUtils.getIpAddr(request)); + + //获取登录用户信息 + LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal(); + if(sysUser!=null){ + sysLog.setUserid(sysUser.getUsername()); + sysLog.setUsername(sysUser.getRealname()); + + } + //耗时 + sysLog.setCostTime(time); + sysLog.setCreateTime(new Date()); + //保存系统日志 + sysLogService.save(sysLog); + }*/ +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/DictAspect.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/DictAspect.java new file mode 100644 index 0000000..92394f5 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/DictAspect.java @@ -0,0 +1,395 @@ +package org.jeecg.common.aspect; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.parser.Feature; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.jeecg.common.api.CommonAPI; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.aspect.annotation.Dict; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.system.vo.DictModel; +import org.jeecg.common.util.oConvertUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.lang.reflect.Field; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * @Description: 字典aop类 + * @Author: dangzhenghui + * @Date: 2019-3-17 21:50 + * @Version: 1.0 + */ +@Aspect +@Component +@Slf4j +public class DictAspect { + @Lazy + @Autowired + private CommonAPI commonAPI; + @Autowired + public RedisTemplate redisTemplate; + + // 定义切点Pointcut + @Pointcut("execution(public * org.jeecg.modules..*.*Controller.*(..)) || @annotation(org.jeecg.common.aspect.annotation.AutoDict)") + public void excudeService() { + } + + @Around("excudeService()") + public Object doAround(ProceedingJoinPoint pjp) throws Throwable { + long time1=System.currentTimeMillis(); + Object result = pjp.proceed(); + long time2=System.currentTimeMillis(); + log.debug("获取JSON数据 耗时:"+(time2-time1)+"ms"); + long start=System.currentTimeMillis(); + this.parseDictText(result); + long end=System.currentTimeMillis(); + log.debug("注入字典到JSON数据 耗时"+(end-start)+"ms"); + return result; + } + + /** + * 本方法针对返回对象为Result 的IPage的分页列表数据进行动态字典注入 + * 字典注入实现 通过对实体类添加注解@dict 来标识需要的字典内容,字典分为单字典code即可 ,table字典 code table text配合使用与原来jeecg的用法相同 + * 示例为SysUser 字段为sex 添加了注解@Dict(dicCode = "sex") 会在字典服务立马查出来对应的text 然后在请求list的时候将这个字典text,已字段名称加_dictText形式返回到前端 + * 例输入当前返回值的就会多出一个sex_dictText字段 + * { + * sex:1, + * sex_dictText:"男" + * } + * 前端直接取值sext_dictText在table里面无需再进行前端的字典转换了 + * customRender:function (text) { + * if(text==1){ + * return "男"; + * }else if(text==2){ + * return "女"; + * }else{ + * return text; + * } + * } + * 目前vue是这么进行字典渲染到table上的多了就很麻烦了 这个直接在服务端渲染完成前端可以直接用 + * @param result + */ + private void parseDictText(Object result) { + if (result instanceof Result) { + if (((Result) result).getResult() instanceof IPage) { + List items = new ArrayList<>(); + + //step.1 筛选出加了 Dict 注解的字段列表 + List dictFieldList = new ArrayList<>(); + // 字典数据列表, key = 字典code,value=数据列表 + Map> dataListMap = new HashMap<>(); + + for (Object record : ((IPage) ((Result) result).getResult()).getRecords()) { + ObjectMapper mapper = new ObjectMapper(); + String json="{}"; + try { + //解决@JsonFormat注解解析不了的问题详见SysAnnouncement类的@JsonFormat + json = mapper.writeValueAsString(record); + } catch (JsonProcessingException e) { + log.error("json解析失败"+e.getMessage(),e); + } + //update-begin--Author:scott -- Date:20211223 ----for:【issues/3303】restcontroller返回json数据后key顺序错乱 ----- + JSONObject item = JSONObject.parseObject(json, Feature.OrderedField); + //update-end--Author:scott -- Date:20211223 ----for:【issues/3303】restcontroller返回json数据后key顺序错乱 ----- + + //update-begin--Author:scott -- Date:20190603 ----for:解决继承实体字段无法翻译问题------ + //for (Field field : record.getClass().getDeclaredFields()) { + // 遍历所有字段,把字典Code取出来,放到 map 里 + for (Field field : oConvertUtils.getAllFields(record)) { + String value = item.getString(field.getName()); + if (oConvertUtils.isEmpty(value)) { + continue; + } + //update-end--Author:scott -- Date:20190603 ----for:解决继承实体字段无法翻译问题------ + if (field.getAnnotation(Dict.class) != null) { + if (!dictFieldList.contains(field)) { + dictFieldList.add(field); + } + String code = field.getAnnotation(Dict.class).dicCode(); + String text = field.getAnnotation(Dict.class).dicText(); + String table = field.getAnnotation(Dict.class).dictTable(); + + List dataList; + String dictCode = code; + if (!StringUtils.isEmpty(table)) { + dictCode = String.format("%s,%s,%s", table, text, code); + } + dataList = dataListMap.computeIfAbsent(dictCode, k -> new ArrayList<>()); + this.listAddAllDeduplicate(dataList, Arrays.asList(value.split(","))); + } + //date类型默认转换string格式化日期 + if ("java.util.Date".equals(field.getType().getName())&&field.getAnnotation(JsonFormat.class)==null&&item.get(field.getName())!=null){ + SimpleDateFormat aDate=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + item.put(field.getName(), aDate.format(new Date((Long) item.get(field.getName())))); + } + } + items.add(item); + } + + //step.2 调用翻译方法,一次性翻译 + Map> translText = this.translateAllDict(dataListMap); + + //step.3 将翻译结果填充到返回结果里 + for (JSONObject record : items) { + for (Field field : dictFieldList) { + String code = field.getAnnotation(Dict.class).dicCode(); + String text = field.getAnnotation(Dict.class).dicText(); + String table = field.getAnnotation(Dict.class).dictTable(); + + String fieldDictCode = code; + if (!StringUtils.isEmpty(table)) { + fieldDictCode = String.format("%s,%s,%s", table, text, code); + } + + String value = record.getString(field.getName()); + if (oConvertUtils.isNotEmpty(value)) { + List dictModels = translText.get(fieldDictCode); + if(dictModels==null || dictModels.size()==0){ + continue; + } + + String textValue = this.translDictText(dictModels, value); + log.debug(" 字典Val : " + textValue); + log.debug(" __翻译字典字段__ " + field.getName() + CommonConstant.DICT_TEXT_SUFFIX + ": " + textValue); + + // TODO-sun 测试输出,待删 + log.debug(" ---- dictCode: " + fieldDictCode); + log.debug(" ---- value: " + value); + log.debug(" ----- text: " + textValue); + log.debug(" ---- dictModels: " + JSON.toJSONString(dictModels)); + + record.put(field.getName() + CommonConstant.DICT_TEXT_SUFFIX, textValue); + } + } + } + + ((IPage) ((Result) result).getResult()).setRecords(items); + } + + } + } + + /** + * list 去重添加 + */ + private void listAddAllDeduplicate(List dataList, List addList) { + // 筛选出dataList中没有的数据 + List filterList = addList.stream().filter(i -> !dataList.contains(i)).collect(Collectors.toList()); + dataList.addAll(filterList); + } + + /** + * 一次性把所有的字典都翻译了 + * 1. 所有的普通数据字典的所有数据只执行一次SQL + * 2. 表字典相同的所有数据只执行一次SQL + * @param dataListMap + * @return + */ + private Map> translateAllDict(Map> dataListMap) { + // 翻译后的字典文本,key=dictCode + Map> translText = new HashMap<>(); + // 需要翻译的数据(有些可以从redis缓存中获取,就不走数据库查询) + List needTranslData = new ArrayList<>(); + //step.1 先通过redis中获取缓存字典数据 + for (String dictCode : dataListMap.keySet()) { + List dataList = dataListMap.get(dictCode); + if (dataList.size() == 0) { + continue; + } + // 表字典需要翻译的数据 + List needTranslDataTable = new ArrayList<>(); + for (String s : dataList) { + String data = s.trim(); + if (data.length() == 0) { + continue; //跳过循环 + } + if (dictCode.contains(",")) { + String keyString = String.format("sys:cache:dictTable::SimpleKey [%s,%s]", dictCode, data); + if (redisTemplate.hasKey(keyString)) { + try { + String text = oConvertUtils.getString(redisTemplate.opsForValue().get(keyString)); + List list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>()); + list.add(new DictModel(data, text)); + } catch (Exception e) { + log.warn(e.getMessage()); + } + } else if (!needTranslDataTable.contains(data)) { + // 去重添加 + needTranslDataTable.add(data); + } + } else { + String keyString = String.format("sys:cache:dict::%s:%s", dictCode, data); + if (redisTemplate.hasKey(keyString)) { + try { + String text = oConvertUtils.getString(redisTemplate.opsForValue().get(keyString)); + List list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>()); + list.add(new DictModel(data, text)); + } catch (Exception e) { + log.warn(e.getMessage()); + } + } else if (!needTranslData.contains(data)) { + // 去重添加 + needTranslData.add(data); + } + } + + } + //step.2 调用数据库翻译表字典 + if (needTranslDataTable.size() > 0) { + String[] arr = dictCode.split(","); + String table = arr[0], text = arr[1], code = arr[2]; + String values = String.join(",", needTranslDataTable); + log.info("translateDictFromTableByKeys.dictCode:" + dictCode); + log.info("translateDictFromTableByKeys.values:" + values); + List texts = commonAPI.translateDictFromTableByKeys(table, text, code, values); + log.info("translateDictFromTableByKeys.result:" + texts); + List list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>()); + list.addAll(texts); + + // 做 redis 缓存 + for (DictModel dict : texts) { + String redisKey = String.format("sys:cache:dictTable::SimpleKey [%s,%s]", dictCode, dict.getValue()); + try { + // update-begin-author:taoyan date:20211012 for: 字典表翻译注解缓存未更新 issues/3061 + // 保留5分钟 + redisTemplate.opsForValue().set(redisKey, dict.getText(), 300, TimeUnit.SECONDS); + // update-end-author:taoyan date:20211012 for: 字典表翻译注解缓存未更新 issues/3061 + } catch (Exception e) { + log.warn(e.getMessage(), e); + } + } + } + } + + //step.3 调用数据库进行翻译普通字典 + if (needTranslData.size() > 0) { + List dictCodeList = Arrays.asList(dataListMap.keySet().toArray(new String[]{})); + // 将不包含逗号的字典code筛选出来,因为带逗号的是表字典,而不是普通的数据字典 + List filterDictCodes = dictCodeList.stream().filter(key -> !key.contains(",")).collect(Collectors.toList()); + String dictCodes = String.join(",", filterDictCodes); + String values = String.join(",", needTranslData); + log.info("translateManyDict.dictCodes:" + dictCodes); + log.info("translateManyDict.values:" + values); + Map> manyDict = commonAPI.translateManyDict(dictCodes, values); + log.info("translateManyDict.result:" + manyDict); + for (String dictCode : manyDict.keySet()) { + List list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>()); + List newList = manyDict.get(dictCode); + list.addAll(newList); + + // 做 redis 缓存 + for (DictModel dict : newList) { + String redisKey = String.format("sys:cache:dict::%s:%s", dictCode, dict.getValue()); + try { + redisTemplate.opsForValue().set(redisKey, dict.getText()); + } catch (Exception e) { + log.warn(e.getMessage(), e); + } + } + } + } + return translText; + } + + /** + * 字典值替换文本 + * + * @param dictModels + * @param values + * @return + */ + private String translDictText(List dictModels, String values) { + List result = new ArrayList<>(); + + // 允许多个逗号分隔,允许传数组对象 + String[] splitVal = values.split(","); + for (String val : splitVal) { + String dictText = val; + for (DictModel dict : dictModels) { + if (val.equals(dict.getValue())) { + dictText = dict.getText(); + break; + } + } + result.add(dictText); + } + return String.join(",", result); + } + +// /** +// * 翻译字典文本 +// * @param code +// * @param text +// * @param table +// * @param key +// * @return +// */ +// @Deprecated +// private String translateDictValue(String code, String text, String table, String key) { +// if(oConvertUtils.isEmpty(key)) { +// return null; +// } +// StringBuffer textValue=new StringBuffer(); +// String[] keys = key.split(","); +// for (String k : keys) { +// String tmpValue = null; +// log.debug(" 字典 key : "+ k); +// if (k.trim().length() == 0) { +// continue; //跳过循环 +// } +// //update-begin--Author:scott -- Date:20210531 ----for: !56 优化微服务应用下存在表字段需要字典翻译时加载缓慢问题----- +// if (!StringUtils.isEmpty(table)){ +// log.info("--DictAspect------dicTable="+ table+" ,dicText= "+text+" ,dicCode="+code); +// String keyString = String.format("sys:cache:dictTable::SimpleKey [%s,%s,%s,%s]",table,text,code,k.trim()); +// if (redisTemplate.hasKey(keyString)){ +// try { +// tmpValue = oConvertUtils.getString(redisTemplate.opsForValue().get(keyString)); +// } catch (Exception e) { +// log.warn(e.getMessage()); +// } +// }else { +// tmpValue= commonAPI.translateDictFromTable(table,text,code,k.trim()); +// } +// }else { +// String keyString = String.format("sys:cache:dict::%s:%s",code,k.trim()); +// if (redisTemplate.hasKey(keyString)){ +// try { +// tmpValue = oConvertUtils.getString(redisTemplate.opsForValue().get(keyString)); +// } catch (Exception e) { +// log.warn(e.getMessage()); +// } +// }else { +// tmpValue = commonAPI.translateDict(code, k.trim()); +// } +// } +// //update-end--Author:scott -- Date:20210531 ----for: !56 优化微服务应用下存在表字段需要字典翻译时加载缓慢问题----- +// +// if (tmpValue != null) { +// if (!"".equals(textValue.toString())) { +// textValue.append(","); +// } +// textValue.append(tmpValue); +// } +// +// } +// return textValue.toString(); +// } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/PermissionDataAspect.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/PermissionDataAspect.java new file mode 100644 index 0000000..9a81f91 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/PermissionDataAspect.java @@ -0,0 +1,133 @@ +package org.jeecg.common.aspect; + +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.jeecg.common.api.CommonAPI; +import org.jeecg.common.aspect.annotation.PermissionData; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.system.util.JeecgDataAutorUtils; +import org.jeecg.common.system.util.JwtUtil; +import org.jeecg.common.system.vo.SysPermissionDataRuleModel; +import org.jeecg.common.system.vo.SysUserCacheInfo; +import org.jeecg.common.util.SpringContextUtils; +import org.jeecg.common.util.oConvertUtils; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; +import java.util.List; + +/** + * 数据权限切面处理类 + * 当被请求的方法有注解PermissionData时,会在往当前request中写入数据权限信息 + * @Date 2019年4月10日 + * @Version: 1.0 + */ +@Aspect +@Component +@Slf4j +public class PermissionDataAspect { + @Lazy + @Resource + private CommonAPI commonAPI; + + @Pointcut("@annotation(org.jeecg.common.aspect.annotation.PermissionData)") + public void pointCut() { + + } + + @Around("pointCut()") + public Object arround(ProceedingJoinPoint point) throws Throwable{ + HttpServletRequest request = SpringContextUtils.getHttpServletRequest(); + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + PermissionData pd = method.getAnnotation(PermissionData.class); + String component = pd.pageComponent(); + String requestMethod = request.getMethod(); + String requestPath = request.getRequestURI().substring(request.getContextPath().length()); + requestPath = filterUrl(requestPath); + //update-begin-author:taoyan date:20211027 for:JTC-132【online报表权限】online报表带参数的菜单配置数据权限无效 + //先判断是否online报表请求 + // TODO 参数顺序调整有隐患 + if(requestPath.indexOf(UrlMatchEnum.CGREPORT_DATA.getMatch_url())>=0){ + // 获取地址栏参数 + String urlParamString = request.getParameter(CommonConstant.ONL_REP_URL_PARAM_STR); + if(oConvertUtils.isNotEmpty(urlParamString)){ + requestPath+="?"+urlParamString; + } + } + //update-end-author:taoyan date:20211027 for:JTC-132【online报表权限】online报表带参数的菜单配置数据权限无效 + log.info("拦截请求 >> {} ; 请求类型 >> {} . ", requestPath, requestMethod); + String userId = JwtUtil.getUserIdByToken(request); + //查询数据权限信息 + //TODO 微服务情况下也得支持缓存机制 + List dataRules = commonAPI.queryPermissionDataRule(component, requestPath, userId); + if(dataRules!=null && dataRules.size()>0) { + //临时存储 + JeecgDataAutorUtils.installDataSearchConditon(request, dataRules); + //TODO 微服务情况下也得支持缓存机制 + SysUserCacheInfo userinfo = commonAPI.getCacheUser(userId); + JeecgDataAutorUtils.installUserInfo(request, userinfo); + } + return point.proceed(); + } + + private String filterUrl(String requestPath){ + String url = ""; + if(oConvertUtils.isNotEmpty(requestPath)){ + url = requestPath.replace("\\", "/"); + url = url.replace("//", "/"); + if(url.indexOf("//")>=0){ + url = filterUrl(url); + } + /*if(url.startsWith("/")){ + url=url.substring(1); + }*/ + } + return url; + } + + /** + * 获取请求地址 + * @param request + * @return + */ + @Deprecated + private String getJgAuthRequsetPath(HttpServletRequest request) { + String queryString = request.getQueryString(); + String requestPath = request.getRequestURI(); + if(oConvertUtils.isNotEmpty(queryString)){ + requestPath += "?" + queryString; + } + if (requestPath.indexOf("&") > -1) {// 去掉其他参数(保留一个参数) 例如:loginController.do?login + requestPath = requestPath.substring(0, requestPath.indexOf("&")); + } + if(requestPath.indexOf("=")!=-1){ + if(requestPath.indexOf(".do")!=-1){ + requestPath = requestPath.substring(0,requestPath.indexOf(".do")+3); + }else{ + requestPath = requestPath.substring(0,requestPath.indexOf("?")); + } + } + requestPath = requestPath.substring(request.getContextPath().length() + 1);// 去掉项目路径 + return filterUrl(requestPath); + } + + @Deprecated + private boolean moHuContain(List list,String key){ + for(String str : list){ + if(key.contains(str)){ + return true; + } + } + return false; + } + + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/UrlMatchEnum.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/UrlMatchEnum.java new file mode 100644 index 0000000..4407fce --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/UrlMatchEnum.java @@ -0,0 +1,60 @@ +package org.jeecg.common.aspect; + +/** + * @Author scott + * @Date 2020/1/14 13:36 + * @Description: 请求URL与菜单路由URL转换规则(方便于采用菜单路由URL来配置数据权限规则) + */ +public enum UrlMatchEnum { + CGFORM_DATA("/online/cgform/api/getData/", "/online/cgformList/"), + CGFORM_EXCEL_DATA("/online/cgform/api/exportXls/", "/online/cgformList/"), + CGFORM_TREE_DATA("/online/cgform/api/getTreeData/", "/online/cgformList/"), + CGREPORT_DATA("/online/cgreport/api/getColumnsAndData/", "/online/cgreport/"), + CGREPORT_EXCEL_DATA("/online/cgreport/api/exportXls/", "/online/cgreport/"), + CGREPORT_EXCEL_DATA2("/online/cgreport/api/exportManySheetXls/", "/online/cgreport/"); + + UrlMatchEnum(String url, String match_url) { + this.url = url; + this.match_url = match_url; + } + + /** + * Request 请求 URL前缀 + */ + private String url; + /** + * 菜单路由 URL前缀 (对应菜单路径) + */ + private String match_url; + + /** + * 根据req url 获取到菜单配置路径(前端页面路由URL) + * + * @param url + * @return + */ + public static String getMatchResultByUrl(String url) { + //获取到枚举 + UrlMatchEnum[] values = UrlMatchEnum.values(); + //加强for循环进行遍历操作 + for (UrlMatchEnum lr : values) { + //如果遍历获取的type和参数type一致 + if (url.indexOf(lr.url) != -1) { + //返回type对象的desc + return url.replace(lr.url, lr.match_url); + } + } + return null; + } + + public String getMatch_url() { + return match_url; + } + // public static void main(String[] args) { +// /** +// * 比如request真实请求URL: /online/cgform/api/getData/81fcf7d8922d45069b0d5ba983612d3a +// * 转换匹配路由URL后(对应配置的菜单路径):/online/cgformList/81fcf7d8922d45069b0d5ba983612d3a +// */ +// System.out.println(UrlMatchEnum.getMatchResultByUrl("/online/cgform/api/getData/81fcf7d8922d45069b0d5ba983612d3a")); +// } +} \ No newline at end of file diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/AutoDict.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/AutoDict.java new file mode 100644 index 0000000..a6537cb --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/AutoDict.java @@ -0,0 +1,23 @@ +package org.jeecg.common.aspect.annotation; + +import java.lang.annotation.*; + +/** + * 通过此注解声明的接口,自动实现字典翻译 + * + * @Author scott + * @email jeecgos@163.com + * @Date 2022年01月05日 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface AutoDict { + + /** + * 暂时无用 + * @return + */ + String value() default ""; + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/AutoLog.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/AutoLog.java new file mode 100644 index 0000000..5206f60 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/AutoLog.java @@ -0,0 +1,46 @@ +package org.jeecg.common.aspect.annotation; + +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.constant.enums.ModuleType; + +import java.lang.annotation.*; + +/** + * 系统日志注解 + * + * @Author scott + * @email jeecgos@163.com + * @Date 2019年1月14日 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface AutoLog { + + /** + * 日志内容 + * + * @return + */ + String value() default ""; + + /** + * 日志类型 + * + * @return 0:操作日志;1:登录日志;2:定时任务; + */ + int logType() default CommonConstant.LOG_TYPE_2; + + /** + * 操作日志类型 + * + * @return (1查询,2添加,3修改,4删除) + */ + int operateType() default 0; + + /** + * 模块类型 默认为common + * @return + */ + ModuleType module() default ModuleType.COMMON; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/AutoLowApp.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/AutoLowApp.java new file mode 100644 index 0000000..2b3ded9 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/AutoLowApp.java @@ -0,0 +1,33 @@ +package org.jeecg.common.aspect.annotation; + +import java.lang.annotation.*; + +import org.jeecg.common.constant.enums.LowAppAopEnum; + +/** + * 自动注入low_app_id + * + * @Author scott + * @email jeecgos@163.com + * @Date 2022年01月05日 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface AutoLowApp { + + /** + * 切面类型(add、delete、db_import等其他操作) + * + * @return + */ + LowAppAopEnum action(); + + /** + * 业务类型(cgform等) + * + * @return + */ + String bizType(); + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/Dict.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/Dict.java new file mode 100644 index 0000000..871ff44 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/Dict.java @@ -0,0 +1,42 @@ +package org.jeecg.common.aspect.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 字典注解 + * @author: dangzhenghui + * @date: 2019年03月17日-下午9:37:16 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Dict { + /** + * 方法描述: 数据code + * 作 者: dangzhenghui + * 日 期: 2019年03月17日-下午9:37:16 + * + * @return 返回类型: String + */ + String dicCode(); + + /** + * 方法描述: 数据Text + * 作 者: dangzhenghui + * 日 期: 2019年03月17日-下午9:37:16 + * + * @return 返回类型: String + */ + String dicText() default ""; + + /** + * 方法描述: 数据字典表 + * 作 者: dangzhenghui + * 日 期: 2019年03月17日-下午9:37:16 + * + * @return 返回类型: String + */ + String dictTable() default ""; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/DynamicTable.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/DynamicTable.java new file mode 100644 index 0000000..e377c8f --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/DynamicTable.java @@ -0,0 +1,20 @@ +package org.jeecg.common.aspect.annotation; + +import java.lang.annotation.*; + +/** + * 动态table切换 + * + * @author :zyf + * @date:2020-04-25 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DynamicTable { + /** + * 需要动态解析的表名 + * @return + */ + String value(); +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/OnlineAuth.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/OnlineAuth.java new file mode 100644 index 0000000..0100d07 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/OnlineAuth.java @@ -0,0 +1,19 @@ +package org.jeecg.common.aspect.annotation; + +import java.lang.annotation.*; + +/** + * online请求拦截专用注解 + * @author: jeecg-boot + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE,ElementType.METHOD}) +@Documented +public @interface OnlineAuth { + + /** + * 请求关键字,在xxx/code之前的字符串 + * @return + */ + String value(); +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/PermissionData.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/PermissionData.java new file mode 100644 index 0000000..1d8f399 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/PermissionData.java @@ -0,0 +1,29 @@ +package org.jeecg.common.aspect.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 数据权限注解 + * @Author taoyan + * @Date 2019年4月11日 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE,ElementType.METHOD}) +@Documented +public @interface PermissionData { + /** + * 暂时没用 + * @return + */ + String value() default ""; + + + /** + * 配置菜单的组件路径,用于数据权限 + */ + String pageComponent() default ""; +} \ No newline at end of file diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonConstant.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonConstant.java new file mode 100644 index 0000000..9d1c9a4 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonConstant.java @@ -0,0 +1,448 @@ +package org.jeecg.common.constant; + +public interface CommonConstant { + + public static final String SYSTEM_NAME="system"; + + /** + * 内置食堂id + */ + public static final String INLAY_CANTEEN_ID="0"; + + /** + * 正常状态 + */ + public static final Integer STATUS_NORMAL = 0; + + /** + * 禁用状态 + */ + public static final Integer STATUS_DISABLE = -1; + + /** + * 删除标志 + */ + public static final Integer DEL_FLAG_1 = 1; + + /** + * 未删除 + */ + public static final Integer DEL_FLAG_0 = 0; + + /** + * 系统日志类型: 登录 + */ + public static final int LOG_TYPE_1 = 1; + + /** + * 系统日志类型: 操作 + */ + public static final int LOG_TYPE_2 = 2; + + /** + * 操作日志类型: 查询 + */ + public static final int OPERATE_TYPE_1 = 1; + + /** + * 操作日志类型: 添加 + */ + public static final int OPERATE_TYPE_2 = 2; + + /** + * 操作日志类型: 更新 + */ + public static final int OPERATE_TYPE_3 = 3; + + /** + * 操作日志类型: 删除 + */ + public static final int OPERATE_TYPE_4 = 4; + + /** + * 操作日志类型: 倒入 + */ + public static final int OPERATE_TYPE_5 = 5; + + /** + * 操作日志类型: 导出 + */ + public static final int OPERATE_TYPE_6 = 6; + + /** + * 操作日志类型: 四合一 + */ + public static final int OPERATE_TYPE_7 = 7; + + + /** {@code 500 Server Error} (HTTP/1.0 - RFC 1945) */ + public static final Integer SC_INTERNAL_SERVER_ERROR_500 = 500; + /** {@code 200 OK} (HTTP/1.0 - RFC 1945) */ + public static final Integer SC_OK_200 = 200; + + /**访问权限认证未通过 510*/ + public static final Integer SC_JEECG_NO_AUTHZ=510; + + /** 登录用户Shiro权限缓存KEY前缀 */ + public static String PREFIX_USER_SHIRO_CACHE = "shiro:cache:org.jeecg.config.shiro.ShiroRealm.authorizationCache:"; + + /** 登录用户Token令牌缓存KEY前缀 */ + public static final String USER_TOKEN = "user_token"; + + /** 小程序登录用户Token令牌缓存KEY前缀 */ + public static final String XCX_USER_TOKEN = "xcx_user_token"; + /** + * 体重管理小程序token key:user_id + */ + public static final String XCX_USER_WEIGHT_TOKEN = "xcx_user_weight_token"; + /** + * 体重管理小程序用户加密后身份证号 + */ + public static final String XCX_USER_CARD = "xcx_user_card"; + + /** 登录用户Token令牌缓存KEY前缀 */ + public static final String PREFIX_USER_TOKEN = "prefix_user_token_"; + /** Token缓存时间:3600秒即一小时 */ + public static final int TOKEN_EXPIRE_TIME = 3600; + + /** 登录二维码 */ + public static final String LOGIN_QRCODE_PRE = "QRCODELOGIN:"; + public static final String LOGIN_QRCODE = "LQ:"; + /** 登录二维码token */ + public static final String LOGIN_QRCODE_TOKEN = "LQT:"; + + + /** + * 0:一级菜单 + */ + public static final Integer MENU_TYPE_0 = 0; + /** + * 1:子菜单 + */ + public static final Integer MENU_TYPE_1 = 1; + /** + * 2:按钮权限 + */ + public static final Integer MENU_TYPE_2 = 2; + + /**通告对象类型(USER:指定用户,ALL:全体用户)*/ + public static final String MSG_TYPE_UESR = "USER"; + public static final String MSG_TYPE_ALL = "ALL"; + + /**发布状态(0未发布,1已发布,2已撤销)*/ + public static final String NO_SEND = "0"; + public static final String HAS_SEND = "1"; + public static final String HAS_CANCLE = "2"; + + /**阅读状态(0未读,1已读)*/ + public static final String HAS_READ_FLAG = "1"; + public static final String NO_READ_FLAG = "0"; + + /**优先级(L低,M中,H高)*/ + public static final String PRIORITY_L = "L"; + public static final String PRIORITY_M = "M"; + public static final String PRIORITY_H = "H"; + + /** + * 短信模板方式 0 .登录模板、1.注册模板、2.忘记密码模板 + */ + public static final String SMS_TPL_TYPE_0 = "0"; + public static final String SMS_TPL_TYPE_1 = "1"; + public static final String SMS_TPL_TYPE_2 = "2"; + + /** + * 状态(0无效1有效) + */ + public static final String STATUS_0 = "0"; + public static final String STATUS_1 = "1"; + + /** + * 同步工作流引擎1同步0不同步 + */ + public static final Integer ACT_SYNC_1 = 1; + public static final Integer ACT_SYNC_0 = 0; + + /** + * 消息类型1:通知公告2:系统消息 + */ + public static final String MSG_CATEGORY_1 = "1"; + public static final String MSG_CATEGORY_2 = "2"; + + /** + * 是否配置菜单的数据权限 1是0否 + */ + public static final Integer RULE_FLAG_0 = 0; + public static final Integer RULE_FLAG_1 = 1; + + /** + * 是否用户已被冻结 1正常(解冻) 2冻结 + */ + public static final Integer USER_UNFREEZE = 1; + public static final Integer USER_FREEZE = 2; + + /**字典翻译文本后缀*/ + public static final String DICT_TEXT_SUFFIX = "_dictText"; + + /** + * 表单设计器主表类型 + */ + public static final Integer DESIGN_FORM_TYPE_MAIN = 1; + + /** + * 表单设计器子表表类型 + */ + public static final Integer DESIGN_FORM_TYPE_SUB = 2; + + /** + * 表单设计器URL授权通过 + */ + public static final Integer DESIGN_FORM_URL_STATUS_PASSED = 1; + + /** + * 表单设计器URL授权未通过 + */ + public static final Integer DESIGN_FORM_URL_STATUS_NOT_PASSED = 2; + + /** + * 表单设计器新增 Flag + */ + public static final String DESIGN_FORM_URL_TYPE_ADD = "add"; + /** + * 表单设计器修改 Flag + */ + public static final String DESIGN_FORM_URL_TYPE_EDIT = "edit"; + /** + * 表单设计器详情 Flag + */ + public static final String DESIGN_FORM_URL_TYPE_DETAIL = "detail"; + /** + * 表单设计器复用数据 Flag + */ + public static final String DESIGN_FORM_URL_TYPE_REUSE = "reuse"; + /** + * 表单设计器编辑 Flag (已弃用) + */ + public static final String DESIGN_FORM_URL_TYPE_VIEW = "view"; + + /** + * online参数值设置(是:Y, 否:N) + */ + public static final String ONLINE_PARAM_VAL_IS_TURE = "Y"; + public static final String ONLINE_PARAM_VAL_IS_FALSE = "N"; + + /** + * 文件上传类型(本地:local,Minio:minio,阿里云:alioss) + */ + public static final String UPLOAD_TYPE_LOCAL = "local"; + public static final String UPLOAD_TYPE_MINIO = "minio"; + public static final String UPLOAD_TYPE_OSS = "alioss"; + //自建文件服务器 + public static final String UPLOAD_MY_UPLOAD = "myupload"; + + /** + * 文档上传自定义桶名称 + */ + public static final String UPLOAD_CUSTOM_BUCKET = "eoafile"; + /** + * 文档上传自定义路径 + */ + public static final String UPLOAD_CUSTOM_PATH = "eoafile"; + /** + * 文件外链接有效天数 + */ + public static final Integer UPLOAD_EFFECTIVE_DAYS = 1; + + /** + * 用户类型 (0:超级管理员(系统内置-内部使用) 1:admin 2:管理用户(管理一个或多个二级单位的用户) 3:单位管理员(管理二级单位的用户) 4:食堂管理员(管理二级单位下食堂的用户) 5:食堂职工 6:员工(就餐人员)) + */ + public static final Integer USER_IDENTITY_0 = 0; + public static final Integer USER_IDENTITY_1 = 1; + public static final Integer USER_IDENTITY_2 = 2; + public static final Integer USER_IDENTITY_3 = 3; + public static final Integer USER_IDENTITY_4 = 4; + public static final Integer USER_IDENTITY_5 = 5; + public static final Integer USER_IDENTITY_6 = 6; + + + + + /** sys_user 表 username 唯一键索引 */ + public static final String SQL_INDEX_UNIQ_SYS_USER_USERNAME = "uniq_sys_user_username"; + /** sys_user 表 work_no 唯一键索引 */ + public static final String SQL_INDEX_UNIQ_SYS_USER_WORK_NO = "uniq_sys_user_work_no"; + /** sys_user 表 phone 唯一键索引 */ + public static final String SQL_INDEX_UNIQ_SYS_USER_PHONE = "uniq_sys_user_phone"; + /** 达梦数据库升提示。违反表[SYS_USER]唯一性约束 */ + public static final String SQL_INDEX_UNIQ_SYS_USER = "唯一性约束"; + + /** sys_user 表 email 唯一键索引 */ + public static final String SQL_INDEX_UNIQ_SYS_USER_EMAIL = "uniq_sys_user_email"; + /** sys_quartz_job 表 job_class_name 唯一键索引 */ + public static final String SQL_INDEX_UNIQ_JOB_CLASS_NAME = "uniq_job_class_name"; + /** sys_position 表 code 唯一键索引 */ + public static final String SQL_INDEX_UNIQ_CODE = "uniq_code"; + /** sys_role 表 code 唯一键索引 */ + public static final String SQL_INDEX_UNIQ_SYS_ROLE_CODE = "uniq_sys_role_role_code"; + /** sys_depart 表 code 唯一键索引 */ + public static final String SQL_INDEX_UNIQ_DEPART_ORG_CODE = "uniq_depart_org_code"; + /** sys_category 表 code 唯一键索引 */ + public static final String SQL_INDEX_UNIQ_CATEGORY_CODE = "idx_sc_code"; + /** + * 在线聊天 是否为默认分组 + */ + public static final String IM_DEFAULT_GROUP = "1"; + /** + * 在线聊天 图片文件保存路径 + */ + public static final String IM_UPLOAD_CUSTOM_PATH = "imfile"; + /** + * 在线聊天 用户状态 + */ + public static final String IM_STATUS_ONLINE = "online"; + + /** + * 在线聊天 SOCKET消息类型 + */ + public static final String IM_SOCKET_TYPE = "chatMessage"; + + /** + * 在线聊天 是否开启默认添加好友 1是 0否 + */ + public static final String IM_DEFAULT_ADD_FRIEND = "1"; + + /** + * 在线聊天 用户好友缓存前缀 + */ + public static final String IM_PREFIX_USER_FRIEND_CACHE = "sys:cache:im:im_prefix_user_friend_"; + + /** + * 考勤补卡业务状态 (1:同意 2:不同意) + */ + public static final String SIGN_PATCH_BIZ_STATUS_1 = "1"; + public static final String SIGN_PATCH_BIZ_STATUS_2 = "2"; + + /** + * 公文文档上传自定义路径 + */ + public static final String UPLOAD_CUSTOM_PATH_OFFICIAL = "officialdoc"; + /** + * 公文文档下载自定义路径 + */ + public static final String DOWNLOAD_CUSTOM_PATH_OFFICIAL = "officaldown"; + + /** + * WPS存储值类别(1 code文号 2 text(WPS模板还是公文发文模板)) + */ + public static final String WPS_TYPE_1="1"; + public static final String WPS_TYPE_2="2"; + + + public final static String X_ACCESS_TOKEN = "X-Access-Token"; + public final static String X_SIGN = "X-Sign"; + public final static String X_TIMESTAMP = "X-TIMESTAMP"; + public final static String TOKEN_IS_INVALID_MSG = "Token失效,请重新登录!"; + + /** + * 多租户 请求头 + */ + public final static String TENANT_ID = "tenant-id"; + + /** + * 微服务读取配置文件属性 服务地址 + */ + public final static String CLOUD_SERVER_KEY = "spring.cloud.nacos.discovery.server-addr"; + + /** + * 第三方登录 验证密码/创建用户 都需要设置一个操作码 防止被恶意调用 + */ + public final static String THIRD_LOGIN_CODE = "third_login_code"; + + /** + * 第三方APP同步方向:本地 --> 第三方APP + */ + String THIRD_SYNC_TO_APP = "SYNC_TO_APP"; + /** + * 第三方APP同步方向:第三方APP --> 本地 + */ + String THIRD_SYNC_TO_LOCAL = "SYNC_TO_LOCAL"; + + /** 系统通告消息状态:0=未发布 */ + String ANNOUNCEMENT_SEND_STATUS_0 = "0"; + /** 系统通告消息状态:1=已发布 */ + String ANNOUNCEMENT_SEND_STATUS_1 = "1"; + /** 系统通告消息状态:2=已撤销 */ + String ANNOUNCEMENT_SEND_STATUS_2 = "2"; + + /**ONLINE 报表权限用 从request中获取地址栏后的参数*/ + String ONL_REP_URL_PARAM_STR="onlRepUrlParamStr"; + + /**缩略图通用前缀*/ + String THUMBNAIL_PREFIX = "small_"; + + /**缩略图缩略比例*/ + Double THUMBNAIL_SCALE = 0.25; + Double THUMBNAIL_SCALE_5 = 0.50; + Double THUMBNAIL_SCALE_6 = 0.60; + /** + * 食堂缓存前缀 + */ + public static final String CANTEEN_ID_NO_CACHE = "sys:cache:canteen"; + + /** + * 组织机构编码和名称缓存 + */ + public static final String ORG_CODE_CACHE = "sys:cache:org_code"; + + /** + * 膳食宝塔图片 + */ + public static final String FOOD_PAGODA_IMG = "food_pagoda_img"; + + /** + * 活动赛事 + */ + public static final String EVENTS_RULE_NUM = "event_rule_remain_num"; + + String ACCESSTOKEN = "AccessToken"; + + String JSAPITICKET = "JsapiTicket"; + + /** + * 手动添加膳食记录缓存 key: userId+餐次(123)+地址(012)+年月日 + */ + String STAGING_USER_FOOD_INFO = "staging_user_food_info"; + /** + * 年报数据 一月一次 key orgCode+canteenId+departId + personTypeId+年月 + */ + String ANNUAL_REPORT = "annual_report"; + /** + * 四合一后台token + */ + String CONSOLE_TOKEN = "console_token"; + /** + * 智能营养监控各食堂统计表缓存 + */ + String COUNT_NUMBER_DINERS = "count_number_diners"; + /** + * 基础数据缓存(营养素推荐物品列表) + */ + String MATER_INFO_DATA = "mater_info_data"; + /** + * 营养排行榜缓存-员工 + */ + String NUTRITION_RANKINGS = "nutrition_rankings"; + /** + * 营养排行榜缓存-部门 + */ + String DEPART_RANKINGS = "depart_rankings"; + /** + * 排行榜请求参数缓存 + */ + String RANKING_REQUEST_PARAM = "ranking_request_param"; + + // 人脸数据键名 + String FACE_CACHE = "FACE_CACHE"; + + // 食堂支付人脸数据键名 + String FACE_CACHE_PAY = "FACE_CACHE_PAY"; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonSendStatus.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonSendStatus.java new file mode 100644 index 0000000..c69218c --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonSendStatus.java @@ -0,0 +1,37 @@ +package org.jeecg.common.constant; + +/** + * 系统通告 - 发布状态 + * @Author LeeShaoQing + * + */ +public interface CommonSendStatus { + + /** + * 未发布 + */ + public static final String UNPUBLISHED_STATUS_0 = "0"; + + /** + * 已发布 + */ + public static final String PUBLISHED_STATUS_1 = "1"; + + /** + * 撤销 + */ + public static final String REVOKE_STATUS_2 = "2"; + + /** + * app端推送会话标识后缀 + */ + public static final String APP_SESSION_SUFFIX = "_app"; + + + /**流程催办——系统通知消息模板*/ + public static final String TZMB_BPM_CUIBAN = "bpm_cuiban"; + /**标准模板—系统消息通知*/ + public static final String TZMB_SYS_TS_NOTE = "sys_ts_note"; + /**流程超时提醒——系统通知消息模板*/ + public static final String TZMB_BPM_CHAOSHI_TIP = "bpm_chaoshi_tip"; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/DataBaseConstant.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/DataBaseConstant.java new file mode 100644 index 0000000..e94a024 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/DataBaseConstant.java @@ -0,0 +1,166 @@ +package org.jeecg.common.constant; +/** + * 数据库上下文常量 + * @author: jeecg-boot + */ +public interface DataBaseConstant { + //*********数据库类型**************************************** + + /**MYSQL数据库*/ + public static final String DB_TYPE_MYSQL = "MYSQL"; + + /** ORACLE*/ + public static final String DB_TYPE_ORACLE = "ORACLE"; + + /**达梦数据库*/ + public static final String DB_TYPE_DM = "DM"; + + /**postgreSQL达梦数据库*/ + public static final String DB_TYPE_POSTGRESQL = "POSTGRESQL"; + + /**sqlserver数据库*/ + public static final String DB_TYPE_SQLSERVER = "SQLSERVER"; + + /**mariadb 数据库*/ + public static final String DB_TYPE_MARIADB = "MARIADB"; + + /**DB2 数据库*/ + public static final String DB_TYPE_DB2 = "DB2"; + + /**HSQL 数据库*/ + public static final String DB_TYPE_HSQL = "HSQL"; + +// // 数据库类型,对应 database_type 字典 +// public static final String DB_TYPE_MYSQL_NUM = "1"; +// public static final String DB_TYPE_MYSQL7_NUM = "6"; +// public static final String DB_TYPE_ORACLE_NUM = "2"; +// public static final String DB_TYPE_SQLSERVER_NUM = "3"; +// public static final String DB_TYPE_POSTGRESQL_NUM = "4"; +// public static final String DB_TYPE_MARIADB_NUM = "5"; + + //*********系统上下文变量**************************************** + /** + * 数据-所属机构编码 + */ + public static final String SYS_ORG_CODE = "sysOrgCode"; + /** + * 数据-所属机构编码 + */ + public static final String SYS_ORG_CODE_TABLE = "sys_org_code"; + /** + * 数据-所属机构编码 + */ + public static final String SYS_MULTI_ORG_CODE = "sysMultiOrgCode"; + /** + * 数据-所属机构编码 + */ + public static final String SYS_MULTI_ORG_CODE_TABLE = "sys_multi_org_code"; + /** + * 数据-系统用户编码(对应登录用户账号) + */ + public static final String SYS_USER_CODE = "sysUserCode"; + /** + * 数据-系统用户编码(对应登录用户账号) + */ + public static final String SYS_USER_CODE_TABLE = "sys_user_code"; + + /** + * 登录用户真实姓名 + */ + public static final String SYS_USER_NAME = "sysUserName"; + /** + * 登录用户真实姓名 + */ + public static final String SYS_USER_NAME_TABLE = "sys_user_name"; + /** + * 系统日期"yyyy-MM-dd" + */ + public static final String SYS_DATE = "sysDate"; + /** + * 系统日期"yyyy-MM-dd" + */ + public static final String SYS_DATE_TABLE = "sys_date"; + /** + * 系统时间"yyyy-MM-dd HH:mm" + */ + public static final String SYS_TIME = "sysTime"; + /** + * 系统时间"yyyy-MM-dd HH:mm" + */ + public static final String SYS_TIME_TABLE = "sys_time"; + /** + * 数据-所属机构编码 + */ + public static final String SYS_BASE_PATH = "sys_base_path"; + //*********系统上下文变量**************************************** + + + //*********系统建表标准字段**************************************** + /** + * 创建者登录名称 + */ + public static final String CREATE_BY_TABLE = "create_by"; + /** + * 创建者登录名称 + */ + public static final String CREATE_BY = "createBy"; + /** + * 创建日期时间 + */ + public static final String CREATE_TIME_TABLE = "create_time"; + /** + * 创建日期时间 + */ + public static final String CREATE_TIME = "createTime"; + /** + * 更新用户登录名称 + */ + public static final String UPDATE_BY_TABLE = "update_by"; + /** + * 更新用户登录名称 + */ + public static final String UPDATE_BY = "updateBy"; + /** + * 更新日期时间 + */ + public static final String UPDATE_TIME = "updateTime"; + /** + * 更新日期时间 + */ + public static final String UPDATE_TIME_TABLE = "update_time"; + + /** + * 业务流程状态 + */ + public static final String BPM_STATUS = "bpmStatus"; + /** + * 业务流程状态 + */ + public static final String BPM_STATUS_TABLE = "bpm_status"; + //*********系统建表标准字段**************************************** + + + /** + * 租户ID 实体字段名 + */ + String TENANT_ID = "tenantId"; + /** + * 租户ID 数据库字段名 + */ + String TENANT_ID_TABLE = "tenant_id"; + + /** + * sql语句 where + */ + String SQL_WHERE = "where"; + + /** + * sql语句 asc + */ + String SQL_ASC = "asc"; + + /** + * sqlserver数据库,中间有空格 + */ + String DB_TYPE_SQL_SERVER_BLANK = "sql server"; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/DynamicTableConstant.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/DynamicTableConstant.java new file mode 100644 index 0000000..0dbff6c --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/DynamicTableConstant.java @@ -0,0 +1,16 @@ +package org.jeecg.common.constant; + +/** + * 动态切换表配置常量 + * + * @author: scott + * @date: 2022年04月25日 22:30 + */ +public class DynamicTableConstant { + /** + * 角色首页配置表 + * vue2表名: sys_role_index + * vue3表名: sys_role_index_vue3 + */ + public static final String SYS_ROLE_INDEX = "sys_role_index"; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/FillRuleConstant.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/FillRuleConstant.java new file mode 100644 index 0000000..d2b07d7 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/FillRuleConstant.java @@ -0,0 +1,25 @@ +package org.jeecg.common.constant; + +/** + * 规则值生成 编码常量类 + * @author: taoyan + * @date: 2020年04月02日 + */ +public class FillRuleConstant { + + /** + * 公文发文编码 + */ + public static final String DOC_SEND = "doc_send_code"; + + /** + * 部门编码 + */ + public static final String DEPART = "org_num_role"; + + /** + * 分类字典编码 + */ + public static final String CATEGORY = "category_code_rule"; + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/ProvinceCityArea.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/ProvinceCityArea.java new file mode 100644 index 0000000..6c2f170 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/ProvinceCityArea.java @@ -0,0 +1,218 @@ +package org.jeecg.common.constant; + +import com.alibaba.fastjson.JSONObject; +import org.jeecg.common.util.oConvertUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Scanner; +import java.util.Set; +import java.util.List; + +/** + * @Description: 省市区 + * @author: jeecg-boot + */ +@Component("pca") +public class ProvinceCityArea { + List areaList; + + public String getText(String code){ + this.initAreaList(); + if(this.areaList!=null || this.areaList.size()>0){ + List ls = new ArrayList(); + getAreaByCode(code,ls); + return String.join("/",ls); + } + return ""; + } + + public String getCode(String text){ + this.initAreaList(); + if(areaList!=null && areaList.size()>0){ + for(int i=areaList.size()-1;i>=0;i--){ + //update-begin-author:taoyan date:2022-5-24 for:VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区 + String areaText = areaList.get(i).getText(); + String cityText = areaList.get(i).getAheadText(); + if(text.indexOf(areaText)>=0 && (cityText!=null && text.indexOf(cityText)>=0)){ + return areaList.get(i).getId(); + } + //update-end-author:taoyan date:2022-5-24 for:VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区 + } + } + return null; + } + + // update-begin-author:sunjianlei date:20220121 for:【JTC-704】数据导入错误 省市区组件,文件中为北京市,导入后,导为了山西省 + /** + * 获取省市区code,精准匹配 + * @param texts 文本数组,省,市,区 + * @return 返回 省市区的code + */ + public String[] getCode(String[] texts) { + if (texts == null || texts.length == 0) { + return null; + } + this.initAreaList(); + if (areaList == null || areaList.size() == 0) { + return null; + } + String[] codes = new String[texts.length]; + String code = null; + for (int i = 0; i < texts.length; i++) { + String text = texts[i]; + Area area; + if (code == null) { + area = getAreaByText(text); + } else { + area = getAreaByPidAndText(code, text); + } + if (area != null) { + code = area.id; + codes[i] = code; + } else { + return null; + } + } + return codes; + } + + /** + * 根据text获取area + * @param text + * @return + */ + public Area getAreaByText(String text) { + for (Area area : areaList) { + if (text.equals(area.getText())) { + return area; + } + } + return null; + } + + /** + * 通过pid获取 area 对象 + * @param pCode 父级编码 + * @param text + * @return + */ + public Area getAreaByPidAndText(String pCode, String text) { + this.initAreaList(); + if (this.areaList != null && this.areaList.size() > 0) { + for (Area area : this.areaList) { + if (area.getPid().equals(pCode) && area.getText().equals(text)) { + return area; + } + } + } + return null; + } + // update-end-author:sunjianlei date:20220121 for:【JTC-704】数据导入错误 省市区组件,文件中为北京市,导入后,导为了山西省 + + public void getAreaByCode(String code,List ls){ + for(Area area: areaList){ + if(area.getId().equals(code)){ + String pid = area.getPid(); + ls.add(0,area.getText()); + getAreaByCode(pid,ls); + } + } + } + + private void initAreaList(){ + //System.out.println("====================="); + if(this.areaList==null || this.areaList.size()==0){ + this.areaList = new ArrayList(); + try { + String jsonData = oConvertUtils.readStatic("classpath:static/pca.json"); + JSONObject baseJson = JSONObject.parseObject(jsonData); + //第一层 省 + JSONObject provinceJson = baseJson.getJSONObject("86"); + for(String provinceKey: provinceJson.keySet()){ + //System.out.println("===="+provinceKey); + Area province = new Area(provinceKey,provinceJson.getString(provinceKey),"86"); + this.areaList.add(province); + //第二层 市 + JSONObject cityJson = baseJson.getJSONObject(provinceKey); + for(String cityKey:cityJson.keySet()){ + //System.out.println("-----"+cityKey); + Area city = new Area(cityKey,cityJson.getString(cityKey),provinceKey); + this.areaList.add(city); + //第三层 区 + JSONObject areaJson = baseJson.getJSONObject(cityKey); + if(areaJson!=null){ + for(String areaKey:areaJson.keySet()){ + //System.out.println("········"+areaKey); + Area area = new Area(areaKey,areaJson.getString(areaKey),cityKey); + //update-begin-author:taoyan date:2022-5-24 for:VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区 + area.setAheadText(cityJson.getString(cityKey)); + //update-end-author:taoyan date:2022-5-24 for:VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区 + this.areaList.add(area); + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + + + private String jsonRead(File file){ + Scanner scanner = null; + StringBuilder buffer = new StringBuilder(); + try { + scanner = new Scanner(file, "utf-8"); + while (scanner.hasNextLine()) { + buffer.append(scanner.nextLine()); + } + } catch (Exception e) { + + } finally { + if (scanner != null) { + scanner.close(); + } + } + return buffer.toString(); + } + + class Area{ + String id; + String text; + String pid; + // 用于存储上级文本数据,区的上级文本 是市的数据 + String aheadText; + + public Area(String id,String text,String pid){ + this.id = id; + this.text = text; + this.pid = pid; + } + + public String getId() { + return id; + } + + public String getText() { + return text; + } + + public String getPid() { + return pid; + } + + public String getAheadText() { + return aheadText; + } + public void setAheadText(String aheadText) { + this.aheadText = aheadText; + } + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/ServiceNameConstants.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/ServiceNameConstants.java new file mode 100644 index 0000000..0d8711e --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/ServiceNameConstants.java @@ -0,0 +1,42 @@ +/* + * + * * Copyright (c) 2019-2020, 冷冷 (wangiegie@gmail.com). + * *

+ * * Licensed under the GNU Lesser General Public License 3.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * *

+ * * https://www.gnu.org/licenses/lgpl.html + * *

+ * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package org.jeecg.common.constant; + +/** + * @author scott + * @date 2019年05月18日 + * 服务名称 + */ +public interface ServiceNameConstants { + + /** + * 微服务名:系统管理模块 + */ + String SERVICE_SYSTEM = "jeecg-system"; + /** + * 微服务名:Demo模块 + */ + String SERVICE_DEMO = "jeecg-demo"; + + /** + * gateway通过header传递根路径 basePath + */ + String X_GATEWAY_BASE_PATH = "X_GATEWAY_BASE_PATH"; + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/SymbolConstant.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/SymbolConstant.java new file mode 100644 index 0000000..e18e6aa --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/SymbolConstant.java @@ -0,0 +1,119 @@ +package org.jeecg.common.constant; + +/** + * @Description: 符号和特殊符号常用类 + * @author: wangshuai + * @date: 2022年03月30日 17:44 + */ +public class SymbolConstant { + + /** + * 符号:点 + */ + public static final String SPOT = "."; + + /** + * 符号:双斜杠 + */ + public static final String DOUBLE_BACKSLASH = "\\"; + + /** + * 符号:冒号 + */ + public static final String COLON = ":"; + + /** + * 符号:逗号 + */ + public static final String COMMA = ","; + + /** + * 符号:左花括号 } + */ + public static final String LEFT_CURLY_BRACKET = "{"; + + /** + * 符号:右花括号 } + */ + public static final String RIGHT_CURLY_BRACKET = "}"; + + /** + * 符号:井号 # + */ + public static final String WELL_NUMBER = "#"; + + /** + * 符号:单斜杠 + */ + public static final String SINGLE_SLASH = "/"; + + /** + * 符号:双斜杠 + */ + public static final String DOUBLE_SLASH = "//"; + + /** + * 符号:感叹号 + */ + public static final String EXCLAMATORY_MARK = "!"; + + /** + * 符号:下划线 + */ + public static final String UNDERLINE = "_"; + + /** + * 符号:单引号 + */ + public static final String SINGLE_QUOTATION_MARK = "'"; + + /** + * 符号:星号 + */ + public static final String ASTERISK = "*"; + + /** + * 符号:百分号 + */ + public static final String PERCENT_SIGN = "%"; + + /** + * 符号:美元 $ + */ + public static final String DOLLAR = "$"; + + /** + * 符号:和 & + */ + public static final String AND = "&"; + + /** + * 符号:../ + */ + public static final String SPOT_SINGLE_SLASH = "../"; + + /** + * 符号:..\\ + */ + public static final String SPOT_DOUBLE_BACKSLASH = "..\\"; + + /** + * 系统变量前缀 #{ + */ + public static final String SYS_VAR_PREFIX = "#{"; + + /** + * 符号 {{ + */ + public static final String DOUBLE_LEFT_CURLY_BRACKET = "{{"; + + /** + * 符号:[ + */ + public static final String SQUARE_BRACKETS_LEFT = "["; + /** + * 符号:] + */ + public static final String SQUARE_BRACKETS_RIGHT = "]"; + +} \ No newline at end of file diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/VxeSocketConst.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/VxeSocketConst.java new file mode 100644 index 0000000..cac4d66 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/VxeSocketConst.java @@ -0,0 +1,33 @@ +package org.jeecg.common.constant; + +/** + * VXESocket 常量 + * + * update: 【类名改了大小写】 date: 2022-04-18 + * @author: jeecg-boot + */ +public class VxeSocketConst { + + /** + * 消息类型 + */ + public static final String TYPE = "type"; + /** + * 消息数据 + */ + public static final String DATA = "data"; + + /** + * 消息类型:心跳检测 + */ + public static final String TYPE_HB = "heart_beat"; + /** + * 消息类型:通用数据传递 + */ + public static final String TYPE_CSD = "common_send_date"; + /** + * 消息类型:更新vxe table数据 + */ + public static final String TYPE_UVT = "update_vxe_table"; + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/WebsocketConst.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/WebsocketConst.java new file mode 100644 index 0000000..a165e39 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/WebsocketConst.java @@ -0,0 +1,61 @@ +package org.jeecg.common.constant; + +/** + * @Description: Websocket常量类 + * @author: taoyan + * @date: 2020年03月23日 + */ +public class WebsocketConst { + + + /** + * 消息json key:cmd + */ + public static final String MSG_CMD = "cmd"; + + /** + * 消息json key:msgId + */ + public static final String MSG_ID = "msgId"; + + /** + * 消息json key:msgTxt + */ + public static final String MSG_TXT = "msgTxt"; + + /** + * 消息json key:userId + */ + public static final String MSG_USER_ID = "userId"; + + /** + * 消息类型 heartcheck + */ + public static final String CMD_CHECK = "heartcheck"; + + /** + * 消息类型 user 用户消息 + */ + public static final String CMD_USER = "user"; + + /** + * 消息类型 topic 系统通知 + */ + public static final String CMD_TOPIC = "topic"; + + /** + * 消息类型 email + */ + public static final String CMD_EMAIL = "email"; + + /** + * 消息类型 meetingsign 会议签到 + */ + public static final String CMD_SIGN = "sign"; + + /** + * 消息类型 新闻发布/取消 + */ + public static final String NEWS_PUBLISH = "publish"; + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/CgformEnum.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/CgformEnum.java new file mode 100644 index 0000000..e97c553 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/CgformEnum.java @@ -0,0 +1,152 @@ +package org.jeecg.common.constant.enums; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * online表单枚举 代码生成器用到 + * @author: jeecg-boot + */ +public enum CgformEnum { + + /** + * 单表 + */ + ONE(1, "one", "/jeecg/code-template-online", "default.one", "经典风格"), + /** + * 多表 + */ + MANY(2, "many", "/jeecg/code-template-online", "default.onetomany", "经典风格"), + /** + * 多表(jvxe风格) + * */ + JVXE_TABLE(2, "jvxe", "/jeecg/code-template-online", "jvxe.onetomany", "JVXE风格"), + + /** + * 多表 + */ + ERP(2, "erp", "/jeecg/code-template-online", "erp.onetomany", "ERP风格"), + /** + * 多表(内嵌子表风格) + */ + INNER_TABLE(2, "innerTable", "/jeecg/code-template-online", "inner-table.onetomany", "内嵌子表风格"), + /** + * 多表(tab风格) + * */ + TAB(2, "tab", "/jeecg/code-template-online", "tab.onetomany", "Tab风格"), + /** + * 树形列表 + */ + TREE(3, "tree", "/jeecg/code-template-online", "default.tree", "树形列表"); + + /** + * 类型 1/单表 2/一对多 3/树 + */ + int type; + /** + * 编码标识 + */ + String code; + /** + * 代码生成器模板路径 + */ + String templatePath; + /** + * 代码生成器模板路径 + */ + String stylePath; + /** + * 模板风格名称 + */ + String note; + + /** + * 构造器 + * + * @param type 类型 1/单表 2/一对多 3/树 + * @param code 模板编码 + * @param templatePath 模板路径 + * @param stylePath 模板子路径 + * @param note + */ + CgformEnum(int type, String code, String templatePath, String stylePath, String note) { + this.type = type; + this.code = code; + this.templatePath = templatePath; + this.stylePath = stylePath; + this.note = note; + } + + /** + * 根据code获取模板路径 + * + * @param code + * @return + */ + public static String getTemplatePathByConfig(String code) { + return getCgformEnumByConfig(code).templatePath; + } + + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public String getTemplatePath() { + return templatePath; + } + + public void setTemplatePath(String templatePath) { + this.templatePath = templatePath; + } + + public String getStylePath() { + return stylePath; + } + + public void setStylePath(String stylePath) { + this.stylePath = stylePath; + } + + /** + * 根据code找枚举 + * + * @param code + * @return + */ + public static CgformEnum getCgformEnumByConfig(String code) { + for (CgformEnum e : CgformEnum.values()) { + if (e.code.equals(code)) { + return e; + } + } + return null; + } + + /** + * 根据类型找所有 + * + * @param type + * @return + */ + public static List> getJspModelList(int type) { + List> ls = new ArrayList>(); + for (CgformEnum e : CgformEnum.values()) { + if (e.type == type) { + Map map = new HashMap(); + map.put("code", e.code); + map.put("note", e.note); + ls.add(map); + } + } + return ls; + } + + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/LowAppAopEnum.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/LowAppAopEnum.java new file mode 100644 index 0000000..81c22aa --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/LowAppAopEnum.java @@ -0,0 +1,25 @@ +package org.jeecg.common.constant.enums; + +/** + * LowApp 切面注解枚举 + * @date 2022-1-5 + * @author: jeecg-boot + */ +public enum LowAppAopEnum { + + /** + * 新增方法 + */ + ADD, + /** + * 删除方法(包含单个和批量删除) + */ + DELETE, + /** 复制表单操作 */ + COPY, + + /** + * Online表单专用:数据库表转Online表单 + */ + CGFORM_DB_IMPORT +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/MessageTypeEnum.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/MessageTypeEnum.java new file mode 100644 index 0000000..b2e65e0 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/MessageTypeEnum.java @@ -0,0 +1,68 @@ +package org.jeecg.common.constant.enums; + +import org.jeecg.common.system.annotation.EnumDict; +import org.jeecg.common.system.vo.DictModel; + +import java.util.ArrayList; +import java.util.List; + +/** + * 消息类型 + * @author: jeecg-boot + */ +@EnumDict("messageType") +public enum MessageTypeEnum { + + XT("system", "系统消息"), + YJ("email", "邮件消息"), + DD("dingtalk", "钉钉消息"), + QYWX("wechat_enterprise", "企业微信"); + + MessageTypeEnum(String type, String note){ + this.type = type; + this.note = note; + } + + /** + * 消息类型 + */ + String type; + + /** + * 类型说明 + */ + String note; + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + + /** + * 获取字典数据 + * @return + */ + public static List getDictList(){ + List list = new ArrayList<>(); + DictModel dictModel = null; + for(MessageTypeEnum e: MessageTypeEnum.values()){ + dictModel = new DictModel(); + dictModel.setValue(e.getType()); + dictModel.setText(e.getNote()); + list.add(dictModel); + } + return list; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/ModuleType.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/ModuleType.java new file mode 100644 index 0000000..5d3aa1e --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/ModuleType.java @@ -0,0 +1,18 @@ +package org.jeecg.common.constant.enums; + +/** + * 日志按模块分类 + * @author: jeecg-boot + */ +public enum ModuleType { + + /** + * 普通 + */ + COMMON, + + /** + * online + */ + ONLINE; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/OperateTypeEnum.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/OperateTypeEnum.java new file mode 100644 index 0000000..a438b40 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/OperateTypeEnum.java @@ -0,0 +1,95 @@ +package org.jeecg.common.constant.enums; + +import org.jeecg.common.constant.CommonConstant; + +/** + * @Description: 操作类型 + * @author: jeecg-boot + * @date: 2022/3/31 10:05 + */ +public enum OperateTypeEnum { + + /** + * 列表 + */ + LIST(CommonConstant.OPERATE_TYPE_1, "list"), + + /** + * 新增 + */ + ADD(CommonConstant.OPERATE_TYPE_2, "add"), + + /** + * 编辑 + */ + EDIT(CommonConstant.OPERATE_TYPE_3, "edit"), + + /** + * 删除 + */ + DELETE(CommonConstant.OPERATE_TYPE_4, "delete"), + + /** + * 导入 + */ + IMPORT(CommonConstant.OPERATE_TYPE_5, "import"), + + /** + * 导出 + */ + EXPORT(CommonConstant.OPERATE_TYPE_6, "export"); + + /** + * 类型 1列表,2新增,3编辑,4删除,5导入,6导出 + */ + int type; + + /** + * 编码(请求方式) + */ + String code; + + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + /** + * 构造器 + * + * @param type 类型 + * @param code 编码(请求方式) + */ + OperateTypeEnum(int type, String code) { + this.type = type; + this.code = code; + } + + + /** + * 根据请求名称匹配 + * + * @param methodName 请求名称 + * @return Integer 类型 + */ + public static Integer getTypeByMethodName(String methodName) { + for (OperateTypeEnum e : OperateTypeEnum.values()) { + if (methodName.startsWith(e.getCode())) { + return e.getType(); + } + } + return CommonConstant.OPERATE_TYPE_1; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/RoleIndexConfigEnum.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/RoleIndexConfigEnum.java new file mode 100644 index 0000000..09d9c10 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/RoleIndexConfigEnum.java @@ -0,0 +1,93 @@ +package org.jeecg.common.constant.enums; + +import org.jeecg.common.util.oConvertUtils; + +import java.util.List; + +/** + * 首页自定义 + * 通过角色编码与首页组件路径配置 + * 枚举的顺序有权限高低权重作用(也就是配置多个角色,在前面的角色首页,会优先生效) + * @author: jeecg-boot + */ +public enum RoleIndexConfigEnum { + + /**首页自定义 admin*/ + ADMIN("admin", "dashboard/Analysis"), + //TEST("test", "dashboard/IndexChart"), + /**首页自定义 hr*/ + HR("hr", "dashboard/IndexBdc"); + //DM("dm", "dashboard/IndexTask"), + + /** + * 角色编码 + */ + String roleCode; + /** + * 路由index + */ + String componentUrl; + + /** + * 构造器 + * + * @param roleCode 角色编码 + * @param componentUrl 首页组件路径(规则跟菜单配置一样) + */ + RoleIndexConfigEnum(String roleCode, String componentUrl) { + this.roleCode = roleCode; + this.componentUrl = componentUrl; + } + /** + * 根据code找枚举 + * @param roleCode 角色编码 + * @return + */ + private static RoleIndexConfigEnum getEnumByCode(String roleCode) { + for (RoleIndexConfigEnum e : RoleIndexConfigEnum.values()) { + if (e.roleCode.equals(roleCode)) { + return e; + } + } + return null; + } + /** + * 根据code找index + * @param roleCode 角色编码 + * @return + */ + private static String getIndexByCode(String roleCode) { + for (RoleIndexConfigEnum e : RoleIndexConfigEnum.values()) { + if (e.roleCode.equals(roleCode)) { + return e.componentUrl; + } + } + return null; + } + + public static String getIndexByRoles(List roles) { + String[] rolesArray = roles.toArray(new String[roles.size()]); + for (RoleIndexConfigEnum e : RoleIndexConfigEnum.values()) { + if (oConvertUtils.isIn(e.roleCode,rolesArray)){ + return e.componentUrl; + } + } + return null; + } + + public String getRoleCode() { + return roleCode; + } + + public void setRoleCode(String roleCode) { + this.roleCode = roleCode; + } + + public String getComponentUrl() { + return componentUrl; + } + + public void setComponentUrl(String componentUrl) { + this.componentUrl = componentUrl; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/annotation/SensitiveDecode.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/annotation/SensitiveDecode.java new file mode 100644 index 0000000..698ecba --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/annotation/SensitiveDecode.java @@ -0,0 +1,20 @@ +package org.jeecg.common.desensitization.annotation; + +import java.lang.annotation.*; + +/** + * 解密注解 + * + * 在方法上定义 将方法返回对象中的敏感字段 解密,需要注意的是,如果没有加密过,解密会出问题,返回原字符串 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +public @interface SensitiveDecode { + + /** + * 指明需要脱敏的实体类class + * @return + */ + Class entity() default Object.class; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/annotation/SensitiveEncode.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/annotation/SensitiveEncode.java new file mode 100644 index 0000000..eb89d75 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/annotation/SensitiveEncode.java @@ -0,0 +1,20 @@ +package org.jeecg.common.desensitization.annotation; + +import java.lang.annotation.*; + +/** + * 加密注解 + * + * 在方法上声明 将方法返回对象中的敏感字段 加密/格式化 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +public @interface SensitiveEncode { + + /** + * 指明需要脱敏的实体类class + * @return + */ + Class entity() default Object.class; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/annotation/SensitiveField.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/annotation/SensitiveField.java new file mode 100644 index 0000000..a887e5a --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/annotation/SensitiveField.java @@ -0,0 +1,21 @@ +package org.jeecg.common.desensitization.annotation; + + +import org.jeecg.common.desensitization.enums.SensitiveEnum; + +import java.lang.annotation.*; + +/** + * 在字段上定义 标识字段存储的信息是敏感的 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface SensitiveField { + + /** + * 不同类型处理不同 + * @return + */ + SensitiveEnum type() default SensitiveEnum.ENCODE; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/aspect/SensitiveDataAspect.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/aspect/SensitiveDataAspect.java new file mode 100644 index 0000000..da69702 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/aspect/SensitiveDataAspect.java @@ -0,0 +1,81 @@ +package org.jeecg.common.desensitization.aspect; + +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.jeecg.common.desensitization.annotation.SensitiveDecode; +import org.jeecg.common.desensitization.annotation.SensitiveEncode; +import org.jeecg.common.desensitization.util.SensitiveInfoUtil; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.util.List; + +/** + * 敏感数据切面处理类 + * @Author taoYan + * @Date 2022/4/20 17:45 + **/ +@Slf4j +@Aspect +@Component +public class SensitiveDataAspect { + + /** + * 定义切点Pointcut + */ + @Pointcut("@annotation(org.jeecg.common.desensitization.annotation.SensitiveEncode) || @annotation(org.jeecg.common.desensitization.annotation.SensitiveDecode)") + public void sensitivePointCut() { + } + + @Around("sensitivePointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable { + // 处理结果 + Object result = point.proceed(); + if(result == null){ + return result; + } + Class resultClass = result.getClass(); + log.debug(" resultClass = {}" , resultClass); + + if(resultClass.isPrimitive()){ + //是基本类型 直接返回 不需要处理 + return result; + } + // 获取方法注解信息:是哪个实体、是加密还是解密 + boolean isEncode = true; + Class entity = null; + MethodSignature methodSignature = (MethodSignature) point.getSignature(); + Method method = methodSignature.getMethod(); + SensitiveEncode encode = method.getAnnotation(SensitiveEncode.class); + if(encode==null){ + SensitiveDecode decode = method.getAnnotation(SensitiveDecode.class); + if(decode!=null){ + entity = decode.entity(); + isEncode = false; + } + }else{ + entity = encode.entity(); + } + + long startTime=System.currentTimeMillis(); + if(resultClass.equals(entity) || entity.equals(Object.class)){ + // 方法返回实体和注解的entity一样,如果注解没有申明entity属性则认为是(方法返回实体和注解的entity一样) + SensitiveInfoUtil.handlerObject(result, isEncode); + } else if(result instanceof List){ + // 方法返回List<实体> + SensitiveInfoUtil.handleList(result, entity, isEncode); + }else{ + // 方法返回一个对象 + SensitiveInfoUtil.handleNestedObject(result, entity, isEncode); + } + long endTime=System.currentTimeMillis(); + log.info((isEncode ? "加密操作," : "解密操作,") + "Aspect程序耗时:" + (endTime - startTime) + "ms"); + + return result; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/enums/SensitiveEnum.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/enums/SensitiveEnum.java new file mode 100644 index 0000000..bdffc75 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/enums/SensitiveEnum.java @@ -0,0 +1,55 @@ +package org.jeecg.common.desensitization.enums; + +/** + * 敏感字段信息类型 + */ +public enum SensitiveEnum { + + + /** + * 加密 + */ + ENCODE, + + /** + * 中文名 + */ + CHINESE_NAME, + + /** + * 身份证号 + */ + ID_CARD, + + /** + * 座机号 + */ + FIXED_PHONE, + + /** + * 手机号 + */ + MOBILE_PHONE, + + /** + * 地址 + */ + ADDRESS, + + /** + * 电子邮件 + */ + EMAIL, + + /** + * 银行卡 + */ + BANK_CARD, + + /** + * 公司开户银行联号 + */ + CNAPS_CODE; + + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/util/SensitiveInfoUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/util/SensitiveInfoUtil.java new file mode 100644 index 0000000..e872437 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/desensitization/util/SensitiveInfoUtil.java @@ -0,0 +1,362 @@ +package org.jeecg.common.desensitization.util; + +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.desensitization.annotation.SensitiveField; +import org.jeecg.common.desensitization.enums.SensitiveEnum; +import org.jeecg.common.util.encryption.AesEncryptUtil; +import org.jeecg.common.util.oConvertUtils; + +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.util.Collections; +import java.util.List; + +/** + * 敏感信息处理工具类 + * @author taoYan + * @date 2022/4/20 18:01 + **/ +@Slf4j +public class SensitiveInfoUtil { + + /** + * 处理嵌套对象 + * @param obj 方法返回值 + * @param entity 实体class + * @param isEncode 是否加密(true: 加密操作 / false:解密操作) + * @throws IllegalAccessException + */ + public static void handleNestedObject(Object obj, Class entity, boolean isEncode) throws IllegalAccessException { + Field[] fields = obj.getClass().getDeclaredFields(); + for (Field field : fields) { + if(field.getType().isPrimitive()){ + continue; + } + if(field.getType().equals(entity)){ + // 对象里面是实体 + field.setAccessible(true); + Object nestedObject = field.get(obj); + handlerObject(nestedObject, isEncode); + break; + }else{ + // 对象里面是List<实体> + if(field.getGenericType() instanceof ParameterizedType){ + ParameterizedType pt = (ParameterizedType)field.getGenericType(); + if(pt.getRawType().equals(List.class)){ + if(pt.getActualTypeArguments()[0].equals(entity)){ + field.setAccessible(true); + Object nestedObject = field.get(obj); + handleList(nestedObject, entity, isEncode); + break; + } + } + } + } + } + } + + /** + * 处理Object + * @param obj 方法返回值 + * @param isEncode 是否加密(true: 加密操作 / false:解密操作) + * @return + * @throws IllegalAccessException + */ + public static Object handlerObject(Object obj, boolean isEncode) throws IllegalAccessException { + log.debug(" obj --> "+ obj.toString()); + long startTime=System.currentTimeMillis(); + if (oConvertUtils.isEmpty(obj)) { + return obj; + } + // 判断是不是一个对象 + Field[] fields = obj.getClass().getDeclaredFields(); + for (Field field : fields) { + boolean isSensitiveField = field.isAnnotationPresent(SensitiveField.class); + if(isSensitiveField){ + // 必须有SensitiveField注解 才作处理 + if(field.getType().isAssignableFrom(String.class)){ + //必须是字符串类型 才作处理 + field.setAccessible(true); + String realValue = (String) field.get(obj); + if(realValue==null || "".equals(realValue)){ + continue; + } + SensitiveField sf = field.getAnnotation(SensitiveField.class); + if(isEncode==true){ + //加密 + String value = SensitiveInfoUtil.getEncodeData(realValue, sf.type()); + field.set(obj, value); + }else{ + //解密只处理 encode类型的 + if(sf.type().equals(SensitiveEnum.ENCODE)){ + String value = SensitiveInfoUtil.getDecodeData(realValue); + field.set(obj, value); + } + } + } + } + } + //long endTime=System.currentTimeMillis(); + //log.info((isEncode ? "加密操作," : "解密操作,") + "当前程序耗时:" + (endTime - startTime) + "ms"); + return obj; + } + + /** + * 处理 List<实体> + * @param obj + * @param entity + * @param isEncode(true: 加密操作 / false:解密操作) + */ + public static void handleList(Object obj, Class entity, boolean isEncode){ + List list = (List)obj; + if(list.size()>0){ + Object first = list.get(0); + if(first.getClass().equals(entity)){ + for(int i=0; i + */ + private static String chineseName(String fullName) { + if (oConvertUtils.isEmpty(fullName)) { + return ""; + } + return formatRight(fullName, 1); + } + + /** + * [中文姓名] 只显示第一个汉字,其他隐藏为2个星号 + * @param familyName 姓 + * @param firstName 名 + * @return <例子:李**> + */ + private static String chineseName(String familyName, String firstName) { + if (oConvertUtils.isEmpty(familyName) || oConvertUtils.isEmpty(firstName)) { + return ""; + } + return chineseName(familyName + firstName); + } + + /** + * [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。 + * @param id 身份证号 + * @return <例子:*************5762> + */ + private static String idCardNum(String id) { + if (oConvertUtils.isEmpty(id)) { + return ""; + } + return formatLeft(id, 4); + + } + + /** + * [固定电话] 后四位,其他隐藏 + * @param num 固定电话 + * @return <例子:****1234> + */ + private static String fixedPhone(String num) { + if (oConvertUtils.isEmpty(num)) { + return ""; + } + return formatLeft(num, 4); + } + + /** + * [手机号码] 前三位,后四位,其他隐藏 + * @param num 手机号码 + * @return <例子:138******1234> + */ + private static String mobilePhone(String num) { + if (oConvertUtils.isEmpty(num)) { + return ""; + } + int len = num.length(); + if(len<11){ + return num; + } + return formatBetween(num, 3, 4); + } + + /** + * [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护 + * @param address 地址 + * @param sensitiveSize 敏感信息长度 + * @return <例子:北京市海淀区****> + */ + private static String address(String address, int sensitiveSize) { + if (oConvertUtils.isEmpty(address)) { + return ""; + } + int len = address.length(); + if(len + */ + private static String email(String email) { + if (oConvertUtils.isEmpty(email)) { + return ""; + } + int index = email.indexOf("@"); + if (index <= 1){ + return email; + } + String begin = email.substring(0, 1); + String end = email.substring(index); + String stars = "**"; + return begin + stars + end; + } + + /** + * [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号 + * @param cardNum 银行卡号 + * @return <例子:6222600**********1234> + */ + private static String bankCard(String cardNum) { + if (oConvertUtils.isEmpty(cardNum)) { + return ""; + } + return formatBetween(cardNum, 6, 4); + } + + /** + * [公司开户银行联号] 公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号 + * @param code 公司开户银行联号 + * @return <例子:12********> + */ + private static String cnapsCode(String code) { + if (oConvertUtils.isEmpty(code)) { + return ""; + } + return formatRight(code, 2); + } + + + /** + * 将右边的格式化成* + * @param str 字符串 + * @param reservedLength 保留长度 + * @return 格式化后的字符串 + */ + private static String formatRight(String str, int reservedLength){ + String name = str.substring(0, reservedLength); + String stars = String.join("", Collections.nCopies(str.length()-reservedLength, "*")); + return name + stars; + } + + /** + * 将左边的格式化成* + * @param str 字符串 + * @param reservedLength 保留长度 + * @return 格式化后的字符串 + */ + private static String formatLeft(String str, int reservedLength){ + int len = str.length(); + String show = str.substring(len-reservedLength); + String stars = String.join("", Collections.nCopies(len-reservedLength, "*")); + return stars + show; + } + + /** + * 将中间的格式化成* + * @param str 字符串 + * @param beginLen 开始保留长度 + * @param endLen 结尾保留长度 + * @return 格式化后的字符串 + */ + private static String formatBetween(String str, int beginLen, int endLen){ + int len = str.length(); + String begin = str.substring(0, beginLen); + String end = str.substring(len-endLen); + String stars = String.join("", Collections.nCopies(len-beginLen-endLen, "*")); + return begin + stars + end; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBoot401Exception.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBoot401Exception.java new file mode 100644 index 0000000..6f7205b --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBoot401Exception.java @@ -0,0 +1,23 @@ +package org.jeecg.common.exception; + +/** + * @Description: jeecg-boot自定义401异常 + * @author: jeecg-boot + */ +public class JeecgBoot401Exception extends RuntimeException { + private static final long serialVersionUID = 1L; + + public JeecgBoot401Exception(String message){ + super(message); + } + + public JeecgBoot401Exception(Throwable cause) + { + super(cause); + } + + public JeecgBoot401Exception(String message, Throwable cause) + { + super(message,cause); + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBootException.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBootException.java new file mode 100644 index 0000000..fe0706c --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBootException.java @@ -0,0 +1,23 @@ +package org.jeecg.common.exception; + +/** + * @Description: jeecg-boot自定义异常 + * @author: jeecg-boot + */ +public class JeecgBootException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public JeecgBootException(String message){ + super(message); + } + + public JeecgBootException(Throwable cause) + { + super(cause); + } + + public JeecgBootException(String message,Throwable cause) + { + super(message,cause); + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBootExceptionHandler.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBootExceptionHandler.java new file mode 100644 index 0000000..6d5d8ef --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBootExceptionHandler.java @@ -0,0 +1,136 @@ +package org.jeecg.common.exception; + +import cn.hutool.core.util.ObjectUtil; +import org.apache.shiro.authz.AuthorizationException; +import org.apache.shiro.authz.UnauthorizedException; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.enums.SentinelErrorInfoEnum; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.data.redis.connection.PoolException; +import org.springframework.http.HttpStatus; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.multipart.MaxUploadSizeExceededException; +import org.springframework.web.servlet.NoHandlerFoundException; + +import lombok.extern.slf4j.Slf4j; + +/** + * 异常处理器 + * + * @Author scott + * @Date 2019 + */ +@RestControllerAdvice +@Slf4j +public class JeecgBootExceptionHandler { + + /** + * 处理自定义异常 + */ + @ExceptionHandler(JeecgBootException.class) + public Result handleJeecgBootException(JeecgBootException e){ + log.error(e.getMessage(), e); + return Result.error(e.getMessage()); + } + + /** + * 处理自定义微服务异常 + */ + @ExceptionHandler(JeecgCloudException.class) + public Result handleJeecgCloudException(JeecgCloudException e){ + log.error(e.getMessage(), e); + return Result.error(e.getMessage()); + } + + /** + * 处理自定义异常 + */ + @ExceptionHandler(JeecgBoot401Exception.class) + @ResponseStatus(HttpStatus.UNAUTHORIZED) + public Result handleJeecgBoot401Exception(JeecgBoot401Exception e){ + log.error(e.getMessage(), e); + return new Result(401,e.getMessage()); + } + + @ExceptionHandler(NoHandlerFoundException.class) + public Result handlerNoFoundException(Exception e) { + log.error(e.getMessage(), e); + return Result.error(404, "路径不存在,请检查路径是否正确"); + } + + @ExceptionHandler(DuplicateKeyException.class) + public Result handleDuplicateKeyException(DuplicateKeyException e){ + log.error(e.getMessage(), e); + return Result.error("数据库中已存在该记录"); + } + + @ExceptionHandler({UnauthorizedException.class, AuthorizationException.class}) + public Result handleAuthorizationException(AuthorizationException e){ + log.error(e.getMessage(), e); + return Result.noauth("没有权限,请联系管理员授权"); + } + + @ExceptionHandler(Exception.class) + public Result handleException(Exception e){ + log.error(e.getMessage(), e); + //update-begin---author:zyf ---date:20220411 for:处理Sentinel限流自定义异常 + Throwable throwable = e.getCause(); + SentinelErrorInfoEnum errorInfoEnum = SentinelErrorInfoEnum.getErrorByException(throwable); + if (ObjectUtil.isNotEmpty(errorInfoEnum)) { + return Result.error(errorInfoEnum.getError()); + } + //update-end---author:zyf ---date:20220411 for:处理Sentinel限流自定义异常 + return Result.error("操作失败,"+e.getMessage()); + } + + /** + * @Author 政辉 + * @param e + * @return + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public Result httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e){ + StringBuffer sb = new StringBuffer(); + sb.append("不支持"); + sb.append(e.getMethod()); + sb.append("请求方法,"); + sb.append("支持以下"); + String [] methods = e.getSupportedMethods(); + if(methods!=null){ + for(String str:methods){ + sb.append(str); + sb.append("、"); + } + } + log.error(sb.toString(), e); + //return Result.error("没有权限,请联系管理员授权"); + return Result.error(405,sb.toString()); + } + + /** + * spring默认上传大小100MB 超出大小捕获异常MaxUploadSizeExceededException + */ + @ExceptionHandler(MaxUploadSizeExceededException.class) + public Result handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) { + log.error(e.getMessage(), e); + return Result.error("文件大小超出10MB限制, 请压缩或降低文件质量! "); + } + + @ExceptionHandler(DataIntegrityViolationException.class) + public Result handleDataIntegrityViolationException(DataIntegrityViolationException e) { + log.error(e.getMessage(), e); + //【issues/3624】数据库执行异常handleDataIntegrityViolationException提示有误 #3624 + return Result.error("执行数据库异常,违反了完整性例如:违反惟一约束、违反非空限制、字段内容超出长度等"); + } + + @ExceptionHandler(PoolException.class) + public Result handlePoolException(PoolException e) { + log.error(e.getMessage(), e); + return Result.error("Redis 连接异常!"); + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/executor/ConstomThreadPool.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/executor/ConstomThreadPool.java new file mode 100644 index 0000000..95da581 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/executor/ConstomThreadPool.java @@ -0,0 +1,21 @@ +package org.jeecg.common.executor; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class ConstomThreadPool extends ThreadPoolExecutor { + /** + * + * @param corePoolSize 核心线程池 + * @param maximumPoolSize 线程池最大数量 + * @param keepAliveTime 线程存活时间 + * @param unit TimeUnit + * @param workQueue 工作队列,自定义大小 + * @param poolName 线程工厂自定义线程名称 + */ + public ConstomThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, String poolName) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); + setThreadFactory(new CustomThreadFactory(poolName, false)); + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/executor/CustomExecutors.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/executor/CustomExecutors.java new file mode 100644 index 0000000..c51fdcb --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/executor/CustomExecutors.java @@ -0,0 +1,64 @@ +package org.jeecg.common.executor; + +import java.util.concurrent.*; + +public final class CustomExecutors { + /** + * 核心线程数大小 + */ + private static final int CORE_POOL_SIZE = 1; + /** + * 核心线程池大小 + */ + private static final int MAX_POOL_SIZE = 5; + /** + * 线程存活时间 + */ + private static final int KEEP_ALIVE_TIME = 60; + /** + * 工作队列大小 + */ + private static final LinkedBlockingQueue queue = new LinkedBlockingQueue<>(1000); + /** + * 自定义线程池名前缀 + */ + private static final String POOL_PREFIX_NAME = "Custom-Common-Pool"; + + private CustomExecutors() { + //throw new XXXXException("un support create pool!"); + } + + private static ConstomThreadPool constomThreadPool; + + /** + * 静态块初始化只执行一次,不关闭,整个系统公用一个线程池 + */ + static { + constomThreadPool = new ConstomThreadPool(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, queue, POOL_PREFIX_NAME); + } + + /** + * 单例模式获取线程池 + * + * @return ExecutorService + */ + public static ExecutorService getInstance() { + return constomThreadPool; + } + + public static Future submit(Runnable task) { + return constomThreadPool.submit(task); + } + + public static Future submit(Runnable task, T result) { + return constomThreadPool.submit(task, result); + } + + public static Future submit(Callable task) { + return constomThreadPool.submit(task); + } + + public static void execute(Runnable task) { + constomThreadPool.execute(task); + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/executor/CustomThreadFactory.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/executor/CustomThreadFactory.java new file mode 100644 index 0000000..02d3d92 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/executor/CustomThreadFactory.java @@ -0,0 +1,45 @@ +package org.jeecg.common.executor; + +import org.apache.commons.lang3.StringUtils; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 自定义线程工厂 + */ +public class CustomThreadFactory implements ThreadFactory { + /** + * 线程前缀,采用AtomicInteger实现线程编号线程安全自增 + */ + private final AtomicInteger atomicInteger = new AtomicInteger(1); + /** + * 线程命名前缀 + */ + private final String namePrefix; + /** + * 线程工厂创建的线程是否是守护线程 + */ + private final boolean isDaemon; + + public CustomThreadFactory(String prefix, boolean daemin) { + if (StringUtils.isNotBlank(prefix)) { + this.namePrefix = prefix; + } else { + this.namePrefix = "thread_pool"; + } + // 是否是守护线程 + isDaemon = daemin; + } + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, namePrefix + "-" + atomicInteger.getAndIncrement()); + thread.setDaemon(isDaemon); + // 设置线程优先级 + if (thread.getPriority() != Thread.NORM_PRIORITY) { + thread.setPriority(Thread.NORM_PRIORITY); + } + return thread; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/handler/IFillRuleHandler.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/handler/IFillRuleHandler.java new file mode 100644 index 0000000..7c6478b --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/handler/IFillRuleHandler.java @@ -0,0 +1,22 @@ +package org.jeecg.common.handler; + +import com.alibaba.fastjson.JSONObject; + +/** + * 填值规则接口 + * + * @author Yan_东 + * 如需使用填值规则功能,规则实现类必须实现此接口 + */ +public interface IFillRuleHandler { + + /** + * 填值规则 + * @param params 页面配置固定参数 + * @param formData 动态表单参数 + * @return + */ + public Object execute(JSONObject params, JSONObject formData); + +} + diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/annotation/EnumDict.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/annotation/EnumDict.java new file mode 100644 index 0000000..1ffb760 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/annotation/EnumDict.java @@ -0,0 +1,19 @@ +package org.jeecg.common.system.annotation; + +import java.lang.annotation.*; + +/** + * 将枚举类转化成字典数据 + * @Author taoYan + * @Date 2022/7/8 10:34 + **/ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface EnumDict { + + /** + * 作为字典数据的唯一编码 + */ + String value() default ""; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/controller/JeecgController.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/controller/JeecgController.java new file mode 100644 index 0000000..83c52d3 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/controller/JeecgController.java @@ -0,0 +1,205 @@ +package org.jeecg.common.system.base.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.query.QueryGenerator; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.util.oConvertUtils; +import org.jeecgframework.poi.excel.ExcelImportUtil; +import org.jeecgframework.poi.excel.def.NormalExcelConstants; +import org.jeecgframework.poi.excel.entity.ExportParams; +import org.jeecgframework.poi.excel.entity.ImportParams; +import org.jeecgframework.poi.excel.entity.enmus.ExcelType; +import org.jeecgframework.poi.excel.view.JeecgEntityExcelView; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @Description: Controller基类 + * @Author: dangzhenghui@163.com + * @Date: 2019-4-21 8:13 + * @Version: 1.0 + */ +@Slf4j +public class JeecgController> { + /**issues/2933 JeecgController注入service时改用protected修饰,能避免重复引用service*/ + @Autowired + protected S service; + + @Value("${jeecg.path.upload}") + private String upLoadPath; + /** + * 导出excel + * + * @param request + */ + protected ModelAndView exportXls(HttpServletRequest request, T object, Class clazz, String title) { + // Step.1 组装查询条件 + QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper(object, request.getParameterMap()); + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + + // 过滤选中数据 + String selections = request.getParameter("selections"); + if (oConvertUtils.isNotEmpty(selections)) { + List selectionList = Arrays.asList(selections.split(",")); + queryWrapper.in("id",selectionList); + } + // Step.2 获取导出数据 + List exportList = service.list(queryWrapper); + + // Step.3 AutoPoi 导出Excel + ModelAndView mv = new ModelAndView(new JeecgEntityExcelView()); + //此处设置的filename无效 ,前端会重更新设置一下 + mv.addObject(NormalExcelConstants.FILE_NAME, title); + mv.addObject(NormalExcelConstants.CLASS, clazz); + //update-begin--Author:liusq Date:20210126 for:图片导出报错,ImageBasePath未设置-------------------- + String name = null != sysUser ? sysUser.getRealname() : ""; + ExportParams exportParams=new ExportParams(title + "报表", "导出人:" + name, title); + exportParams.setImageBasePath(upLoadPath); + //update-end--Author:liusq Date:20210126 for:图片导出报错,ImageBasePath未设置---------------------- + mv.addObject(NormalExcelConstants.PARAMS,exportParams); + mv.addObject(NormalExcelConstants.DATA_LIST, exportList); + return mv; + } + /** + * 根据每页sheet数量导出多sheet + * + * @param request + * @param object 实体类 + * @param clazz 实体类class + * @param title 标题 + * @param exportFields 导出字段自定义 + * @param pageNum 每个sheet的数据条数 + * @param request + */ + protected ModelAndView exportXlsSheet(HttpServletRequest request, T object, Class clazz, String title,String exportFields,Integer pageNum) { + // Step.1 组装查询条件 + QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper(object, request.getParameterMap()); + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + // Step.2 计算分页sheet数据 + double total = service.count(); + int count = (int)Math.ceil(total/pageNum); + //update-begin-author:liusq---date:20220629--for: 多sheet导出根据选择导出写法调整 --- + // Step.3 过滤选中数据 + String selections = request.getParameter("selections"); + if (oConvertUtils.isNotEmpty(selections)) { + List selectionList = Arrays.asList(selections.split(",")); + queryWrapper.in("id",selectionList); + } + //update-end-author:liusq---date:20220629--for: 多sheet导出根据选择导出写法调整 --- + // Step.4 多sheet处理 + List> listMap = new ArrayList>(); + for (int i = 1; i <=count ; i++) { + Page page = new Page(i, pageNum); + IPage pageList = service.page(page, queryWrapper); + List exportList = pageList.getRecords(); + Map map = new HashMap<>(5); + String name = null != sysUser ? sysUser.getRealname() : ""; + ExportParams exportParams=new ExportParams(title + "报表", "导出人:" + name, title+i,upLoadPath); + exportParams.setType(ExcelType.XSSF); + //map.put("title",exportParams); + //表格Title + map.put(NormalExcelConstants.PARAMS,exportParams); + //表格对应实体 + map.put(NormalExcelConstants.CLASS,clazz); + //数据集合 + map.put(NormalExcelConstants.DATA_LIST, exportList); + listMap.add(map); + } + // Step.4 AutoPoi 导出Excel + ModelAndView mv = new ModelAndView(new JeecgEntityExcelView()); + //此处设置的filename无效 ,前端会重更新设置一下 + mv.addObject(NormalExcelConstants.FILE_NAME, title); + mv.addObject(NormalExcelConstants.MAP_LIST, listMap); + return mv; + } + + + /** + * 根据权限导出excel,传入导出字段参数 + * + * @param request + */ + protected ModelAndView exportXls(HttpServletRequest request, T object, Class clazz, String title,String exportFields) { + ModelAndView mv = this.exportXls(request,object,clazz,title); + mv.addObject(NormalExcelConstants.EXPORT_FIELDS,exportFields); + return mv; + } + + /** + * 获取对象ID + * + * @return + */ + private String getId(T item) { + try { + return PropertyUtils.getProperty(item, "id").toString(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 通过excel导入数据 + * + * @param request + * @param response + * @return + */ + protected Result importExcel(HttpServletRequest request, HttpServletResponse response, Class clazz) { + MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; + Map fileMap = multipartRequest.getFileMap(); + for (Map.Entry entity : fileMap.entrySet()) { + // 获取上传文件对象 + MultipartFile file = entity.getValue(); + ImportParams params = new ImportParams(); + params.setTitleRows(2); + params.setHeadRows(1); + params.setNeedSave(true); + try { + List list = ExcelImportUtil.importExcel(file.getInputStream(), clazz, params); + //update-begin-author:taoyan date:20190528 for:批量插入数据 + long start = System.currentTimeMillis(); + service.saveBatch(list); + //400条 saveBatch消耗时间1592毫秒 循环插入消耗时间1947毫秒 + //1200条 saveBatch消耗时间3687毫秒 循环插入消耗时间5212毫秒 + log.info("消耗时间" + (System.currentTimeMillis() - start) + "毫秒"); + //update-end-author:taoyan date:20190528 for:批量插入数据 + return Result.ok("文件导入成功!数据行数:" + list.size()); + } catch (Exception e) { + //update-begin-author:taoyan date:20211124 for: 导入数据重复增加提示 + String msg = e.getMessage(); + log.error(msg, e); + if(msg!=null && msg.indexOf("Duplicate entry")>=0){ + return Result.error("文件导入失败:有重复数据!"); + }else{ + return Result.error("文件导入失败:" + e.getMessage()); + } + //update-end-author:taoyan date:20211124 for: 导入数据重复增加提示 + } finally { + try { + file.getInputStream().close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return Result.error("文件导入失败!"); + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/entity/JeecgEntity.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/entity/JeecgEntity.java new file mode 100644 index 0000000..2eebaff --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/entity/JeecgEntity.java @@ -0,0 +1,68 @@ +package org.jeecg.common.system.base.entity; + +import java.io.Serializable; + +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * @Description: Entity基类 + * @Author: dangzhenghui@163.com + * @Date: 2019-4-28 + * @Version: 1.1 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +public class JeecgEntity implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(type = IdType.ASSIGN_ID) + @ApiModelProperty(value = "ID") + private java.lang.String id; + + /** + * 创建人 + */ + @ApiModelProperty(value = "创建人") + @Excel(name = "创建人", width = 15) + private java.lang.String createBy; + + /** + * 创建时间 + */ + @ApiModelProperty(value = "创建时间") + @Excel(name = "创建时间", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private java.util.Date createTime; + + /** + * 更新人 + */ + @ApiModelProperty(value = "更新人") + @Excel(name = "更新人", width = 15) + private java.lang.String updateBy; + + /** + * 更新时间 + */ + @ApiModelProperty(value = "更新时间") + @Excel(name = "更新时间", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private java.util.Date updateTime; + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/service/JeecgService.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/service/JeecgService.java new file mode 100644 index 0000000..919f3ff --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/service/JeecgService.java @@ -0,0 +1,12 @@ +package org.jeecg.common.system.base.service; + +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * @Description: Service基类 + * @Author: dangzhenghui@163.com + * @Date: 2019-4-21 8:13 + * @Version: 1.0 + */ +public interface JeecgService extends IService { +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/service/impl/JeecgServiceImpl.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/service/impl/JeecgServiceImpl.java new file mode 100644 index 0000000..f19ba84 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/service/impl/JeecgServiceImpl.java @@ -0,0 +1,19 @@ +package org.jeecg.common.system.base.service.impl; + +import org.jeecg.common.system.base.entity.JeecgEntity; +import org.jeecg.common.system.base.service.JeecgService; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; + +/** + * @Description: ServiceImpl基类 + * @Author: dangzhenghui@163.com + * @Date: 2019-4-21 8:13 + * @Version: 1.0 + */ +@Slf4j +public class JeecgServiceImpl, T extends JeecgEntity> extends ServiceImpl implements JeecgService { + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/MatchTypeEnum.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/MatchTypeEnum.java new file mode 100644 index 0000000..d19342d --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/MatchTypeEnum.java @@ -0,0 +1,45 @@ +package org.jeecg.common.system.query; + +import org.jeecg.common.util.oConvertUtils; + +/** + * 查询链接规则 + * + * @Author Sunjianlei + */ +public enum MatchTypeEnum { + + /**查询链接规则 AND*/ + AND("AND"), + /**查询链接规则 OR*/ + OR("OR"); + + private String value; + + MatchTypeEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static MatchTypeEnum getByValue(Object value) { + if (oConvertUtils.isEmpty(value)) { + return null; + } + return getByValue(value.toString()); + } + + public static MatchTypeEnum getByValue(String value) { + if (oConvertUtils.isEmpty(value)) { + return null; + } + for (MatchTypeEnum val : values()) { + if (val.getValue().toLowerCase().equals(value.toLowerCase())) { + return val; + } + } + return null; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryCondition.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryCondition.java new file mode 100644 index 0000000..566280d --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryCondition.java @@ -0,0 +1,73 @@ +package org.jeecg.common.system.query; + +import java.io.Serializable; + +/** + * @Description: QueryCondition + * @author: jeecg-boot + */ +public class QueryCondition implements Serializable { + + private static final long serialVersionUID = 4740166316629191651L; + + private String field; + /** 组件的类型(例如:input、select、radio) */ + private String type; + /** + * 对应的数据库字段的类型 + * 支持:int、bigDecimal、short、long、float、double、boolean + */ + private String dbType; + private String rule; + private String val; + + public String getField() { + return field; + } + + public void setField(String field) { + this.field = field; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getDbType() { + return dbType; + } + + public void setDbType(String dbType) { + this.dbType = dbType; + } + + public String getRule() { + return rule; + } + + public void setRule(String rule) { + this.rule = rule; + } + + public String getVal() { + return val; + } + + public void setVal(String val) { + this.val = val; + } + + @Override + public String toString(){ + StringBuffer sb =new StringBuffer(); + if(field == null || "".equals(field)){ + return ""; + } + sb.append(this.field).append(" ").append(this.rule).append(" ").append(this.type).append(" ").append(this.dbType).append(" ").append(this.val); + return sb.toString(); + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryGenerator.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryGenerator.java new file mode 100644 index 0000000..4a1411f --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryGenerator.java @@ -0,0 +1,1231 @@ +package org.jeecg.common.system.query; + +import java.beans.PropertyDescriptor; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.math.BigDecimal; +import java.net.URLDecoder; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.apache.commons.beanutils.PropertyUtils; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.constant.DataBaseConstant; +import org.jeecg.common.constant.SymbolConstant; +import org.jeecg.common.exception.JeecgBootException; +import org.jeecg.common.system.util.JeecgDataAutorUtils; +import org.jeecg.common.system.util.JwtUtil; +import org.jeecg.common.system.vo.SysPermissionDataRuleModel; +import org.jeecg.common.util.CommonUtils; +import org.jeecg.common.util.DateUtils; +import org.jeecg.common.util.SqlInjectionUtil; +import org.jeecg.common.util.oConvertUtils; +import org.springframework.util.NumberUtils; + +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; + +import lombok.extern.slf4j.Slf4j; + +/** + * @Description: 查询生成器 + * @author: jeecg-boot + */ +@Slf4j +public class QueryGenerator { + public static final String SQL_RULES_COLUMN = "SQL_RULES_COLUMN"; + + private static final String BEGIN = "_begin"; + private static final String END = "_end"; + /** + * 数字类型字段,拼接此后缀 接受多值参数 + */ + private static final String MULTI = "_MultiString"; + private static final String STAR = "*"; + private static final String COMMA = ","; + /** + * 查询 逗号转义符 相当于一个逗号【作废】 + */ + public static final String QUERY_COMMA_ESCAPE = "++"; + private static final String NOT_EQUAL = "!"; + /**页面带有规则值查询,空格作为分隔符*/ + private static final String QUERY_SEPARATE_KEYWORD = " "; + /**高级查询前端传来的参数名*/ + private static final String SUPER_QUERY_PARAMS = "superQueryParams"; + /** 高级查询前端传来的拼接方式参数名 */ + private static final String SUPER_QUERY_MATCH_TYPE = "superQueryMatchType"; + /** 单引号 */ + public static final String SQL_SQ = "'"; + /**排序列*/ + private static final String ORDER_COLUMN = "column"; + /**排序方式*/ + private static final String ORDER_TYPE = "order"; + private static final String ORDER_TYPE_ASC = "ASC"; + + /**mysql 模糊查询之特殊字符下划线 (_、\)*/ + public static final String LIKE_MYSQL_SPECIAL_STRS = "_,%"; + + /**日期格式化yyyy-MM-dd*/ + public static final String YYYY_MM_DD = "yyyy-MM-dd"; + + /**to_date*/ + public static final String TO_DATE = "to_date"; + + /**时间格式化 */ + private static final ThreadLocal LOCAL = new ThreadLocal(); + private static SimpleDateFormat getTime(){ + SimpleDateFormat time = LOCAL.get(); + if(time == null){ + time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + LOCAL.set(time); + } + return time; + } + + /** + * 获取查询条件构造器QueryWrapper实例 通用查询条件已被封装完成 + * @param searchObj 查询实体 + * @param parameterMap request.getParameterMap() + * @return QueryWrapper实例 + */ + public static QueryWrapper initQueryWrapper(T searchObj,Map parameterMap){ + long start = System.currentTimeMillis(); + QueryWrapper queryWrapper = new QueryWrapper(); + installMplus(queryWrapper, searchObj, parameterMap); + log.debug("---查询条件构造器初始化完成,耗时:"+(System.currentTimeMillis()-start)+"毫秒----"); + return queryWrapper; + } + + /** + * 组装Mybatis Plus 查询条件 + *

使用此方法 需要有如下几点注意: + *
1.使用QueryWrapper 而非LambdaQueryWrapper; + *
2.实例化QueryWrapper时不可将实体传入参数 + *
错误示例:如QueryWrapper queryWrapper = new QueryWrapper(jeecgDemo); + *
正确示例:QueryWrapper queryWrapper = new QueryWrapper(); + *
3.也可以不使用这个方法直接调用 {@link #initQueryWrapper}直接获取实例 + */ + private static void installMplus(QueryWrapper queryWrapper,Object searchObj,Map parameterMap) { + + /* + * 注意:权限查询由前端配置数据规则 当一个人有多个所属部门时候 可以在规则配置包含条件 orgCode 包含 #{sys_org_code} + 但是不支持在自定义SQL中写orgCode in #{sys_org_code} + 当一个人只有一个部门 就直接配置等于条件: orgCode 等于 #{sys_org_code} 或者配置自定义SQL: orgCode = '#{sys_org_code}' + */ + + //区间条件组装 模糊查询 高级查询组装 简单排序 权限查询 + PropertyDescriptor[] origDescriptors = PropertyUtils.getPropertyDescriptors(searchObj); + Map ruleMap = getRuleMap(); + + //权限规则自定义SQL表达式 + for (String c : ruleMap.keySet()) { + if(oConvertUtils.isNotEmpty(c) && c.startsWith(SQL_RULES_COLUMN)){ + queryWrapper.and(i ->i.apply(getSqlRuleValue(ruleMap.get(c).getRuleValue()))); + } + } + + String name, type, column; + // update-begin--Author:taoyan Date:20200923 for:issues/1671 如果字段加注解了@TableField(exist = false),不走DB查询------- + //定义实体字段和数据库字段名称的映射 高级查询中 只能获取实体字段 如果设置TableField注解 那么查询条件会出问题 + Map fieldColumnMap = new HashMap<>(5); + for (int i = 0; i < origDescriptors.length; i++) { + //aliasName = origDescriptors[i].getName(); mybatis 不存在实体属性 不用处理别名的情况 + name = origDescriptors[i].getName(); + type = origDescriptors[i].getPropertyType().toString(); + try { + if (judgedIsUselessField(name)|| !PropertyUtils.isReadable(searchObj, name)) { + continue; + } + + Object value = PropertyUtils.getSimpleProperty(searchObj, name); + column = getTableFieldName(searchObj.getClass(), name); + if(column==null){ + //column为null只有一种情况 那就是 添加了注解@TableField(exist = false) 后续都不用处理了 + continue; + } + fieldColumnMap.put(name,column); + //数据权限查询 + if(ruleMap.containsKey(name)) { + addRuleToQueryWrapper(ruleMap.get(name), column, origDescriptors[i].getPropertyType(), queryWrapper); + } + //区间查询 + doIntervalQuery(queryWrapper, parameterMap, type, name, column); + //判断单值 参数带不同标识字符串 走不同的查询 + //TODO 这种前后带逗号的支持分割后模糊查询(多选字段查询生效) 示例:,1,3, + if (null != value && value.toString().startsWith(COMMA) && value.toString().endsWith(COMMA)) { + String multiLikeval = value.toString().replace(",,", COMMA); + String[] vals = multiLikeval.substring(1, multiLikeval.length()).split(COMMA); + final String field = oConvertUtils.camelToUnderline(column); + if(vals.length>1) { + queryWrapper.and(j -> { + log.info("---查询过滤器,Query规则---field:{}, rule:{}, value:{}", field, "like", vals[0]); + j = j.like(field,vals[0]); + for (int k=1;k j.like(field,vals[0])); + } + }else { + //根据参数值带什么关键字符串判断走什么类型的查询 + QueryRuleEnum rule = convert2Rule(value); + value = replaceValue(rule,value); + // add -begin 添加判断为字符串时设为全模糊查询 + //if( (rule==null || QueryRuleEnum.EQ.equals(rule)) && "class java.lang.String".equals(type)) { + // 可以设置左右模糊或全模糊,因人而异 + //rule = QueryRuleEnum.LIKE; + //} + // add -end 添加判断为字符串时设为全模糊查询 + addEasyQuery(queryWrapper, column, rule, value); + } + + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + // 排序逻辑 处理 + doMultiFieldsOrder(queryWrapper, parameterMap, fieldColumnMap.keySet()); + + //高级查询 + doSuperQuery(queryWrapper, parameterMap, fieldColumnMap); + // update-end--Author:taoyan Date:20200923 for:issues/1671 如果字段加注解了@TableField(exist = false),不走DB查询------- + + } + + + /** + * 区间查询 + * @param queryWrapper query对象 + * @param parameterMap 参数map + * @param type 字段类型 + * @param filedName 字段名称 + * @param columnName 列名称 + */ + private static void doIntervalQuery(QueryWrapper queryWrapper, Map parameterMap, String type, String filedName, String columnName) throws ParseException { + // 添加 判断是否有区间值 + String endValue = null,beginValue = null; + if (parameterMap != null && parameterMap.containsKey(filedName + BEGIN)) { + beginValue = parameterMap.get(filedName + BEGIN)[0].trim(); + addQueryByRule(queryWrapper, columnName, type, beginValue, QueryRuleEnum.GE); + + } + if (parameterMap != null && parameterMap.containsKey(filedName + END)) { + endValue = parameterMap.get(filedName + END)[0].trim(); + addQueryByRule(queryWrapper, columnName, type, endValue, QueryRuleEnum.LE); + } + //多值查询 + if (parameterMap != null && parameterMap.containsKey(filedName + MULTI)) { + endValue = parameterMap.get(filedName + MULTI)[0].trim(); + addQueryByRule(queryWrapper, columnName.replace(MULTI,""), type, endValue, QueryRuleEnum.IN); + } + } + + private static void doMultiFieldsOrder(QueryWrapper queryWrapper,Map parameterMap, Set allFields) { + String column=null,order=null; + if(parameterMap!=null&& parameterMap.containsKey(ORDER_COLUMN)) { + column = parameterMap.get(ORDER_COLUMN)[0]; + } + if(parameterMap!=null&& parameterMap.containsKey(ORDER_TYPE)) { + order = parameterMap.get(ORDER_TYPE)[0]; + } + log.debug("排序规则>>列:" + column + ",排序方式:" + order); + if (oConvertUtils.isNotEmpty(column) && oConvertUtils.isNotEmpty(order)) { + //字典字段,去掉字典翻译文本后缀 + if(column.endsWith(CommonConstant.DICT_TEXT_SUFFIX)) { + column = column.substring(0, column.lastIndexOf(CommonConstant.DICT_TEXT_SUFFIX)); + } + + //update-begin-author:taoyan date:2022-5-16 for: issues/3676 获取系统用户列表时,使用SQL注入生效 + //判断column是不是当前实体的 + log.info("当前字段有:"+ allFields); + if (!allColumnExist(column, allFields)) { + throw new JeecgBootException("请注意,将要排序的列字段不存在:" + column); + } + //update-end-author:taoyan date:2022-5-16 for: issues/3676 获取系统用户列表时,使用SQL注入生效 + + //SQL注入check + SqlInjectionUtil.filterContent(column); + + //update-begin--Author:scott Date:20210531 for:36 多条件排序无效问题修正------- + // 排序规则修改 + // 将现有排序 _ 前端传递排序条件{....,column: 'column1,column2',order: 'desc'} 翻译成sql "column1,column2 desc" + // 修改为 _ 前端传递排序条件{....,column: 'column1,column2',order: 'desc'} 翻译成sql "column1 desc,column2 desc" + if (order.toUpperCase().indexOf(ORDER_TYPE_ASC)>=0) { + //queryWrapper.orderByAsc(oConvertUtils.camelToUnderline(column)); + String columnStr = oConvertUtils.camelToUnderline(column); + String[] columnArray = columnStr.split(","); + queryWrapper.orderByAsc(Arrays.asList(columnArray)); + } else { + //queryWrapper.orderByDesc(oConvertUtils.camelToUnderline(column)); + String columnStr = oConvertUtils.camelToUnderline(column); + String[] columnArray = columnStr.split(","); + queryWrapper.orderByDesc(Arrays.asList(columnArray)); + } + //update-end--Author:scott Date:20210531 for:36 多条件排序无效问题修正------- + } + } + + //update-begin-author:taoyan date:2022-5-23 for: issues/3676 获取系统用户列表时,使用SQL注入生效 + /** + * 多字段排序 判断所传字段是否存在 + * @return + */ + private static boolean allColumnExist(String columnStr, Set allFields){ + boolean exist = true; + if(columnStr.indexOf(COMMA)>=0){ + String[] arr = columnStr.split(COMMA); + for(String column: arr){ + if(!allFields.contains(column)){ + exist = false; + break; + } + } + }else{ + exist = allFields.contains(columnStr); + } + return exist; + } + //update-end-author:taoyan date:2022-5-23 for: issues/3676 获取系统用户列表时,使用SQL注入生效 + + /** + * 高级查询 + * @param queryWrapper 查询对象 + * @param parameterMap 参数对象 + * @param fieldColumnMap 实体字段和数据库列对应的map + */ + private static void doSuperQuery(QueryWrapper queryWrapper,Map parameterMap, Map fieldColumnMap) { + if(parameterMap!=null&& parameterMap.containsKey(SUPER_QUERY_PARAMS)){ + String superQueryParams = parameterMap.get(SUPER_QUERY_PARAMS)[0]; + String superQueryMatchType = parameterMap.get(SUPER_QUERY_MATCH_TYPE) != null ? parameterMap.get(SUPER_QUERY_MATCH_TYPE)[0] : MatchTypeEnum.AND.getValue(); + MatchTypeEnum matchType = MatchTypeEnum.getByValue(superQueryMatchType); + // update-begin--Author:sunjianlei Date:20200325 for:高级查询的条件要用括号括起来,防止和用户的其他条件冲突 ------- + try { + superQueryParams = URLDecoder.decode(superQueryParams, "UTF-8"); + List conditions = JSON.parseArray(superQueryParams, QueryCondition.class); + if (conditions == null || conditions.size() == 0) { + return; + } + // update-begin-author:sunjianlei date:20220119 for: 【JTC-573】 过滤空条件查询,防止 sql 拼接多余的 and + List filterConditions = conditions.stream().filter( + rule -> oConvertUtils.isNotEmpty(rule.getField()) + && oConvertUtils.isNotEmpty(rule.getRule()) + && oConvertUtils.isNotEmpty(rule.getVal()) + ).collect(Collectors.toList()); + if (filterConditions.size() == 0) { + return; + } + // update-end-author:sunjianlei date:20220119 for: 【JTC-573】 过滤空条件查询,防止 sql 拼接多余的 and + log.info("---高级查询参数-->" + filterConditions); + + queryWrapper.and(andWrapper -> { + for (int i = 0; i < filterConditions.size(); i++) { + QueryCondition rule = filterConditions.get(i); + if (oConvertUtils.isNotEmpty(rule.getField()) + && oConvertUtils.isNotEmpty(rule.getRule()) + && oConvertUtils.isNotEmpty(rule.getVal())) { + + log.debug("SuperQuery ==> " + rule.toString()); + + //update-begin-author:taoyan date:20201228 for: 【高级查询】 oracle 日期等于查询报错 + Object queryValue = rule.getVal(); + if("date".equals(rule.getType())){ + queryValue = DateUtils.str2Date(rule.getVal(),DateUtils.date_sdf.get()); + }else if("datetime".equals(rule.getType())){ + queryValue = DateUtils.str2Date(rule.getVal(), DateUtils.datetimeFormat.get()); + } + // update-begin--author:sunjianlei date:20210702 for:【/issues/I3VR8E】高级查询没有类型转换,查询参数都是字符串类型 ---- + String dbType = rule.getDbType(); + if (oConvertUtils.isNotEmpty(dbType)) { + try { + String valueStr = String.valueOf(queryValue); + switch (dbType.toLowerCase().trim()) { + case "int": + queryValue = Integer.parseInt(valueStr); + break; + case "bigdecimal": + queryValue = new BigDecimal(valueStr); + break; + case "short": + queryValue = Short.parseShort(valueStr); + break; + case "long": + queryValue = Long.parseLong(valueStr); + break; + case "float": + queryValue = Float.parseFloat(valueStr); + break; + case "double": + queryValue = Double.parseDouble(valueStr); + break; + case "boolean": + queryValue = Boolean.parseBoolean(valueStr); + break; + default: + } + } catch (Exception e) { + log.error("高级查询值转换失败:", e); + } + } + // update-begin--author:sunjianlei date:20210702 for:【/issues/I3VR8E】高级查询没有类型转换,查询参数都是字符串类型 ---- + addEasyQuery(andWrapper, fieldColumnMap.get(rule.getField()), QueryRuleEnum.getByValue(rule.getRule()), queryValue); + //update-end-author:taoyan date:20201228 for: 【高级查询】 oracle 日期等于查询报错 + + // 如果拼接方式是OR,就拼接OR + if (MatchTypeEnum.OR == matchType && i < (filterConditions.size() - 1)) { + andWrapper.or(); + } + } + } + //return andWrapper; + }); + } catch (UnsupportedEncodingException e) { + log.error("--高级查询参数转码失败:" + superQueryParams, e); + } catch (Exception e) { + log.error("--高级查询拼接失败:" + e.getMessage()); + e.printStackTrace(); + } + // update-end--Author:sunjianlei Date:20200325 for:高级查询的条件要用括号括起来,防止和用户的其他条件冲突 ------- + } + //log.info(" superQuery getCustomSqlSegment: "+ queryWrapper.getCustomSqlSegment()); + } + /** + * 根据所传的值 转化成对应的比较方式 + * 支持><= like in ! + * @param value + * @return + */ + public static QueryRuleEnum convert2Rule(Object value) { + // 避免空数据 + // update-begin-author:taoyan date:20210629 for: 查询条件输入空格导致return null后续判断导致抛出null异常 + if (value == null) { + return QueryRuleEnum.EQ; + } + String val = (value + "").toString().trim(); + if (val.length() == 0) { + return QueryRuleEnum.EQ; + } + // update-end-author:taoyan date:20210629 for: 查询条件输入空格导致return null后续判断导致抛出null异常 + QueryRuleEnum rule =null; + + //update-begin--Author:scott Date:20190724 for:initQueryWrapper组装sql查询条件错误 #284------------------- + //TODO 此处规则,只适用于 le lt ge gt + // step 2 .>= =< + int length2 = 2; + int length3 = 3; + if (rule == null && val.length() >= length3) { + if(QUERY_SEPARATE_KEYWORD.equals(val.substring(length2, length3))){ + rule = QueryRuleEnum.getByValue(val.substring(0, 2)); + } + } + // step 1 .> < + if (rule == null && val.length() >= length2) { + if(QUERY_SEPARATE_KEYWORD.equals(val.substring(1, length2))){ + rule = QueryRuleEnum.getByValue(val.substring(0, 1)); + } + } + //update-end--Author:scott Date:20190724 for:initQueryWrapper组装sql查询条件错误 #284--------------------- + + // step 3 like + //update-begin-author:taoyan for: /issues/3382 默认带*就走模糊,但是如果只有一个*,那么走等于查询 + if(rule == null && val.equals(STAR)){ + rule = QueryRuleEnum.EQ; + } + //update-end-author:taoyan for: /issues/3382 默认带*就走模糊,但是如果只有一个*,那么走等于查询 + if (rule == null && val.contains(STAR)) { + if (val.startsWith(STAR) && val.endsWith(STAR)) { + rule = QueryRuleEnum.LIKE; + } else if (val.startsWith(STAR)) { + rule = QueryRuleEnum.LEFT_LIKE; + } else if(val.endsWith(STAR)){ + rule = QueryRuleEnum.RIGHT_LIKE; + } + } + + // step 4 in + if (rule == null && val.contains(COMMA)) { + //TODO in 查询这里应该有个bug 如果一字段本身就是多选 此时用in查询 未必能查询出来 + rule = QueryRuleEnum.IN; + } + // step 5 != + if(rule == null && val.startsWith(NOT_EQUAL)){ + rule = QueryRuleEnum.NE; + } + // step 6 xx+xx+xx 这种情况适用于如果想要用逗号作精确查询 但是系统默认逗号走in 所以可以用++替换【此逻辑作废】 + if(rule == null && val.indexOf(QUERY_COMMA_ESCAPE)>0){ + rule = QueryRuleEnum.EQ_WITH_ADD; + } + + //update-begin--Author:taoyan Date:20201229 for:initQueryWrapper组装sql查询条件错误 #284--------------------- + //特殊处理:Oracle的表达式to_date('xxx','yyyy-MM-dd')含有逗号,会被识别为in查询,转为等于查询 + if(rule == QueryRuleEnum.IN && val.indexOf(YYYY_MM_DD)>=0 && val.indexOf(TO_DATE)>=0){ + rule = QueryRuleEnum.EQ; + } + //update-end--Author:taoyan Date:20201229 for:initQueryWrapper组装sql查询条件错误 #284--------------------- + + return rule != null ? rule : QueryRuleEnum.EQ; + } + + /** + * 替换掉关键字字符 + * + * @param rule + * @param value + * @return + */ + private static Object replaceValue(QueryRuleEnum rule, Object value) { + if (rule == null) { + return null; + } + if (! (value instanceof String)){ + return value; + } + String val = (value + "").toString().trim(); + //update-begin-author:taoyan date:20220302 for: 查询条件的值为等号(=)bug #3443 + if(QueryRuleEnum.EQ.getValue().equals(val)){ + return val; + } + //update-end-author:taoyan date:20220302 for: 查询条件的值为等号(=)bug #3443 + if (rule == QueryRuleEnum.LIKE) { + value = val.substring(1, val.length() - 1); + //mysql 模糊查询之特殊字符下划线 (_、\) + value = specialStrConvert(value.toString()); + } else if (rule == QueryRuleEnum.LEFT_LIKE || rule == QueryRuleEnum.NE) { + value = val.substring(1); + //mysql 模糊查询之特殊字符下划线 (_、\) + value = specialStrConvert(value.toString()); + } else if (rule == QueryRuleEnum.RIGHT_LIKE) { + value = val.substring(0, val.length() - 1); + //mysql 模糊查询之特殊字符下划线 (_、\) + value = specialStrConvert(value.toString()); + } else if (rule == QueryRuleEnum.IN) { + value = val.split(","); + } else if (rule == QueryRuleEnum.EQ_WITH_ADD) { + value = val.replaceAll("\\+\\+", COMMA); + }else { + //update-begin--Author:scott Date:20190724 for:initQueryWrapper组装sql查询条件错误 #284------------------- + if(val.startsWith(rule.getValue())){ + //TODO 此处逻辑应该注释掉-> 如果查询内容中带有查询匹配规则符号,就会被截取的(比如:>=您好) + value = val.replaceFirst(rule.getValue(),""); + }else if(val.startsWith(rule.getCondition()+QUERY_SEPARATE_KEYWORD)){ + value = val.replaceFirst(rule.getCondition()+QUERY_SEPARATE_KEYWORD,"").trim(); + } + //update-end--Author:scott Date:20190724 for:initQueryWrapper组装sql查询条件错误 #284------------------- + } + return value; + } + + private static void addQueryByRule(QueryWrapper queryWrapper,String name,String type,String value,QueryRuleEnum rule) throws ParseException { + if(oConvertUtils.isNotEmpty(value)) { + //update-begin--Author:sunjianlei Date:20220104 for:【JTC-409】修复逗号分割情况下没有转换类型,导致类型严格的数据库查询报错 ------------------- + // 针对数字类型字段,多值查询 + if(value.contains(COMMA)){ + Object[] temp = Arrays.stream(value.split(COMMA)).map(v -> { + try { + return QueryGenerator.parseByType(v, type, rule); + } catch (ParseException e) { + e.printStackTrace(); + return v; + } + }).toArray(); + addEasyQuery(queryWrapper, name, rule, temp); + return; + } + Object temp = QueryGenerator.parseByType(value, type, rule); + addEasyQuery(queryWrapper, name, rule, temp); + //update-end--Author:sunjianlei Date:20220104 for:【JTC-409】修复逗号分割情况下没有转换类型,导致类型严格的数据库查询报错 ------------------- + } + } + + /** + * 根据类型转换给定的值 + * @param value + * @param type + * @param rule + * @return + * @throws ParseException + */ + private static Object parseByType(String value, String type, QueryRuleEnum rule) throws ParseException { + Object temp; + switch (type) { + case "class java.lang.Integer": + temp = Integer.parseInt(value); + break; + case "class java.math.BigDecimal": + temp = new BigDecimal(value); + break; + case "class java.lang.Short": + temp = Short.parseShort(value); + break; + case "class java.lang.Long": + temp = Long.parseLong(value); + break; + case "class java.lang.Float": + temp = Float.parseFloat(value); + break; + case "class java.lang.Double": + temp = Double.parseDouble(value); + break; + case "class java.util.Date": + temp = getDateQueryByRule(value, rule); + break; + default: + temp = value; + break; + } + return temp; + } + + /** + * 获取日期类型的值 + * @param value + * @param rule + * @return + * @throws ParseException + */ + private static Date getDateQueryByRule(String value,QueryRuleEnum rule) throws ParseException { + Date date = null; + int length = 10; + if(value.length()==length) { + if(rule==QueryRuleEnum.GE) { + //比较大于 + date = getTime().parse(value + " 00:00:00"); + }else if(rule==QueryRuleEnum.LE) { + //比较小于 + date = getTime().parse(value + " 23:59:59"); + } + //TODO 日期类型比较特殊 可能oracle下不一定好使 + } + if(date==null) { + date = getTime().parse(value); + } + return date; + } + + /** + * 根据规则走不同的查询 + * @param queryWrapper QueryWrapper + * @param name 字段名字 + * @param rule 查询规则 + * @param value 查询条件值 + */ + public static void addEasyQuery(QueryWrapper queryWrapper, String name, QueryRuleEnum rule, Object value) { + if (value == null || rule == null || oConvertUtils.isEmpty(value)) { + return; + } + name = oConvertUtils.camelToUnderline(name); + log.info("---查询过滤器,Query规则---field:{}, rule:{}, value:{}",name,rule.getValue(),value); + switch (rule) { + case GT: + queryWrapper.gt(name, value); + break; + case GE: + queryWrapper.ge(name, value); + break; + case LT: + queryWrapper.lt(name, value); + break; + case LE: + queryWrapper.le(name, value); + break; + case EQ: + case EQ_WITH_ADD: + queryWrapper.eq(name, value); + break; + case NE: + queryWrapper.ne(name, value); + break; + case IN: + if(value instanceof String) { + queryWrapper.in(name, (Object[])value.toString().split(COMMA)); + }else if(value instanceof String[]) { + queryWrapper.in(name, (Object[]) value); + } + //update-begin-author:taoyan date:20200909 for:【bug】in 类型多值查询 不适配postgresql #1671 + else if(value.getClass().isArray()) { + queryWrapper.in(name, (Object[])value); + }else { + queryWrapper.in(name, value); + } + //update-end-author:taoyan date:20200909 for:【bug】in 类型多值查询 不适配postgresql #1671 + break; + case LIKE: + queryWrapper.like(name, value); + break; + case LEFT_LIKE: + queryWrapper.likeLeft(name, value); + break; + case RIGHT_LIKE: + queryWrapper.likeRight(name, value); + break; + default: + log.info("--查询规则未匹配到---"); + break; + } + } + /** + * + * @param name + * @return + */ + private static boolean judgedIsUselessField(String name) { + return "class".equals(name) || "ids".equals(name) + || "page".equals(name) || "rows".equals(name) + || "sort".equals(name) || "order".equals(name); + } + + + + /** + * 获取请求对应的数据权限规则 TODO 相同列权限多个 有问题 + * @return + */ + public static Map getRuleMap() { + Map ruleMap = new HashMap<>(5); + List list =JeecgDataAutorUtils.loadDataSearchConditon(); + if(list != null&&list.size()>0){ + if(list.get(0)==null){ + return ruleMap; + } + for (SysPermissionDataRuleModel rule : list) { + String column = rule.getRuleColumn(); + if(QueryRuleEnum.SQL_RULES.getValue().equals(rule.getRuleConditions())) { + column = SQL_RULES_COLUMN+rule.getId(); + } + ruleMap.put(column, rule); + } + } + return ruleMap; + } + + private static void addRuleToQueryWrapper(SysPermissionDataRuleModel dataRule, String name, Class propertyType, QueryWrapper queryWrapper) { + QueryRuleEnum rule = QueryRuleEnum.getByValue(dataRule.getRuleConditions()); + if(rule.equals(QueryRuleEnum.IN) && ! propertyType.equals(String.class)) { + String[] values = dataRule.getRuleValue().split(","); + Object[] objs = new Object[values.length]; + for (int i = 0; i < values.length; i++) { + objs[i] = NumberUtils.parseNumber(values[i], propertyType); + } + addEasyQuery(queryWrapper, name, rule, objs); + }else { + if (propertyType.equals(String.class)) { + addEasyQuery(queryWrapper, name, rule, converRuleValue(dataRule.getRuleValue())); + }else if (propertyType.equals(Date.class)) { + String dateStr =converRuleValue(dataRule.getRuleValue()); + int length = 10; + if(dateStr.length()==length){ + addEasyQuery(queryWrapper, name, rule, DateUtils.str2Date(dateStr,DateUtils.date_sdf.get())); + }else{ + addEasyQuery(queryWrapper, name, rule, DateUtils.str2Date(dateStr,DateUtils.datetimeFormat.get())); + } + }else { + addEasyQuery(queryWrapper, name, rule, NumberUtils.parseNumber(dataRule.getRuleValue(), propertyType)); + } + } + } + + public static String converRuleValue(String ruleValue) { + String value = JwtUtil.getUserSystemData(ruleValue,null); + return value!= null ? value : ruleValue; + } + + /** + * @author: scott + * @Description: 去掉值前后单引号 + * @date: 2020/3/19 21:26 + * @param ruleValue: + * @Return: java.lang.String + */ + public static String trimSingleQuote(String ruleValue) { + if (oConvertUtils.isEmpty(ruleValue)) { + return ""; + } + if (ruleValue.startsWith(QueryGenerator.SQL_SQ)) { + ruleValue = ruleValue.substring(1); + } + if (ruleValue.endsWith(QueryGenerator.SQL_SQ)) { + ruleValue = ruleValue.substring(0, ruleValue.length() - 1); + } + return ruleValue; + } + + public static String getSqlRuleValue(String sqlRule){ + try { + Set varParams = getSqlRuleParams(sqlRule); + for(String var:varParams){ + String tempValue = converRuleValue(var); + sqlRule = sqlRule.replace("#{"+var+"}",tempValue); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return sqlRule; + } + + /** + * 获取sql中的#{key} 这个key组成的set + */ + public static Set getSqlRuleParams(String sql) { + if(oConvertUtils.isEmpty(sql)){ + return null; + } + Set varParams = new HashSet(); + String regex = "\\#\\{\\w+\\}"; + + Pattern p = Pattern.compile(regex); + Matcher m = p.matcher(sql); + while(m.find()){ + String var = m.group(); + varParams.add(var.substring(var.indexOf("{")+1,var.indexOf("}"))); + } + return varParams; + } + + /** + * 获取查询条件 + * @param field + * @param alias + * @param value + * @param isString + * @return + */ + public static String getSingleQueryConditionSql(String field,String alias,Object value,boolean isString) { + return getSingleQueryConditionSql(field, alias, value, isString,null); + } + + /** + * 报表获取查询条件 支持多数据源 + * @param field + * @param alias + * @param value + * @param isString + * @param dataBaseType + * @return + */ + public static String getSingleQueryConditionSql(String field,String alias,Object value,boolean isString, String dataBaseType) { + if (value == null) { + return ""; + } + field = alias+oConvertUtils.camelToUnderline(field); + QueryRuleEnum rule = QueryGenerator.convert2Rule(value); + return getSingleSqlByRule(rule, field, value, isString, dataBaseType); + } + + /** + * 获取单个查询条件的值 + * @param rule + * @param field + * @param value + * @param isString + * @param dataBaseType + * @return + */ + private static String getSingleSqlByRule(QueryRuleEnum rule,String field,Object value,boolean isString, String dataBaseType) { + String res = ""; + switch (rule) { + case GT: + res =field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType); + break; + case GE: + res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType); + break; + case LT: + res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType); + break; + case LE: + res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType); + break; + case EQ: + res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType); + break; + case EQ_WITH_ADD: + res = field+" = "+getFieldConditionValue(value, isString, dataBaseType); + break; + case NE: + res = field+" <> "+getFieldConditionValue(value, isString, dataBaseType); + break; + case IN: + res = field + " in "+getInConditionValue(value, isString); + break; + case LIKE: + res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.LIKE); + break; + case LEFT_LIKE: + res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.LEFT_LIKE); + break; + case RIGHT_LIKE: + res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.RIGHT_LIKE); + break; + default: + res = field+" = "+getFieldConditionValue(value, isString, dataBaseType); + break; + } + return res; + } + + + /** + * 获取单个查询条件的值 + * @param rule + * @param field + * @param value + * @param isString + * @return + */ + private static String getSingleSqlByRule(QueryRuleEnum rule,String field,Object value,boolean isString) { + return getSingleSqlByRule(rule, field, value, isString, null); + } + + /** + * 获取查询条件的值 + * @param value + * @param isString + * @param dataBaseType + * @return + */ + private static String getFieldConditionValue(Object value,boolean isString, String dataBaseType) { + String str = value.toString().trim(); + if(str.startsWith(SymbolConstant.EXCLAMATORY_MARK)) { + str = str.substring(1); + }else if(str.startsWith(QueryRuleEnum.GE.getValue())) { + str = str.substring(2); + }else if(str.startsWith(QueryRuleEnum.LE.getValue())) { + str = str.substring(2); + }else if(str.startsWith(QueryRuleEnum.GT.getValue())) { + str = str.substring(1); + }else if(str.startsWith(QueryRuleEnum.LT.getValue())) { + str = str.substring(1); + }else if(str.indexOf(QUERY_COMMA_ESCAPE)>0) { + str = str.replaceAll("\\+\\+", COMMA); + } + if(dataBaseType==null){ + dataBaseType = getDbType(); + } + if(isString) { + if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(dataBaseType)){ + return " N'"+str+"' "; + }else{ + return " '"+str+"' "; + } + }else { + // 如果不是字符串 有一种特殊情况 popup调用都走这个逻辑 参数传递的可能是“‘admin’”这种格式的 + if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(dataBaseType) && str.endsWith(SymbolConstant.SINGLE_QUOTATION_MARK) && str.startsWith(SymbolConstant.SINGLE_QUOTATION_MARK)){ + return " N"+str; + } + return value.toString(); + } + } + + private static String getInConditionValue(Object value,boolean isString) { + //update-begin-author:taoyan date:20210628 for: 查询条件如果输入,导致sql报错 + String[] temp = value.toString().split(","); + if(temp.length==0){ + return "('')"; + } + if(isString) { + List res = new ArrayList<>(); + for (String string : temp) { + if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){ + res.add("N'"+string+"'"); + }else{ + res.add("'"+string+"'"); + } + } + return "("+String.join("," ,res)+")"; + }else { + return "("+value.toString()+")"; + } + //update-end-author:taoyan date:20210628 for: 查询条件如果输入,导致sql报错 + } + + /** + * 先根据值判断 走左模糊还是右模糊 + * 最后如果值不带任何标识(*或者%),则再根据ruleEnum判断 + * @param value + * @param ruleEnum + * @return + */ + private static String getLikeConditionValue(Object value, QueryRuleEnum ruleEnum) { + String str = value.toString().trim(); + if(str.startsWith(SymbolConstant.ASTERISK) && str.endsWith(SymbolConstant.ASTERISK)) { + if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){ + return "N'%"+str.substring(1,str.length()-1)+"%'"; + }else{ + return "'%"+str.substring(1,str.length()-1)+"%'"; + } + }else if(str.startsWith(SymbolConstant.ASTERISK)) { + if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){ + return "N'%"+str.substring(1)+"'"; + }else{ + return "'%"+str.substring(1)+"'"; + } + }else if(str.endsWith(SymbolConstant.ASTERISK)) { + if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){ + return "N'"+str.substring(0,str.length()-1)+"%'"; + }else{ + return "'"+str.substring(0,str.length()-1)+"%'"; + } + }else { + if(str.indexOf(SymbolConstant.PERCENT_SIGN)>=0) { + if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){ + if(str.startsWith(SymbolConstant.SINGLE_QUOTATION_MARK) && str.endsWith(SymbolConstant.SINGLE_QUOTATION_MARK)){ + return "N"+str; + }else{ + return "N"+"'"+str+"'"; + } + }else{ + if(str.startsWith(SymbolConstant.SINGLE_QUOTATION_MARK) && str.endsWith(SymbolConstant.SINGLE_QUOTATION_MARK)){ + return str; + }else{ + return "'"+str+"'"; + } + } + }else { + + //update-begin-author:taoyan date:2022-6-30 for: issues/3810 数据权限规则问题 + // 走到这里说明 value不带有任何模糊查询的标识(*或者%) + if (ruleEnum == QueryRuleEnum.LEFT_LIKE) { + if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) { + return "N'%" + str + "'"; + } else { + return "'%" + str + "'"; + } + } else if (ruleEnum == QueryRuleEnum.RIGHT_LIKE) { + if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) { + return "N'" + str + "%'"; + } else { + return "'" + str + "%'"; + } + } else { + if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) { + return "N'%" + str + "%'"; + } else { + return "'%" + str + "%'"; + } + } + //update-end-author:taoyan date:2022-6-30 for: issues/3810 数据权限规则问题 + + } + } + } + + /** + * 根据权限相关配置生成相关的SQL 语句 + * @param clazz + * @return + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static String installAuthJdbc(Class clazz) { + StringBuffer sb = new StringBuffer(); + //权限查询 + Map ruleMap = getRuleMap(); + PropertyDescriptor[] origDescriptors = PropertyUtils.getPropertyDescriptors(clazz); + String sqlAnd = " and "; + for (String c : ruleMap.keySet()) { + if(oConvertUtils.isNotEmpty(c) && c.startsWith(SQL_RULES_COLUMN)){ + sb.append(sqlAnd+getSqlRuleValue(ruleMap.get(c).getRuleValue())); + } + } + String name, column; + for (int i = 0; i < origDescriptors.length; i++) { + name = origDescriptors[i].getName(); + if (judgedIsUselessField(name)) { + continue; + } + if(ruleMap.containsKey(name)) { + column = getTableFieldName(clazz, name); + if(column==null){ + continue; + } + SysPermissionDataRuleModel dataRule = ruleMap.get(name); + QueryRuleEnum rule = QueryRuleEnum.getByValue(dataRule.getRuleConditions()); + Class propType = origDescriptors[i].getPropertyType(); + boolean isString = propType.equals(String.class); + Object value; + if(isString) { + value = converRuleValue(dataRule.getRuleValue()); + }else { + value = NumberUtils.parseNumber(dataRule.getRuleValue(),propType); + } + String filedSql = getSingleSqlByRule(rule, oConvertUtils.camelToUnderline(column), value,isString); + sb.append(sqlAnd+filedSql); + } + } + log.info("query auth sql is:"+sb.toString()); + return sb.toString(); + } + + /** + * 根据权限相关配置 组装mp需要的权限 + * @param queryWrapper + * @param clazz + * @return + */ + public static void installAuthMplus(QueryWrapper queryWrapper,Class clazz) { + //权限查询 + Map ruleMap = getRuleMap(); + PropertyDescriptor[] origDescriptors = PropertyUtils.getPropertyDescriptors(clazz); + for (String c : ruleMap.keySet()) { + if(oConvertUtils.isNotEmpty(c) && c.startsWith(SQL_RULES_COLUMN)){ + queryWrapper.and(i ->i.apply(getSqlRuleValue(ruleMap.get(c).getRuleValue()))); + } + } + String name, column; + for (int i = 0; i < origDescriptors.length; i++) { + name = origDescriptors[i].getName(); + if (judgedIsUselessField(name)) { + continue; + } + column = getTableFieldName(clazz, name); + if(column==null){ + continue; + } + if(ruleMap.containsKey(name)) { + addRuleToQueryWrapper(ruleMap.get(name), column, origDescriptors[i].getPropertyType(), queryWrapper); + } + } + } + + /** + * 转换sql中的系统变量 + * @param sql + * @return + */ + public static String convertSystemVariables(String sql){ + return getSqlRuleValue(sql); + } + + /** + * 获取所有配置的权限 返回sql字符串 不受字段限制 配置什么就拿到什么 + * @return + */ + public static String getAllConfigAuth() { + StringBuffer sb = new StringBuffer(); + //权限查询 + Map ruleMap = getRuleMap(); + String sqlAnd = " and "; + for (String c : ruleMap.keySet()) { + SysPermissionDataRuleModel dataRule = ruleMap.get(c); + String ruleValue = dataRule.getRuleValue(); + if(oConvertUtils.isEmpty(ruleValue)){ + continue; + } + if(oConvertUtils.isNotEmpty(c) && c.startsWith(SQL_RULES_COLUMN)){ + sb.append(sqlAnd+getSqlRuleValue(ruleValue)); + }else{ + boolean isString = false; + ruleValue = ruleValue.trim(); + if(ruleValue.startsWith("'") && ruleValue.endsWith("'")){ + isString = true; + ruleValue = ruleValue.substring(1,ruleValue.length()-1); + } + QueryRuleEnum rule = QueryRuleEnum.getByValue(dataRule.getRuleConditions()); + String value = converRuleValue(ruleValue); + String filedSql = getSingleSqlByRule(rule, c, value,isString); + sb.append(sqlAnd+filedSql); + } + } + log.info("query auth sql is = "+sb.toString()); + return sb.toString(); + } + + + + /** + * 获取系统数据库类型 + */ + private static String getDbType(){ + return CommonUtils.getDatabaseType(); + } + + + /** + * 获取class的 包括父类的 + * @param clazz + * @return + */ + private static List getClassFields(Class clazz) { + List list = new ArrayList(); + Field[] fields; + do{ + fields = clazz.getDeclaredFields(); + for(int i = 0;i clazz, String name) { + try { + //如果字段加注解了@TableField(exist = false),不走DB查询 + Field field = null; + try { + field = clazz.getDeclaredField(name); + } catch (NoSuchFieldException e) { + //e.printStackTrace(); + } + + //如果为空,则去父类查找字段 + if (field == null) { + List allFields = getClassFields(clazz); + List searchFields = allFields.stream().filter(a -> a.getName().equals(name)).collect(Collectors.toList()); + if(searchFields!=null && searchFields.size()>0){ + field = searchFields.get(0); + } + } + + if (field != null) { + TableField tableField = field.getAnnotation(TableField.class); + if (tableField != null){ + if(tableField.exist() == false){ + //如果设置了TableField false 这个字段不需要处理 + return null; + }else{ + String column = tableField.value(); + //如果设置了TableField value 这个字段是实体字段 + if(!"".equals(column)){ + return column; + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return name; + } + + /** + * mysql 模糊查询之特殊字符下划线 (_、\) + * + * @param value: + * @Return: java.lang.String + */ + private static String specialStrConvert(String value) { + if (DataBaseConstant.DB_TYPE_MYSQL.equals(getDbType()) || DataBaseConstant.DB_TYPE_MARIADB.equals(getDbType())) { + String[] specialStr = QueryGenerator.LIKE_MYSQL_SPECIAL_STRS.split(","); + for (String str : specialStr) { + if (value.indexOf(str) !=-1) { + value = value.replace(str, "\\" + str); + } + } + } + return value; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryRuleEnum.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryRuleEnum.java new file mode 100644 index 0000000..c31bf2b --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryRuleEnum.java @@ -0,0 +1,86 @@ +package org.jeecg.common.system.query; + +import org.jeecg.common.util.oConvertUtils; + +/** + * Query 规则 常量 + * @Author Scott + * @Date 2019年02月14日 + */ +public enum QueryRuleEnum { + + /**查询规则 大于*/ + GT(">","gt","大于"), + /**查询规则 大于等于*/ + GE(">=","ge","大于等于"), + /**查询规则 小于*/ + LT("<","lt","小于"), + /**查询规则 小于等于*/ + LE("<=","le","小于等于"), + /**查询规则 等于*/ + EQ("=","eq","等于"), + /**查询规则 不等于*/ + NE("!=","ne","不等于"), + /**查询规则 包含*/ + IN("IN","in","包含"), + /**查询规则 全模糊*/ + LIKE("LIKE","like","全模糊"), + /**查询规则 左模糊*/ + LEFT_LIKE("LEFT_LIKE","left_like","左模糊"), + /**查询规则 右模糊*/ + RIGHT_LIKE("RIGHT_LIKE","right_like","右模糊"), + /**查询规则 带加号等于*/ + EQ_WITH_ADD("EQWITHADD","eq_with_add","带加号等于"), + /**查询规则 多词模糊匹配*/ + LIKE_WITH_AND("LIKEWITHAND","like_with_and","多词模糊匹配————暂时未用上"), + /**查询规则 自定义SQL片段*/ + SQL_RULES("USE_SQL_RULES","ext","自定义SQL片段"); + + private String value; + + private String condition; + + private String msg; + + QueryRuleEnum(String value, String condition, String msg){ + this.value = value; + this.condition = condition; + this.msg = msg; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public String getCondition() { + return condition; + } + + public void setCondition(String condition) { + this.condition = condition; + } + + public static QueryRuleEnum getByValue(String value){ + if(oConvertUtils.isEmpty(value)) { + return null; + } + for(QueryRuleEnum val :values()){ + if (val.getValue().equals(value) || val.getCondition().equals(value)){ + return val; + } + } + return null; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JeecgDataAutorUtils.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JeecgDataAutorUtils.java new file mode 100644 index 0000000..ed6aa3c --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JeecgDataAutorUtils.java @@ -0,0 +1,160 @@ +package org.jeecg.common.system.util; + +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.system.vo.SysPermissionDataRuleModel; +import org.jeecg.common.system.vo.SysUserCacheInfo; +import org.jeecg.common.util.SpringContextUtils; +import org.springframework.util.StringUtils; + +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +/** + * @ClassName: JeecgDataAutorUtils + * @Description: 数据权限查询规则容器工具类 + * @Author: 张代浩 + * @Date: 2012-12-15 下午11:27:39 + */ +public class JeecgDataAutorUtils { + + public static final String MENU_DATA_AUTHOR_RULES = "MENU_DATA_AUTHOR_RULES"; + + public static final String MENU_DATA_AUTHOR_RULE_SQL = "MENU_DATA_AUTHOR_RULE_SQL"; + + public static final String SYS_USER_INFO = "SYS_USER_INFO"; + + + /** + * 根据用户类型返回对应实体 + * + * @param obj + * @return + */ + public static synchronized Object initDateAutorByUserType(Object obj) { + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + + if (sysUser.getUserIdentity() != null) { + try { + if (sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_2)) { + Field field =null; + try { + field = obj.getClass().getDeclaredField("sysOrgCode"); + } catch (Exception ex) { + field = obj.getClass().getSuperclass().getDeclaredField("sysOrgCode"); + } + if (field != null) { + field.setAccessible(true); + //赋值 + field.set(obj, sysUser.getSysOrgCode()); + } + + + } else if (sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_4) || sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_5)) { + Field field =null; + + try { + field = obj.getClass().getDeclaredField("canteenId"); + } catch (Exception ex) { + field = obj.getClass().getSuperclass().getDeclaredField("canteenId"); + } + + if (field != null) { + field.setAccessible(true); + //赋值 + field.set(obj, sysUser.getCanteenId()); + } + } + } catch (Exception ex) { + + } + } + + return obj; + + } + + /** + * 往链接请求里面,传入数据查询条件 + * + * @param request + * @param dataRules + */ + public static synchronized void installDataSearchConditon(HttpServletRequest request, List dataRules) { + @SuppressWarnings("unchecked") + List list = (List) loadDataSearchConditon();// 1.先从request获取MENU_DATA_AUTHOR_RULES,如果存则获取到LIST + if (list == null) { + // 2.如果不存在,则new一个list + list = new ArrayList(); + } + for (SysPermissionDataRuleModel tsDataRule : dataRules) { + list.add(tsDataRule); + } + request.setAttribute(MENU_DATA_AUTHOR_RULES, list); // 3.往list里面增量存指 + } + + /** + * 获取请求对应的数据权限规则 + * + * @return + */ + @SuppressWarnings("unchecked") + public static synchronized List loadDataSearchConditon() { + return (List) SpringContextUtils.getHttpServletRequest().getAttribute(MENU_DATA_AUTHOR_RULES); + + } + + /** + * 获取请求对应的数据权限SQL + * + * @return + */ + public static synchronized String loadDataSearchConditonSQLString() { + return (String) SpringContextUtils.getHttpServletRequest().getAttribute(MENU_DATA_AUTHOR_RULE_SQL); + } + + /** + * 往链接请求里面,传入数据查询条件 + * + * @param request + * @param sql + */ + public static synchronized void installDataSearchConditon(HttpServletRequest request, String sql) { + String ruleSql = (String) loadDataSearchConditonSQLString(); + if (!StringUtils.hasText(ruleSql)) { + request.setAttribute(MENU_DATA_AUTHOR_RULE_SQL, sql); + } + } + + /** + * 将用户信息存到request + * + * @param request + * @param userinfo + */ + public static synchronized void installUserInfo(HttpServletRequest request, SysUserCacheInfo userinfo) { + request.setAttribute(SYS_USER_INFO, userinfo); + } + + /** + * 将用户信息存到request + * + * @param userinfo + */ + public static synchronized void installUserInfo(SysUserCacheInfo userinfo) { + SpringContextUtils.getHttpServletRequest().setAttribute(SYS_USER_INFO, userinfo); + } + + /** + * 从request获取用户信息 + * + * @return + */ + public static synchronized SysUserCacheInfo loadUserInfo() { + return (SysUserCacheInfo) SpringContextUtils.getHttpServletRequest().getAttribute(SYS_USER_INFO); + + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JwtUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JwtUtil.java new file mode 100644 index 0000000..15a749d --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JwtUtil.java @@ -0,0 +1,306 @@ +package org.jeecg.common.system.util; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTDecodeException; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Joiner; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.constant.DataBaseConstant; +import org.jeecg.common.exception.JeecgBootException; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.system.vo.SysUserCacheInfo; +import org.jeecg.common.util.DateUtils; +import org.jeecg.common.util.SpringContextUtils; +import org.jeecg.common.util.oConvertUtils; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; + +/** + * @Author Scott + * @Date 2018-07-12 14:23 + * @Desc JWT工具类 + **/ +public class JwtUtil { + + + // Token过期时间2小时(用户登录过期时间是此时间的两倍,以token在reids缓存时间为准) + public static final long EXPIRE_TIME = 2 * 60 * 60 * 1000; + + /** + * @param response + * @param code + * @param errorMsg + */ + public static void responseError(ServletResponse response, Integer code, String errorMsg) { + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + Result jsonResult = new Result(code, errorMsg); + OutputStream os = null; + try { + os = httpServletResponse.getOutputStream(); + httpServletResponse.setCharacterEncoding("UTF-8"); + httpServletResponse.setStatus(401); + os.write(new ObjectMapper().writeValueAsString(jsonResult).getBytes("UTF-8")); + os.flush(); + os.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 校验token是否正确 + * + * @param token 密钥 + * @param secret 用户的密码 + * @return 是否正确 + */ + public static boolean verify(String token, String username, String secret) { + try { + // 根据密码生成JWT效验器 + Algorithm algorithm = Algorithm.HMAC256(secret); + JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build(); + // 效验TOKEN + DecodedJWT jwt = verifier.verify(token); + return true; + } catch (Exception exception) { + return false; + } + } + + /** + * 获得token中的信息无需secret解密也能获得 + * + * @return token中包含的用户名 + */ + public static String getUserId(String token) { + try { + DecodedJWT jwt = JWT.decode(token); + return jwt.getClaim("userId").asString(); + } catch (JWTDecodeException e) { + return null; + } + } + + public static String getUserId() throws JeecgBootException { + HttpServletRequest request = + ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); + try { + String accessToken = request.getHeader("X-Access-Token"); + DecodedJWT jwt = JWT.decode(accessToken); + return jwt.getClaim("userId").asString(); + } catch (JWTDecodeException e) { + return null; + } + } + + public static String getCanteenId() throws JeecgBootException { + HttpServletRequest request = + ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); + try { + String accessToken = request.getHeader("X-Access-Token"); + DecodedJWT jwt = JWT.decode(accessToken); + return jwt.getClaim("canteenId").asString(); + } catch (JWTDecodeException e) { + return null; + } + } + + + public static String getType(String accessToken) throws JeecgBootException { + try { + DecodedJWT jwt = JWT.decode(accessToken); + return jwt.getClaim("type").asString(); + } catch (JWTDecodeException e) { + return null; + } + } + + /** + * 小程序获得签名专用 + * + * @param userId + * @param canteenId + * @param secret + * @return + */ + public static String signUserId(String userId, String canteenId, String secret,String type) { + Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); + Algorithm algorithm = Algorithm.HMAC256(secret); + // openId + return JWT.create().withClaim("userId", userId).withClaim("canteenId", canteenId).withClaim("type",type). + //withExpiresAt(date). + sign(algorithm); + + } + + /** + * 生成签名, + * + * @param userId 用户id + * @param secret 用户的密码 + * @return 加密的token + */ + public static String sign(String userId, String secret) { + Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); + Algorithm algorithm = Algorithm.HMAC256(secret); + // 附带username信息 + return JWT.create().withClaim("userId", userId).withExpiresAt(date).sign(algorithm); + + } + + + /** + * 根据request中的token获取用户账号 + * + * @param request + * @return + * @throws JeecgBootException + */ + public static String getUserIdByToken(HttpServletRequest request) throws JeecgBootException { + String accessToken = request.getHeader("X-Access-Token"); + String userId = getUserId(accessToken); + if (oConvertUtils.isEmpty(userId)) { + throw new JeecgBootException("未获取到用户"); + } + return userId; + } + + /** + * 从session中获取变量 + * + * @param key + * @return + */ + public static String getSessionData(String key) { + //${myVar}% + //得到${} 后面的值 + String moshi = ""; + if (key.indexOf("}") != -1) { + moshi = key.substring(key.indexOf("}") + 1); + } + String returnValue = null; + if (key.contains("#{")) { + key = key.substring(2, key.indexOf("}")); + } + if (oConvertUtils.isNotEmpty(key)) { + HttpSession session = SpringContextUtils.getHttpServletRequest().getSession(); + returnValue = (String) session.getAttribute(key); + } + //结果加上${} 后面的值 + if (returnValue != null) { + returnValue = returnValue + moshi; + } + return returnValue; + } + + /** + * 从当前用户中获取变量 + * + * @param ruleValue + * @param user + * @return + */ + //硬编码加密密钥-中危.(命名的关键字key被扫描到误报,修改命名ruleValue) +// public static String getUserSystemData(String key, SysUserCacheInfo user) { + public static String getUserSystemData(String ruleValue, SysUserCacheInfo user) { + if (user == null) { + user = JeecgDataAutorUtils.loadUserInfo(); + } + //#{sys_user_code}% + + // 获取登录用户信息 + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + + String moshi = ""; + if (ruleValue.indexOf("}") != -1) { + moshi = ruleValue.substring(ruleValue.indexOf("}") + 1); + } + String returnValue = null; + //针对特殊标示处理#{sysOrgCode},判断替换 + if (ruleValue.contains("#{")) { + ruleValue = ruleValue.substring(2, ruleValue.indexOf("}")); + } else { + ruleValue = ruleValue; + } + //替换为系统登录用户帐号 + if (ruleValue.equals(DataBaseConstant.SYS_USER_CODE) || ruleValue.toLowerCase().equals(DataBaseConstant.SYS_USER_CODE_TABLE)) { + if (user == null) { + returnValue = sysUser.getUsername(); + } else { + returnValue = user.getSysUserCode(); + } + } + //替换为系统登录用户真实名字 + else if (ruleValue.equals(DataBaseConstant.SYS_USER_NAME) || ruleValue.toLowerCase().equals(DataBaseConstant.SYS_USER_NAME_TABLE)) { + if (user == null) { + returnValue = sysUser.getRealname(); + } else { + returnValue = user.getSysUserName(); + } + } + + //替换为系统用户登录所使用的机构编码 + else if (ruleValue.equals(DataBaseConstant.SYS_ORG_CODE) || ruleValue.toLowerCase().equals(DataBaseConstant.SYS_ORG_CODE_TABLE)) { + if (user == null) { + returnValue = sysUser.getSysOrgCode(); + } else { + returnValue = user.getSysOrgCode(); + } + } + //替换为系统用户所拥有的所有机构编码 + else if (ruleValue.equals(DataBaseConstant.SYS_MULTI_ORG_CODE) || ruleValue.toLowerCase().equals(DataBaseConstant.SYS_MULTI_ORG_CODE_TABLE)) { + if (user == null) { + //TODO 暂时使用用户登录部门,存在逻辑缺陷,不是用户所拥有的部门 + returnValue = sysUser.getSysOrgCode(); + } else { + if (user.isOneDepart()) { + returnValue = user.getSysMultiOrgCode().get(0); + } else { + returnValue = Joiner.on(",").join(user.getSysMultiOrgCode()); + } + } + } + //替换为当前系统时间(年月日) + else if (ruleValue.equals(DataBaseConstant.SYS_DATE) || ruleValue.toLowerCase().equals(DataBaseConstant.SYS_DATE_TABLE)) { + returnValue = DateUtils.formatDate(); + } + //替换为当前系统时间(年月日时分秒) + else if (ruleValue.equals(DataBaseConstant.SYS_TIME) || ruleValue.toLowerCase().equals(DataBaseConstant.SYS_TIME_TABLE)) { + returnValue = DateUtils.now(); + } + //流程状态默认值(默认未发起) + else if (ruleValue.equals(DataBaseConstant.BPM_STATUS) || ruleValue.toLowerCase().equals(DataBaseConstant.BPM_STATUS_TABLE)) { + returnValue = "1"; + } + //update-begin-author:taoyan date:20210330 for:多租户ID作为系统变量 + else if (ruleValue.equals(DataBaseConstant.TENANT_ID) || ruleValue.toLowerCase().equals(DataBaseConstant.TENANT_ID_TABLE)) { + returnValue = sysUser.getRelTenantIds(); + if (oConvertUtils.isEmpty(returnValue) || (returnValue != null && returnValue.indexOf(",") > 0)) { + returnValue = SpringContextUtils.getHttpServletRequest().getHeader(CommonConstant.TENANT_ID); + } + } + //update-end-author:taoyan date:20210330 for:多租户ID作为系统变量 + if (returnValue != null) { + returnValue = returnValue + moshi; + } + return returnValue; + } + + public static void main(String[] args) { + String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTgzOTc0NjAsInVzZXJuYW1lIjoic3lzdGVtIn0.ABrRxlh-wkuDppbhiwmkobN2YSUhu4GMHtyW6bPQCtQ"; + System.out.println(JwtUtil.getUserId(token)); + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/ResourceUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/ResourceUtil.java new file mode 100644 index 0000000..d3260c6 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/ResourceUtil.java @@ -0,0 +1,111 @@ +package org.jeecg.common.system.util; + +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.system.annotation.EnumDict; +import org.jeecg.common.system.vo.DictModel; +import org.jeecg.common.util.oConvertUtils; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.util.ClassUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 资源加载工具类 + * @Author taoYan + * @Date 2022/7/8 10:40 + **/ +@Slf4j +public class ResourceUtil { + + + /** + * 枚举字典数据 + */ + private final static Map> enumDictData = new HashMap<>(5); + + /** + * 所有java类 + */ + private final static String CLASS_PATTERN="/**/*.class"; + + /** + * 包路径 org.jeecg + */ + private final static String BASE_PACKAGE = "org.jeecg"; + + /** + * 枚举类中获取字典数据的方法名 + */ + private final static String METHOD_NAME = "getDictList"; + + /** + * 获取枚举类对应的字典数据 SysDictServiceImpl#queryAllDictItems() + * @return + */ + public static Map> getEnumDictData(){ + if(enumDictData.keySet().size()>0){ + return enumDictData; + } + ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); + String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(BASE_PACKAGE) + CLASS_PATTERN; + try { + Resource[] resources = resourcePatternResolver.getResources(pattern); + MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver); + for (Resource resource : resources) { + MetadataReader reader = readerFactory.getMetadataReader(resource); + String classname = reader.getClassMetadata().getClassName(); + Class clazz = Class.forName(classname); + EnumDict enumDict = clazz.getAnnotation(EnumDict.class); + if (enumDict != null) { + EnumDict annotation = clazz.getAnnotation(EnumDict.class); + String key = annotation.value(); + if(oConvertUtils.isNotEmpty(key)){ + List list = (List) clazz.getDeclaredMethod(METHOD_NAME).invoke(null); + enumDictData.put(key, list); + } + } + } + }catch (Exception e){ + log.error("获取枚举类字典数据异常", e.getMessage()); + // e.printStackTrace(); + } + return enumDictData; + } + + /** + * 用于后端字典翻译 SysDictServiceImpl#queryManyDictByKeys(java.util.List, java.util.List) + * @param dictCodeList + * @param keys + * @return + */ + public static Map> queryManyDictByKeys(List dictCodeList, List keys){ + if(enumDictData.keySet().size()==0){ + getEnumDictData(); + } + Map> map = new HashMap<>(); + for (String code : enumDictData.keySet()) { + if(dictCodeList.indexOf(code)>=0){ + List dictItemList = enumDictData.get(code); + for(DictModel dm: dictItemList){ + String value = dm.getValue(); + if(keys.indexOf(value)>=0){ + List list = new ArrayList<>(); + list.add(new DictModel(value, dm.getText())); + map.put(code,list); + break; + } + } + } + } + return map; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/ComboModel.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/ComboModel.java new file mode 100644 index 0000000..663cdb9 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/ComboModel.java @@ -0,0 +1,40 @@ +package org.jeecg.common.system.vo; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * @Description: 文档管理 + * @author: jeecg-boot + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@JsonIgnoreProperties(ignoreUnknown = true) +public class ComboModel implements Serializable { + private String id; + private String title; + /**文档管理 表单table默认选中*/ + private boolean checked; + /**文档管理 表单table 用户账号*/ + private String username; + /**文档管理 表单table 用户邮箱*/ + private String email; + /**文档管理 表单table 角色编码*/ + private String roleCode; + + public ComboModel(){ + + }; + + public ComboModel(String id,String title,boolean checked,String username){ + this.id = id; + this.title = title; + this.checked = false; + this.username = username; + }; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DictModel.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DictModel.java new file mode 100644 index 0000000..b407146 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DictModel.java @@ -0,0 +1,53 @@ +package org.jeecg.common.system.vo; + +import java.io.Serializable; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * @Description: 字典类 + * @author: jeecg-boot + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@JsonIgnoreProperties(ignoreUnknown = true) +public class DictModel implements Serializable{ + private static final long serialVersionUID = 1L; + + public DictModel() { + } + + public DictModel(String value, String text) { + this.value = value; + this.text = text; + } + + /** + * 字典value + */ + private String value; + /** + * 字典文本 + */ + private String text; + + /** + * 特殊用途: JgEditableTable + * @return + */ + public String getTitle() { + return this.text; + } + /** + * 特殊用途: vue3 Select组件 + */ + public String getLabel() { + return this.text; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DictModelMany.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DictModelMany.java new file mode 100644 index 0000000..cc6a34f --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DictModelMany.java @@ -0,0 +1,19 @@ +package org.jeecg.common.system.vo; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 查询多个字典时用到 + * @author: jeecg-boot + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class DictModelMany extends DictModel { + + /** + * 字典code,根据多个字段code查询时才用到,用于区分不同的字典选项 + */ + private String dictCode; + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DictQuery.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DictQuery.java new file mode 100644 index 0000000..b24ede8 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DictQuery.java @@ -0,0 +1,35 @@ +package org.jeecg.common.system.vo; + +import lombok.Data; + +/** + * 字典查询参数实体 + * @author: jeecg-boot + */ +@Data +public class DictQuery { + /** + * 表名 + */ + private String table; + /** + * 存储列 + */ + private String code; + + /** + * 显示列 + */ + private String text; + + /** + * 关键字查询 + */ + private String keyword; + + /** + * 存储列的值 用于回显查询 + */ + private String codeValue; + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DynamicDataSourceModel.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DynamicDataSourceModel.java new file mode 100644 index 0000000..303ab17 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DynamicDataSourceModel.java @@ -0,0 +1,58 @@ +package org.jeecg.common.system.vo; + +import lombok.Data; +import org.springframework.beans.BeanUtils; + +/** + * @Description: 数据源 + * @author: jeecg-boot + */ +@Data +public class DynamicDataSourceModel { + + public DynamicDataSourceModel() { + + } + + public DynamicDataSourceModel(Object dbSource) { + if (dbSource != null) { + BeanUtils.copyProperties(dbSource, this); + } + } + + /** + * id + */ + private java.lang.String id; + /** + * 数据源编码 + */ + private java.lang.String code; + /** + * 数据库类型 + */ + private java.lang.String dbType; + /** + * 驱动类 + */ + private java.lang.String dbDriver; + /** + * 数据源地址 + */ + private java.lang.String dbUrl; + +// /** +// * 数据库名称 +// */ +// private java.lang.String dbName; + + /** + * 用户名 + */ + private java.lang.String dbUsername; + /** + * 密码 + */ + private java.lang.String dbPassword; + +} \ No newline at end of file diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/LoginUser.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/LoginUser.java new file mode 100644 index 0000000..7c208ad --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/LoginUser.java @@ -0,0 +1,129 @@ +package org.jeecg.common.system.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; +import java.util.List; + +/** + *

+ * 在线用户信息 + *

+ * + * @Author scott + * @since 2018-12-20 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +public class LoginUser { + + /** + * 登录人id + */ + private String id; + + /** + * 登录人账号 + */ + private String username; + + /** + * 登录人名字 + */ + private String realname; + + /** + * 登录人密码 + */ + private String password; + + /** + * 当前登录部门code + */ + private String orgCode; + + /** + * 当前登录部门code + */ + private String sysOrgCode; + /** + * 头像 + */ + private String avatar; + + /** + * 生日 + */ + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date birthday; + + /** + * 性别(1:男 2:女) + */ + private Integer sex; + + /** + * 电子邮件 + */ + private String email; + + /** + * 电话 + */ + private String phone; + + /** + * 状态(1:正常 2:冻结 ) + */ + private Integer status; + + private Integer delFlag; + /** + * 同步工作流引擎1同步0不同步 + */ + private Integer activitiSync; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 用户类型 (0:超级管理员(系统内置-内部使用) 1:admin 2:二级管理员 3:平台用户 4:食堂管理员 5:食堂职工 6:员工(就餐人员)) + */ + private Integer userIdentity; + + /** + * 管理部门ids + */ + private String departIds; + + /** + * 职务,关联职务表 + */ + private String post; + + /** + * 座机号 + */ + private String telephone; + + /**多租户id配置,编辑用户的时候设置*/ + private String relTenantIds; + + /**设备id uniapp推送用*/ + private String clientId; + + /**食堂id*/ + private String canteenId; + + /**用户管理食堂*/ + private List canteenIdList; + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/ScreenVo.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/ScreenVo.java new file mode 100644 index 0000000..339d120 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/ScreenVo.java @@ -0,0 +1,24 @@ +package org.jeecg.common.system.vo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +public class ScreenVo { + @Schema(title = "食堂id") + private String id; + + @Schema(title = "食堂名称") + private String restName; + + public ScreenVo(String id) { + this.id = id; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SelectTreeModel.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SelectTreeModel.java new file mode 100644 index 0000000..5201267 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SelectTreeModel.java @@ -0,0 +1,32 @@ +package org.jeecg.common.system.vo; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 下拉树 model + * + * @author jeecg-boot + */ +@Data +public class SelectTreeModel implements Serializable { + + private String key; + private String title; + private String value; + /** + * 父Id + */ + private String parentId; + /** + * 是否是叶节点 + */ + private boolean isLeaf; + /** + * 子节点 + */ + private List children; + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysCategoryModel.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysCategoryModel.java new file mode 100644 index 0000000..5499bef --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysCategoryModel.java @@ -0,0 +1,52 @@ +package org.jeecg.common.system.vo; + +import org.jeecgframework.poi.excel.annotation.Excel; + +/** + * @Author qinfeng + * @Date 2020/2/19 12:01 + * @Description: + * @Version 1.0 + */ +public class SysCategoryModel { + /**主键*/ + private java.lang.String id; + /**父级节点*/ + private java.lang.String pid; + /**类型名称*/ + private java.lang.String name; + /**类型编码*/ + private java.lang.String code; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getPid() { + return pid; + } + + public void setPid(String pid) { + this.pid = pid; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysDepartModel.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysDepartModel.java new file mode 100644 index 0000000..41dca9d --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysDepartModel.java @@ -0,0 +1,148 @@ +package org.jeecg.common.system.vo; + +/** + * 部门机构model + * @author: lvdandan + */ +public class SysDepartModel { + /**ID*/ + private String id; + /**父机构ID*/ + private String parentId; + /**机构/部门名称*/ + private String departName; + /**英文名*/ + private String departNameEn; + /**缩写*/ + private String departNameAbbr; + /**排序*/ + private Integer departOrder; + /**描述*/ + private String description; + /**机构类别 1组织机构,2岗位*/ + private String orgCategory; + /**机构类型*/ + private String orgType; + /**机构编码*/ + private String orgCode; + /**手机号*/ + private String mobile; + /**传真*/ + private String fax; + /**地址*/ + private String address; + /**备注*/ + private String memo; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public String getDepartName() { + return departName; + } + + public void setDepartName(String departName) { + this.departName = departName; + } + + public String getDepartNameEn() { + return departNameEn; + } + + public void setDepartNameEn(String departNameEn) { + this.departNameEn = departNameEn; + } + + public String getDepartNameAbbr() { + return departNameAbbr; + } + + public void setDepartNameAbbr(String departNameAbbr) { + this.departNameAbbr = departNameAbbr; + } + + public Integer getDepartOrder() { + return departOrder; + } + + public void setDepartOrder(Integer departOrder) { + this.departOrder = departOrder; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getOrgCategory() { + return orgCategory; + } + + public void setOrgCategory(String orgCategory) { + this.orgCategory = orgCategory; + } + + public String getOrgType() { + return orgType; + } + + public void setOrgType(String orgType) { + this.orgType = orgType; + } + + public String getOrgCode() { + return orgCode; + } + + public void setOrgCode(String orgCode) { + this.orgCode = orgCode; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public String getFax() { + return fax; + } + + public void setFax(String fax) { + this.fax = fax; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getMemo() { + return memo; + } + + public void setMemo(String memo) { + this.memo = memo; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysPermissionDataRuleModel.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysPermissionDataRuleModel.java new file mode 100644 index 0000000..f9204d6 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysPermissionDataRuleModel.java @@ -0,0 +1,151 @@ +package org.jeecg.common.system.vo; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 菜单权限规则表 + *

+ * + * @Author huangzhilin + * @since 2019-03-29 + */ +public class SysPermissionDataRuleModel { + + /** + * id + */ + private String id; + + /** + * 对应的菜单id + */ + private String permissionId; + + /** + * 规则名称 + */ + private String ruleName; + + /** + * 字段 + */ + private String ruleColumn; + + /** + * 条件 + */ + private String ruleConditions; + + /** + * 规则值 + */ + private String ruleValue; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 创建人 + */ + private String createBy; + + /** + * 修改时间 + */ + private Date updateTime; + + /** + * 修改人 + */ + private String updateBy; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getPermissionId() { + return permissionId; + } + + public void setPermissionId(String permissionId) { + this.permissionId = permissionId; + } + + public String getRuleName() { + return ruleName; + } + + public void setRuleName(String ruleName) { + this.ruleName = ruleName; + } + + public String getRuleColumn() { + return ruleColumn; + } + + public void setRuleColumn(String ruleColumn) { + this.ruleColumn = ruleColumn; + } + + public String getRuleConditions() { + return ruleConditions; + } + + public void setRuleConditions(String ruleConditions) { + this.ruleConditions = ruleConditions; + } + + public String getRuleValue() { + return ruleValue; + } + + public void setRuleValue(String ruleValue) { + this.ruleValue = ruleValue; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getCreateBy() { + return createBy; + } + + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public String getUpdateBy() { + return updateBy; + } + + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysUserCacheInfo.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysUserCacheInfo.java new file mode 100644 index 0000000..a6e117a --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/SysUserCacheInfo.java @@ -0,0 +1,71 @@ +package org.jeecg.common.system.vo; + +import java.util.List; + +import org.jeecg.common.util.DateUtils; + +/** + * @Description: 用户缓存信息 + * @author: jeecg-boot + */ +public class SysUserCacheInfo { + + private String sysUserCode; + + private String sysUserName; + + private String sysOrgCode; + + private List sysMultiOrgCode; + + private boolean oneDepart; + + public boolean isOneDepart() { + return oneDepart; + } + + public void setOneDepart(boolean oneDepart) { + this.oneDepart = oneDepart; + } + + public String getSysDate() { + return DateUtils.formatDate(); + } + + public String getSysTime() { + return DateUtils.now(); + } + + public String getSysUserCode() { + return sysUserCode; + } + + public void setSysUserCode(String sysUserCode) { + this.sysUserCode = sysUserCode; + } + + public String getSysUserName() { + return sysUserName; + } + + public void setSysUserName(String sysUserName) { + this.sysUserName = sysUserName; + } + + public String getSysOrgCode() { + return sysOrgCode; + } + + public void setSysOrgCode(String sysOrgCode) { + this.sysOrgCode = sysOrgCode; + } + + public List getSysMultiOrgCode() { + return sysMultiOrgCode; + } + + public void setSysMultiOrgCode(List sysMultiOrgCode) { + this.sysMultiOrgCode = sysMultiOrgCode; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/Base64Util.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/Base64Util.java new file mode 100644 index 0000000..6068262 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/Base64Util.java @@ -0,0 +1,182 @@ + +package org.jeecg.common.util; + +import org.springframework.util.StringUtils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Base64; + +/** + * Base64工具 + */ +public class Base64Util extends org.springframework.util.Base64Utils { + + /** + * 编码 + * + * @param value 字符串 + * @return {String} + */ + public static String encode(String value) { + return Base64Util.encode(value, Charsets.UTF_8); + } + + /** + * 编码 + * + * @param value 字符串 + * @param charset 字符集 + * @return {String} + */ + public static String encode(String value, java.nio.charset.Charset charset) { + byte[] val = value.getBytes(charset); + return new String(Base64Util.encode(val), charset); + } + + /** + * 编码URL安全 + * + * @param value 字符串 + * @return {String} + */ + public static String encodeUrlSafe(String value) { + return Base64Util.encodeUrlSafe(value, Charsets.UTF_8); + } + + /** + * 编码URL安全 + * + * @param value 字符串 + * @param charset 字符集 + * @return {String} + */ + public static String encodeUrlSafe(String value, java.nio.charset.Charset charset) { + byte[] val = value.getBytes(charset); + return new String(Base64Util.encodeUrlSafe(val), charset); + } + + /** + * 解码 + * + * @param value 字符串 + * @return {String} + */ + public static String decode(String value) { + return Base64Util.decode(value, Charsets.UTF_8); + } + + /** + * 解码 + * + * @param value 字符串 + * @param charset 字符集 + * @return {String} + */ + public static String decode(String value, java.nio.charset.Charset charset) { + byte[] val = value.getBytes(charset); + byte[] decodedValue = Base64Util.decode(val); + return new String(decodedValue, charset); + } + + /** + * 解码URL安全 + * + * @param value 字符串 + * @return {String} + */ + public static String decodeUrlSafe(String value) { + return Base64Util.decodeUrlSafe(value, Charsets.UTF_8); + } + + /** + * 解码URL安全 + * + * @param value 字符串 + * @param charset 字符集 + * @return {String} + */ + public static String decodeUrlSafe(String value, java.nio.charset.Charset charset) { + byte[] val = value.getBytes(charset); + byte[] decodedValue = Base64Util.decodeUrlSafe(val); + return new String(decodedValue, charset); + } + + public static String base64Process(String base64Str) { + if (!StringUtils.isEmpty(base64Str)) { + String photoBase64 = base64Str.substring(0, 30).toLowerCase(); + int indexOf = photoBase64.indexOf("base64,"); + if (indexOf > 0) { + base64Str = base64Str.substring(indexOf + 7); + } + base64Str = base64Str.replaceAll(" ", "+"); + base64Str = base64Str.replaceAll("\r|\n", ""); + return base64Str; + } + return ""; + } + + public static byte[] base64ToBytes(String base64) { + if (StringUtils.isEmpty(base64)) { + return null; + } + String base64Process = base64Process(base64); + + byte[] decode = Base64.getDecoder().decode(base64Process); + return decode; + + + } + //资源未释放:流 - 质量问题 -中危 +// public static String imageToBase64(String img){ +// InputStream in = null; +// byte[] date = null; +// try{ +// in = new FileInputStream(img); +// date = new byte[in.available()]; +// in.read(); +// in.close(); +// }catch (Exception e){ +// e.printStackTrace(); +// } +// return Base64.getEncoder().encodeToString(date); +// } + + /** + * + * @param img + * @return + */ + public static String netImageToBase64(String img){ + ByteArrayOutputStream date = new ByteArrayOutputStream(); + InputStream is = null; + try{ + URL url = new URL(img); + byte[] by = new byte[1024]; + HttpURLConnection connection = (HttpURLConnection)url.openConnection(); + connection.setRequestMethod("GET"); + connection.setConnectTimeout(5000); + is = connection.getInputStream(); + int len = -1; + while ((len = is.read(by))!=-1){ + date.write(by,0,len); + } + is.close(); + }catch (Exception e){ + + } finally{ + //资源未释放:流 - 质量问题 -中危 + if(is!=null){ + try { + is.close(); + } catch (IOException e) { + System.out.println("关闭流异常【】"); + } + } + } + return Base64.getEncoder().encodeToString(date.toByteArray()); + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/BigDecimalSerialize.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/BigDecimalSerialize.java new file mode 100644 index 0000000..943730f --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/BigDecimalSerialize.java @@ -0,0 +1,23 @@ +package org.jeecg.common.util; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; +import java.math.BigDecimal; + +/** + * @author Ls. + * @date 2021/7/22. + */ +public class BigDecimalSerialize extends JsonSerializer { + @Override + public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException { + if (value != null && !"".equals(value)) { + gen.writeString(value.setScale(2, BigDecimal.ROUND_HALF_DOWN) + ""); + } else { + gen.writeString(value + ""); + } + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/BrowserType.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/BrowserType.java new file mode 100644 index 0000000..c080c61 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/BrowserType.java @@ -0,0 +1,13 @@ +package org.jeecg.common.util; + +/** + * + * @Author 张代浩 + * + */ +public enum BrowserType { + /** + * 浏览类型 IE11,IE10,IE9,IE8,IE7,IE6,Firefox,Safari,Chrome,Opera,Camino,Gecko + */ + IE11,IE10,IE9,IE8,IE7,IE6,Firefox,Safari,Chrome,Opera,Camino,Gecko +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/BrowserUtils.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/BrowserUtils.java new file mode 100644 index 0000000..08b3fbc --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/BrowserUtils.java @@ -0,0 +1,212 @@ +package org.jeecg.common.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.servlet.http.HttpServletRequest; + +/** + * + * @Author 张代浩 + * + */ +public class BrowserUtils { + + /** + * 判断是否是IE + * @param request + * @return + */ + public static boolean isIe(HttpServletRequest request) { + return (request.getHeader("USER-AGENT").toLowerCase().indexOf("msie") > 0 || request + .getHeader("USER-AGENT").toLowerCase().indexOf("rv:11.0") > 0) ? true + : false; + } + + /** + * 获取IE版本 + * + * @param request + * @return + */ + public static Double getIeVersion(HttpServletRequest request) { + Double version = 0.0; + if (getBrowserType(request, IE11)) { + version = 11.0; + } else if (getBrowserType(request, IE10)) { + version = 10.0; + } else if (getBrowserType(request, IE9)) { + version = 9.0; + } else if (getBrowserType(request, IE8)) { + version = 8.0; + } else if (getBrowserType(request, IE7)) { + version = 7.0; + } else if (getBrowserType(request, IE6)) { + version = 6.0; + } + return version; + } + + /** + * 获取浏览器类型 + * + * @param request + * @return + */ + public static BrowserType getBrowserType(HttpServletRequest request) { + BrowserType browserType = null; + if (getBrowserType(request, IE11)) { + browserType = BrowserType.IE11; + } + if (getBrowserType(request, IE10)) { + browserType = BrowserType.IE10; + } + if (getBrowserType(request, IE9)) { + browserType = BrowserType.IE9; + } + if (getBrowserType(request, IE8)) { + browserType = BrowserType.IE8; + } + if (getBrowserType(request, IE7)) { + browserType = BrowserType.IE7; + } + if (getBrowserType(request, IE6)) { + browserType = BrowserType.IE6; + } + if (getBrowserType(request, FIREFOX)) { + browserType = BrowserType.Firefox; + } + if (getBrowserType(request, SAFARI)) { + browserType = BrowserType.Safari; + } + if (getBrowserType(request, CHROME)) { + browserType = BrowserType.Chrome; + } + if (getBrowserType(request, OPERA)) { + browserType = BrowserType.Opera; + } + if (getBrowserType(request, CAMINO)) { + browserType = BrowserType.Camino; + } + return browserType; + } + + private static boolean getBrowserType(HttpServletRequest request, + String brosertype) { + return request.getHeader("USER-AGENT").toLowerCase() + .indexOf(brosertype) > 0 ? true : false; + } + + private final static String IE11 = "rv:11.0"; + private final static String IE10 = "MSIE 10.0"; + private final static String IE9 = "MSIE 9.0"; + private final static String IE8 = "MSIE 8.0"; + private final static String IE7 = "MSIE 7.0"; + private final static String IE6 = "MSIE 6.0"; + private final static String MAXTHON = "Maxthon"; + private final static String QQ = "QQBrowser"; + private final static String GREEN = "GreenBrowser"; + private final static String SE360 = "360SE"; + private final static String FIREFOX = "Firefox"; + private final static String OPERA = "Opera"; + private final static String CHROME = "Chrome"; + private final static String SAFARI = "Safari"; + private final static String OTHER = "其它"; + private final static String CAMINO = "Camino"; + + public static String checkBrowse(HttpServletRequest request) { + String userAgent = request.getHeader("USER-AGENT"); + if (regex(OPERA, userAgent)) { + return OPERA; + } + if (regex(CHROME, userAgent)) { + return CHROME; + } + if (regex(FIREFOX, userAgent)) { + return FIREFOX; + } + if (regex(SAFARI, userAgent)) { + return SAFARI; + } + if (regex(SE360, userAgent)) { + return SE360; + } + if (regex(GREEN, userAgent)) { + return GREEN; + } + if (regex(QQ, userAgent)) { + return QQ; + } + if (regex(MAXTHON, userAgent)) { + return MAXTHON; + } + if (regex(IE11, userAgent)) { + return IE11; + } + if (regex(IE10, userAgent)) { + return IE10; + } + if (regex(IE9, userAgent)) { + return IE9; + } + if (regex(IE8, userAgent)) { + return IE8; + } + if (regex(IE7, userAgent)) { + return IE7; + } + if (regex(IE6, userAgent)) { + return IE6; + } + return OTHER; + } + + public static boolean regex(String regex, String str) { + Pattern p = Pattern.compile(regex, Pattern.MULTILINE); + Matcher m = p.matcher(str); + return m.find(); + } + + + private static Map langMap = new HashMap(); + private final static String ZH = "zh"; + private final static String ZH_CN = "zh-cn"; + + private final static String EN = "en"; + private final static String EN_US = "en"; + + + static + { + langMap.put(ZH, ZH_CN); + langMap.put(EN, EN_US); + } + + public static String getBrowserLanguage(HttpServletRequest request) { + + String browserLang = request.getLocale().getLanguage(); + String browserLangCode = (String)langMap.get(browserLang); + + if(browserLangCode == null) + { + browserLangCode = EN_US; + } + return browserLangCode; + } + + /** 判断请求是否来自电脑端 */ + public static boolean isDesktop(HttpServletRequest request) { + return !isMobile(request); + } + + /** 判断请求是否来自移动端 */ + public static boolean isMobile(HttpServletRequest request) { + String ua = request.getHeader("User-Agent").toLowerCase(); + String type = "(phone|pad|pod|iphone|ipod|ios|ipad|android|mobile|blackberry|iemobile|mqqbrowser|juc|fennec|wosbrowser|browserng|webos|symbian|windows phone)"; + Pattern pattern = Pattern.compile(type); + return pattern.matcher(ua).find(); + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/Charsets.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/Charsets.java new file mode 100644 index 0000000..61a7caf --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/Charsets.java @@ -0,0 +1,45 @@ + +package org.jeecg.common.util; + + +import org.apache.commons.lang3.StringUtils; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.charset.UnsupportedCharsetException; + +/** + * 字符集工具类 + */ +public class Charsets { + + /** + * 字符集ISO-8859-1 + */ + public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1; + public static final String ISO_8859_1_NAME = ISO_8859_1.name(); + + /** + * 字符集GBK + */ + public static final Charset GBK = Charset.forName(StringPool.GBK); + public static final String GBK_NAME = GBK.name(); + + /** + * 字符集utf-8 + */ + public static final Charset UTF_8 = StandardCharsets.UTF_8; + public static final String UTF_8_NAME = UTF_8.name(); + + /** + * 转换为Charset对象 + * + * @param charsetName 字符集,为空则返回默认字符集 + * @return Charsets + * @throws UnsupportedCharsetException 编码不支持 + */ + public static Charset charset(String charsetName) throws UnsupportedCharsetException { + return StringUtils.isBlank(charsetName) ? Charset.defaultCharset() : Charset.forName(charsetName); + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/CommonUtils.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/CommonUtils.java new file mode 100644 index 0000000..cf3bce9 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/CommonUtils.java @@ -0,0 +1,422 @@ +package org.jeecg.common.util; + +import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty; +import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties; +import com.baomidou.mybatisplus.annotation.DbType; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FilenameUtils; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.constant.DataBaseConstant; +import org.jeecg.common.util.filter.FileTypeFilter; +import org.jeecg.common.util.oss.OssBootUtil; +import org.jeecgframework.poi.util.PoiPublicUtil; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.stereotype.Component; +import org.springframework.util.FileCopyUtils; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.sql.DataSource; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Slf4j +@Component +public class CommonUtils { + + private static String uploadType; + + //中文正则 + private static Pattern ZHONGWEN_PATTERN = Pattern.compile("[\u4e00-\u9fa5]"); + + public static String uploadOnlineImage(byte[] data,String basePath,String bizPath,String uploadType){ + String dbPath = null; + String fileName = null; + try { + //不安全的随机数-中危 + SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); + fileName = "image" + Math.round(random.nextDouble() * 100000000000L); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + fileName += "." + PoiPublicUtil.getFileExtendName(data); + try { + if(CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)){ + String path = basePath + File.separator + bizPath + File.separator; +// File file = new File(basePath + File.separator + bizPath + File.separator ); + //解决路径遍历漏洞-路径 -高危 + String filePath = FilenameUtils.getFullPath(path); + File file = new File(XssUtils.checkTraversal(filePath)); + + if (!file.exists()) { + file.mkdirs();// 创建文件根目录 + } + String savePath = file.getPath() + File.separator + fileName; + File savefile = new File(savePath); + FileCopyUtils.copy(data, savefile); + dbPath = bizPath + File.separator + fileName; + }else { + InputStream in = new ByteArrayInputStream(data); + String relativePath = bizPath+"/"+fileName; + if(CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)){ + dbPath = MinioUtil.upload(in,relativePath); + }if(CommonConstant.UPLOAD_MY_UPLOAD.equals(uploadType)){ + dbPath = MyUploadUtil.upload(in,bizPath,fileName); + }else if(CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)){ + dbPath = OssBootUtil.upload(in,relativePath); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return dbPath; + } + + /** + * 判断文件名是否带盘符,重新处理 + * @param fileName + * @return + */ + public static String getFileName(String fileName){ + //判断是否带有盘符信息 + // Check for Unix-style path + int unixSep = fileName.lastIndexOf('/'); + // Check for Windows-style path + int winSep = fileName.lastIndexOf('\\'); + // Cut off at latest possible point + int pos = (winSep > unixSep ? winSep : unixSep); + if (pos != -1) { + // Any sort of path separator found... + fileName = fileName.substring(pos + 1); + } + //替换上传文件名字的特殊字符 + fileName = fileName.replace("=","").replace(",","").replace("&","") + .replace("#", "").replace("“", "").replace("”", ""); + //替换上传文件名字中的空格 + fileName=fileName.replaceAll("\\s",""); + return fileName; + } + + // java 判断字符串里是否包含中文字符 + public static boolean ifContainChinese(String str) { + if(str.getBytes().length == str.length()){ + return false; + }else{ + Matcher m = ZHONGWEN_PATTERN.matcher(str); + if (m.find()) { + return true; + } + return false; + } + } + + /** + * 统一全局上传 + * @Return: java.lang.String + */ + public static String upload(MultipartFile file, String bizPath, String uploadType) { + String url = ""; + if(CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)){ + url = MinioUtil.upload(file,bizPath); + }else if(CommonConstant.UPLOAD_MY_UPLOAD.equals(uploadType)){ + url = MyUploadUtil.upload(file,bizPath); + }else{ + url = OssBootUtil.upload(file,bizPath); + } + return url; + } + + /** + * 统一全局上传 + * @Return: java.lang.String + */ + public static String upload(InputStream inputStream,String bizPath,String fileName){ + String url = ""; + try { + if(CommonConstant.UPLOAD_MY_UPLOAD.equals(uploadType)){ + url = MyUploadUtil.upload(inputStream,bizPath,fileName); + }else if(CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)){ + url = MinioUtil.upload(inputStream,bizPath+File.separator+fileName); + } + } catch (Exception e) { + e.printStackTrace(); + log.error("上传minio失败"); + } + return url; + } + + /** + * 统一全局上传 带是否需要上传缩略图 + * @Return: java.lang.String + */ + public static String upload(MultipartFile file, String bizPath, String uploadType, Boolean needThumbnailFlag) { + String url = ""; + if(CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)){ + url = MinioUtil.upload(file, bizPath); + }else if(CommonConstant.UPLOAD_MY_UPLOAD.equals(uploadType)){ + url = MyUploadUtil.upload(file, bizPath, needThumbnailFlag); + }else{ + url = OssBootUtil.upload(file, bizPath); + } + return url; + } + /** + * 上传文件 固定目录 带缩略图(同名覆盖) + * @Return: java.lang.String + */ + public static String uploadFixedDirectory(MultipartFile file, String bizPath, String uploadType, Boolean needThumbnailFlag) { + String url = ""; + if(CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)){ + url = MinioUtil.upload(file, bizPath); + }else if(CommonConstant.UPLOAD_MY_UPLOAD.equals(uploadType)){ + url = MyUploadUtil.uploadFixedDirectory(file, bizPath, needThumbnailFlag); + }else{ + url = OssBootUtil.upload(file, bizPath); + } + return url; + } + + /** + * 本地文件上传 + * @param mf 文件 + * @param bizPath 自定义路径 + * @return + */ + public static String uploadLocal(MultipartFile mf,String bizPath,String uploadpath){ + try { + //update-begin-author:liusq date:20210809 for: 过滤上传文件类型 + FileTypeFilter.fileTypeFilter(mf); + //update-end-author:liusq date:20210809 for: 过滤上传文件类型 + String fileName = null; + + File file = new File(XssUtils.checkTraversal(uploadpath + File.separator + bizPath + File.separator )); + if (!file.exists()) { + file.mkdirs();// 创建文件根目录 + } + String orgName = mf.getOriginalFilename();// 获取文件名 + orgName = CommonUtils.getFileName(orgName); + if(orgName.indexOf(".")!=-1){ + fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf(".")); + }else{ + fileName = orgName+ "_" + System.currentTimeMillis(); + } + String savePath = file.getPath() + File.separator + fileName; + File savefile = new File(savePath); + FileCopyUtils.copy(mf.getBytes(), savefile); + String dbpath = null; + if(oConvertUtils.isNotEmpty(bizPath)){ + dbpath = bizPath + File.separator + fileName; + }else{ + dbpath = fileName; + } + if (dbpath.contains("\\")) { + dbpath = dbpath.replace("\\", "/"); + } + return dbpath; + } catch (IOException e) { + log.error(e.getMessage(), e); + }catch (Exception e) { + log.error(e.getMessage(), e); + } + return ""; + } + + /** + * 统一全局上传 带桶 + * @Return: java.lang.String + */ + public static String upload(MultipartFile file, String bizPath, String uploadType, String customBucket) { + String url = ""; + if(CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)){ + url = MinioUtil.upload(file,bizPath,customBucket); + }else if(CommonConstant.UPLOAD_MY_UPLOAD.equals(uploadType)){ + url = MyUploadUtil.upload(file,bizPath); + } + return url; + } + + /** 当前系统数据库类型 */ + private static String DB_TYPE = ""; + private static DbType dbTypeEnum = null; + + /** + * 全局获取平台数据库类型(作废了) + * @return + */ + @Deprecated + public static String getDatabaseType() { + if(oConvertUtils.isNotEmpty(DB_TYPE)){ + return DB_TYPE; + } + DataSource dataSource = SpringContextUtils.getApplicationContext().getBean(DataSource.class); + try { + return getDatabaseTypeByDataSource(dataSource); + } catch (SQLException e) { + //e.printStackTrace(); + log.warn(e.getMessage(),e); + return ""; + } + } + + /** + * 全局获取平台数据库类型(对应mybaisPlus枚举) + * @return + */ + public static DbType getDatabaseTypeEnum() { + return null; + //资源未释放:数据库-中危 +// if (oConvertUtils.isNotEmpty(dbTypeEnum)) { +// return dbTypeEnum; +// } +// try { +// DataSource dataSource = SpringContextUtils.getApplicationContext().getBean(DataSource.class); +// dbTypeEnum = JdbcUtils.getDbType(dataSource.getConnection().getMetaData().getURL()); +// return dbTypeEnum; +// } catch (SQLException e) { +// log.warn(e.getMessage(), e); +// return null; +// } + } + + /** + * 根据数据源key获取DataSourceProperty + * @param sourceKey + * @return + */ + public static DataSourceProperty getDataSourceProperty(String sourceKey){ + DynamicDataSourceProperties prop = SpringContextUtils.getApplicationContext().getBean(DynamicDataSourceProperties.class); + Map map = prop.getDatasource(); + DataSourceProperty db = (DataSourceProperty)map.get(sourceKey); + return db; + } + + /** + * 根据sourceKey 获取数据源连接 + * @param sourceKey + * @return + * @throws SQLException + */ + public static Connection getDataSourceConnect(String sourceKey) throws SQLException { + if (oConvertUtils.isEmpty(sourceKey)) { + sourceKey = "master"; + } + DynamicDataSourceProperties prop = SpringContextUtils.getApplicationContext().getBean(DynamicDataSourceProperties.class); + Map map = prop.getDatasource(); + DataSourceProperty db = (DataSourceProperty)map.get(sourceKey); + if(db==null){ + return null; + } + DriverManagerDataSource ds = new DriverManagerDataSource (); + ds.setDriverClassName(db.getDriverClassName()); + ds.setUrl(db.getUrl()); + ds.setUsername(db.getUsername()); + ds.setPassword(db.getPassword()); + return ds.getConnection(); + } + + /** + * 获取数据库类型 + * @param dataSource + * @return + * @throws SQLException + */ + private static String getDatabaseTypeByDataSource(DataSource dataSource) throws SQLException{ + if("".equals(DB_TYPE)) { + Connection connection = dataSource.getConnection(); + try { + DatabaseMetaData md = connection.getMetaData(); + String dbType = md.getDatabaseProductName().toLowerCase(); + if(dbType.indexOf("mysql")>=0) { + DB_TYPE = DataBaseConstant.DB_TYPE_MYSQL; + }else if(dbType.indexOf("oracle")>=0 ||dbType.indexOf("dm")>=0) { + DB_TYPE = DataBaseConstant.DB_TYPE_ORACLE; + }else if(dbType.indexOf("sqlserver")>=0||dbType.indexOf("sql server")>=0) { + DB_TYPE = DataBaseConstant.DB_TYPE_SQLSERVER; + }else if(dbType.indexOf("postgresql")>=0) { + DB_TYPE = DataBaseConstant.DB_TYPE_POSTGRESQL; + }else if(dbType.indexOf("mariadb")>=0) { + DB_TYPE = DataBaseConstant.DB_TYPE_MARIADB; + }else { + log.error("数据库类型:[" + dbType + "]不识别!"); + //throw new JeecgBootException("数据库类型:["+dbType+"]不识别!"); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + }finally { + connection.close(); + } + } + return DB_TYPE; + + } + /** + * 获取服务器地址 + * + * @param request + * @return + */ + public static String getBaseUrl(HttpServletRequest request) { + //1.【兼容】兼容微服务下的 base path------- + String x_gateway_base_path = request.getHeader("X_GATEWAY_BASE_PATH"); + if(oConvertUtils.isNotEmpty(x_gateway_base_path)){ + log.info("x_gateway_base_path = "+ x_gateway_base_path); + return x_gateway_base_path; + } + //2.【兼容】SSL认证之后,request.getScheme()获取不到https的问题 + // https://blog.csdn.net/weixin_34376986/article/details/89767950 + String scheme = request.getHeader("X-Forwarded-Scheme"); + if(oConvertUtils.isEmpty(scheme)){ + scheme = request.getScheme(); + } + + //3.常规操作 + String serverName = request.getServerName(); + int serverPort = request.getServerPort(); + String contextPath = request.getContextPath(); + + //返回 host domain + String baseDomainPath = null; + if(80 == serverPort){ + baseDomainPath = scheme + "://" + serverName + contextPath ; + }else{ + baseDomainPath = scheme + "://" + serverName + ":" + serverPort + contextPath ; + } + log.info("-----Common getBaseUrl----- : " + baseDomainPath); + return baseDomainPath; + } + + + public static List> splitList(List list, int size) { + List> lists = new ArrayList<>(); + int listSize = list.size(); + int numFullChunks = listSize / size; + for (int i = 0; i < numFullChunks; i++) { + int fromIndex = i * size; + int toIndex = Math.min((i + 1) * size, listSize); + lists.add(list.subList(fromIndex, toIndex)); + } + + // 如果列表大小不是size的整数倍,添加剩余的元素 + if (listSize % size != 0) { + int fromIndex = numFullChunks * size; + lists.add(list.subList(fromIndex, listSize)); + } + + return lists; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DateUtils.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DateUtils.java new file mode 100644 index 0000000..cd98e6c --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DateUtils.java @@ -0,0 +1,697 @@ +package org.jeecg.common.util; + +import java.beans.PropertyEditorSupport; +import java.sql.Timestamp; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import org.jeecg.common.constant.SymbolConstant; +import org.springframework.util.StringUtils; + +/** + * 类描述:时间操作定义类 + * + * @Author: 张代浩 + * @Date:2012-12-8 12:15:03 + * @Version 1.0 + */ +public class DateUtils extends PropertyEditorSupport { + + public static ThreadLocal date_sdf = new ThreadLocal() { + @Override + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat("yyyy-MM-dd"); + } + }; + public static ThreadLocal yyyyMMdd = new ThreadLocal() { + @Override + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat("yyyyMMdd"); + } + }; + public static ThreadLocal date_sdf_wz = new ThreadLocal() { + @Override + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat("yyyy年MM月dd日"); + } + }; + public static ThreadLocal time_sdf = new ThreadLocal() { + @Override + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat("yyyy-MM-dd HH:mm"); + } + }; + public static ThreadLocal yyyymmddhhmmss = new ThreadLocal() { + @Override + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat("yyyyMMddHHmmss"); + } + }; + public static ThreadLocal short_time_sdf = new ThreadLocal() { + @Override + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat("HH:mm"); + } + }; + public static ThreadLocal datetimeFormat = new ThreadLocal() { + @Override + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + } + }; + + /** + * 以毫秒表示的时间 + */ + private static final long DAY_IN_MILLIS = 24 * 3600 * 1000; + private static final long HOUR_IN_MILLIS = 3600 * 1000; + private static final long MINUTE_IN_MILLIS = 60 * 1000; + private static final long SECOND_IN_MILLIS = 1000; + + /** + * 指定模式的时间格式 + * @param pattern + * @return + */ + private static SimpleDateFormat getSdFormat(String pattern) { + return new SimpleDateFormat(pattern); + } + + /** + * 当前日历,这里用中国时间表示 + * + * @return 以当地时区表示的系统当前日历 + */ + public static Calendar getCalendar() { + return Calendar.getInstance(); + } + + /** + * 指定毫秒数表示的日历 + * + * @param millis 毫秒数 + * @return 指定毫秒数表示的日历 + */ + public static Calendar getCalendar(long millis) { + Calendar cal = Calendar.getInstance(); + // --------------------cal.setTimeInMillis(millis); + cal.setTime(new Date(millis)); + return cal; + } + + // //////////////////////////////////////////////////////////////////////////// + // getDate + // 各种方式获取的Date + // //////////////////////////////////////////////////////////////////////////// + + /** + * 当前日期 + * + * @return 系统当前时间 + */ + public static Date getDate() { + return new Date(); + } + + /** + * 指定毫秒数表示的日期 + * + * @param millis 毫秒数 + * @return 指定毫秒数表示的日期 + */ + public static Date getDate(long millis) { + return new Date(millis); + } + + /** + * 时间戳转换为字符串 + * + * @param time + * @return + */ + public static String timestamptoStr(Timestamp time) { + Date date = null; + if (null != time) { + date = new Date(time.getTime()); + } + return date2Str(date_sdf.get()); + } + + /** + * 字符串转换时间戳 + * + * @param str + * @return + */ + public static Timestamp str2Timestamp(String str) { + Date date = str2Date(str, date_sdf.get()); + return new Timestamp(date.getTime()); + } + + /** + * 字符串转换成日期 + * + * @param str + * @param sdf + * @return + */ + public static Date str2Date(String str, SimpleDateFormat sdf) { + if (null == str || "".equals(str)) { + return null; + } + Date date = null; + try { + date = sdf.parse(str); + return date; + } catch (ParseException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 日期转换为字符串 + * + * @param dateSdf 日期格式 + * @return 字符串 + */ + public static String date2Str(SimpleDateFormat dateSdf) { + synchronized (dateSdf) { + Date date = getDate(); + if (null == date) { + return null; + } + return dateSdf.format(date); + } + } + + /** + * 格式化时间 + * + * @param date + * @param format + * @return + */ + public static String dateformat(String date, String format) { + SimpleDateFormat sformat = new SimpleDateFormat(format); + Date nowDate = null; + try { + nowDate = sformat.parse(date); + } catch (ParseException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return sformat.format(nowDate); + } + + /** + * 日期转换为字符串 + * + * @param date 日期 + * @param dateSdf 日期格式 + * @return 字符串 + */ + public static String date2Str(Date date, SimpleDateFormat dateSdf) { + synchronized (dateSdf) { + if (null == date) { + return null; + } + return dateSdf.format(date); + } + } + + /** + * 日期转换为字符串 + * + * @param format 日期格式 + * @return 字符串 + */ + public static String getDate(String format) { + Date date = new Date(); + if (null == date) { + return null; + } + SimpleDateFormat sdf = new SimpleDateFormat(format); + return sdf.format(date); + } + + /** + * 指定毫秒数的时间戳 + * + * @param millis 毫秒数 + * @return 指定毫秒数的时间戳 + */ + public static Timestamp getTimestamp(long millis) { + return new Timestamp(millis); + } + + /** + * 以字符形式表示的时间戳 + * + * @param time 毫秒数 + * @return 以字符形式表示的时间戳 + */ + public static Timestamp getTimestamp(String time) { + return new Timestamp(Long.parseLong(time)); + } + + /** + * 系统当前的时间戳 + * + * @return 系统当前的时间戳 + */ + public static Timestamp getTimestamp() { + return new Timestamp(System.currentTimeMillis()); + } + + /** + * 当前时间,格式 yyyy-MM-dd HH:mm:ss + * + * @return 当前时间的标准形式字符串 + */ + public static String now() { + return datetimeFormat.get().format(getCalendar().getTime()); + } + + /** + * 指定日期的时间戳 + * + * @param date 指定日期 + * @return 指定日期的时间戳 + */ + public static Timestamp getTimestamp(Date date) { + return new Timestamp(date.getTime()); + } + + /** + * 指定日历的时间戳 + * + * @param cal 指定日历 + * @return 指定日历的时间戳 + */ + public static Timestamp getCalendarTimestamp(Calendar cal) { + // ---------------------return new Timestamp(cal.getTimeInMillis()); + return new Timestamp(cal.getTime().getTime()); + } + + public static Timestamp gettimestamp() { + Date dt = new Date(); + DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String nowTime = df.format(dt); + java.sql.Timestamp buydate = java.sql.Timestamp.valueOf(nowTime); + return buydate; + } + + // //////////////////////////////////////////////////////////////////////////// + // getMillis + // 各种方式获取的Millis + // //////////////////////////////////////////////////////////////////////////// + + /** + * 系统时间的毫秒数 + * + * @return 系统时间的毫秒数 + */ + public static long getMillis() { + return System.currentTimeMillis(); + } + + /** + * 指定日历的毫秒数 + * + * @param cal 指定日历 + * @return 指定日历的毫秒数 + */ + public static long getMillis(Calendar cal) { + // --------------------return cal.getTimeInMillis(); + return cal.getTime().getTime(); + } + + /** + * 指定日期的毫秒数 + * + * @param date 指定日期 + * @return 指定日期的毫秒数 + */ + public static long getMillis(Date date) { + return date.getTime(); + } + + /** + * 指定时间戳的毫秒数 + * + * @param ts 指定时间戳 + * @return 指定时间戳的毫秒数 + */ + public static long getMillis(Timestamp ts) { + return ts.getTime(); + } + + // //////////////////////////////////////////////////////////////////////////// + // formatDate + // 将日期按照一定的格式转化为字符串 + // //////////////////////////////////////////////////////////////////////////// + + /** + * 默认方式表示的系统当前日期,具体格式:年-月-日 + * + * @return 默认日期按“年-月-日“格式显示 + */ + public static String formatDate() { + return date_sdf.get().format(getCalendar().getTime()); + } + + /** + * 默认方式表示的系统当前日期,具体格式:yyyy-MM-dd HH:mm:ss + * + * @return 默认日期按“yyyy-MM-dd HH:mm:ss“格式显示 + */ + public static String formatDateTime() { + return datetimeFormat.get().format(getCalendar().getTime()); + } + + /** + * 获取时间字符串 + */ + public static String getDataString(SimpleDateFormat formatstr) { + synchronized (formatstr) { + return formatstr.format(getCalendar().getTime()); + } + } + + /** + * 指定日期的默认显示,具体格式:年-月-日 + * + * @param cal 指定的日期 + * @return 指定日期按“年-月-日“格式显示 + */ + public static String formatDate(Calendar cal) { + return date_sdf.get().format(cal.getTime()); + } + + /** + * 指定日期的默认显示,具体格式:年-月-日 + * + * @param date 指定的日期 + * @return 指定日期按“年-月-日“格式显示 + */ + public static String formatDate(Date date) { + return date_sdf.get().format(date); + } + + /** + * 指定毫秒数表示日期的默认显示,具体格式:年-月-日 + * + * @param millis 指定的毫秒数 + * @return 指定毫秒数表示日期按“年-月-日“格式显示 + */ + public static String formatDate(long millis) { + return date_sdf.get().format(new Date(millis)); + } + + /** + * 默认日期按指定格式显示 + * + * @param pattern 指定的格式 + * @return 默认日期按指定格式显示 + */ + public static String formatDate(String pattern) { + return getSdFormat(pattern).format(getCalendar().getTime()); + } + + /** + * 指定日期按指定格式显示 + * + * @param cal 指定的日期 + * @param pattern 指定的格式 + * @return 指定日期按指定格式显示 + */ + public static String formatDate(Calendar cal, String pattern) { + return getSdFormat(pattern).format(cal.getTime()); + } + + /** + * 指定日期按指定格式显示 + * + * @param date 指定的日期 + * @param pattern 指定的格式 + * @return 指定日期按指定格式显示 + */ + public static String formatDate(Date date, String pattern) { + return getSdFormat(pattern).format(date); + } + + // //////////////////////////////////////////////////////////////////////////// + // formatTime + // 将日期按照一定的格式转化为字符串 + // //////////////////////////////////////////////////////////////////////////// + + /** + * 默认方式表示的系统当前日期,具体格式:年-月-日 时:分 + * + * @return 默认日期按“年-月-日 时:分“格式显示 + */ + public static String formatTime() { + return time_sdf.get().format(getCalendar().getTime()); + } + + /** + * 指定毫秒数表示日期的默认显示,具体格式:年-月-日 时:分 + * + * @param millis 指定的毫秒数 + * @return 指定毫秒数表示日期按“年-月-日 时:分“格式显示 + */ + public static String formatTime(long millis) { + return time_sdf.get().format(new Date(millis)); + } + + /** + * 指定日期的默认显示,具体格式:年-月-日 时:分 + * + * @param cal 指定的日期 + * @return 指定日期按“年-月-日 时:分“格式显示 + */ + public static String formatTime(Calendar cal) { + return time_sdf.get().format(cal.getTime()); + } + + /** + * 指定日期的默认显示,具体格式:年-月-日 时:分 + * + * @param date 指定的日期 + * @return 指定日期按“年-月-日 时:分“格式显示 + */ + public static String formatTime(Date date) { + return time_sdf.get().format(date); + } + + // //////////////////////////////////////////////////////////////////////////// + // formatShortTime + // 将日期按照一定的格式转化为字符串 + // //////////////////////////////////////////////////////////////////////////// + + /** + * 默认方式表示的系统当前日期,具体格式:时:分 + * + * @return 默认日期按“时:分“格式显示 + */ + public static String formatShortTime() { + return short_time_sdf.get().format(getCalendar().getTime()); + } + + /** + * 指定毫秒数表示日期的默认显示,具体格式:时:分 + * + * @param millis 指定的毫秒数 + * @return 指定毫秒数表示日期按“时:分“格式显示 + */ + public static String formatShortTime(long millis) { + return short_time_sdf.get().format(new Date(millis)); + } + + /** + * 指定日期的默认显示,具体格式:时:分 + * + * @param cal 指定的日期 + * @return 指定日期按“时:分“格式显示 + */ + public static String formatShortTime(Calendar cal) { + return short_time_sdf.get().format(cal.getTime()); + } + + /** + * 指定日期的默认显示,具体格式:时:分 + * + * @param date 指定的日期 + * @return 指定日期按“时:分“格式显示 + */ + public static String formatShortTime(Date date) { + return short_time_sdf.get().format(date); + } + + // //////////////////////////////////////////////////////////////////////////// + // parseDate + // parseCalendar + // parseTimestamp + // 将字符串按照一定的格式转化为日期或时间 + // //////////////////////////////////////////////////////////////////////////// + + /** + * 根据指定的格式将字符串转换成Date 如输入:2003-11-19 11:20:20将按照这个转成时间 + * + * @param src 将要转换的原始字符窜 + * @param pattern 转换的匹配格式 + * @return 如果转换成功则返回转换后的日期 + * @throws ParseException + */ + public static Date parseDate(String src, String pattern) throws ParseException { + return getSdFormat(pattern).parse(src); + + } + + /** + * 根据指定的格式将字符串转换成Date 如输入:2003-11-19 11:20:20将按照这个转成时间 + * + * @param src 将要转换的原始字符窜 + * @param pattern 转换的匹配格式 + * @return 如果转换成功则返回转换后的日期 + * @throws ParseException + */ + public static Calendar parseCalendar(String src, String pattern) throws ParseException { + + Date date = parseDate(src, pattern); + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + return cal; + } + + public static String formatAddDate(String src, String pattern, int amount) throws ParseException { + Calendar cal; + cal = parseCalendar(src, pattern); + cal.add(Calendar.DATE, amount); + return formatDate(cal); + } + + /** + * 根据指定的格式将字符串转换成Date 如输入:2003-11-19 11:20:20将按照这个转成时间 + * + * @param src 将要转换的原始字符窜 + * @param pattern 转换的匹配格式 + * @return 如果转换成功则返回转换后的时间戳 + * @throws ParseException + */ + public static Timestamp parseTimestamp(String src, String pattern) throws ParseException { + Date date = parseDate(src, pattern); + return new Timestamp(date.getTime()); + } + + // //////////////////////////////////////////////////////////////////////////// + // dateDiff + // 计算两个日期之间的差值 + // //////////////////////////////////////////////////////////////////////////// + + /** + * 计算两个时间之间的差值,根据标志的不同而不同 + * + * @param flag 计算标志,表示按照年/月/日/时/分/秒等计算 + * @param calSrc 减数 + * @param calDes 被减数 + * @return 两个日期之间的差值 + */ + public static int dateDiff(char flag, Calendar calSrc, Calendar calDes) { + + long millisDiff = getMillis(calSrc) - getMillis(calDes); + char year = 'y'; + char day = 'd'; + char hour = 'h'; + char minute = 'm'; + char second = 's'; + + if (flag == year) { + return (calSrc.get(Calendar.YEAR) - calDes.get(Calendar.YEAR)); + } + + if (flag == day) { + return (int) (millisDiff / DAY_IN_MILLIS); + } + + if (flag == hour) { + return (int) (millisDiff / HOUR_IN_MILLIS); + } + + if (flag == minute) { + return (int) (millisDiff / MINUTE_IN_MILLIS); + } + + if (flag == second) { + return (int) (millisDiff / SECOND_IN_MILLIS); + } + + return 0; + } + + public static Long getCurrentTimestamp() { + return Long.valueOf(DateUtils.yyyymmddhhmmss.get().format(new Date())); + } + + /** + * String类型 转换为Date, 如果参数长度为10 转换格式”yyyy-MM-dd“ 如果参数长度为19 转换格式”yyyy-MM-dd + * HH:mm:ss“ * @param text String类型的时间值 + */ + @Override + public void setAsText(String text) throws IllegalArgumentException { + if (StringUtils.hasText(text)) { + try { + int length10 = 10; + int length19 = 19; + if (text.indexOf(SymbolConstant.COLON) == -1 && text.length() == length10) { + setValue(DateUtils.date_sdf.get().parse(text)); + } else if (text.indexOf(SymbolConstant.COLON) > 0 && text.length() == length19) { + setValue(DateUtils.datetimeFormat.get().parse(text)); + } else { + throw new IllegalArgumentException("Could not parse date, date format is error "); + } + } catch (ParseException ex) { + IllegalArgumentException iae = new IllegalArgumentException("Could not parse date: " + ex.getMessage()); + iae.initCause(ex); + throw iae; + } + } else { + setValue(null); + } + } + + public static int getYear() { + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTime(getDate()); + return calendar.get(Calendar.YEAR); + } + + // 定义格式化器(线程安全) + private static final DateTimeFormatter FORMATTER = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + /** + * 将秒级时间戳转换为字符串(默认系统时区) + */ + public static String timestampToString(long timestampSeconds) { + // 1. 将秒级时间戳转换为 Instant(Java 8+ 的时间戳表示) + Instant instant = Instant.ofEpochSecond(timestampSeconds); + + // 2. 将 Instant 转换为带时区的 LocalDateTime + LocalDateTime localDateTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime(); + + // 3. 格式化为字符串 + return localDateTime.format(FORMATTER); + } + + +} \ No newline at end of file diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DownFileUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DownFileUtil.java new file mode 100644 index 0000000..f677c34 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DownFileUtil.java @@ -0,0 +1,548 @@ +package org.jeecg.common.util; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.stereotype.Component; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.net.UnknownHostException; +import java.nio.channels.FileChannel; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Slf4j +@Component +public class DownFileUtil { + + public static final String linuxPath = "/usr/local/src/zhst/appPackage/";//linux环境下文件下载的目录(nginx文件代理配置的目录) +//-------------------------------------------------------- + /** + * 文件下载单本地方法 + * @param urlString 文件外网全路径 + * @param downloadPath 下载文件本地nginx代理地址(需要保存完整链接传此参数,返回全路径;为空默认返回半路径) + * @param orgUrl 文件原链接 + * @return 返回文件路径(传参数:downloadPath 返回全路径;为空默认返回半路径)) + */ + public static String download(String urlString,String downloadPath,String orgUrl) { + InputStream is = null; + FileOutputStream os = null; + try { + // 构造URL + URL url = new URL(urlString); + // 打开连接 + URLConnection con = url.openConnection(); + // 输入流 + is = con.getInputStream(); + // 1K的数据缓冲 + byte[] bs = new byte[8192]; + // 下载网络文件 + int bytesum = 0; + int byteread = 0; + // 输出的文件流 + String filename = ""; + if(StringUtils.isNotBlank(downloadPath)){ + filename = getFilename(true,urlString); + } else { + filename = getFilename(false,orgUrl); + } + + File file = new File(XssUtils.checkTraversal(filename)); + if(!file.getParentFile().exists()){ + file.getParentFile().mkdirs(); + } + os = new FileOutputStream(file, true); + while ((byteread = is.read(bs)) != -1) { + bytesum += byteread; +// log.warn(bytesum); + os.write(bs, 0, byteread); + } + if(StringUtils.isNotBlank(downloadPath)){ + String files = urlString.substring(urlString.lastIndexOf("/") + 1); + return getFilenameByServe(files,downloadPath); + } else { + return orgUrl; + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + // 完毕,关闭所有链接 + try { + if (null != os) { + os.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + try { + if (null != is) { + is.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + //从微信下载图片时如果没有id对应的图片则下载一个空图片,不会存在返回为null的情况 + return null; + } + + /** + * 文件下载单本地方法 + * @param urlString 文件外网全路径 + * @param downloadPath 下载文件本地nginx代理地址(需要保存完整链接传此参数,返回全路径;为空默认返回半路径) + * @param orgUrl 文件原链接 + * @return 返回文件路径(传参数:downloadPath 返回全路径;为空默认返回半路径)) + */ + public static String downloadV2(String urlString,String downloadPath,String orgUrl) { + InputStream is = null; + FileOutputStream os = null; + try { + // 1K的数据缓冲 + byte[] bs = new byte[1024]; + // 下载网络文件 + int byteread = 0; + // 输出的文件流 + String filename = ""; + if(StringUtils.isNotBlank(downloadPath)){ + filename = getFilename(true,urlString); + } else { + filename = getFilename(false,orgUrl); + } + + File file = new File(XssUtils.checkTraversal(filename)); + if(!file.getParentFile().exists()){ + file.getParentFile().mkdirs(); + } + // 打开连接 + os = new FileOutputStream(file, true); + // 输入流 + is = new URL(urlString).openConnection().getInputStream(); + while ((byteread = is.read(bs)) != -1) { + os.write(bs, 0, byteread); + } + if(StringUtils.isNotBlank(downloadPath)){ + String files = urlString.substring(urlString.lastIndexOf("/") + 1); + return getFilenameByServe(files,downloadPath); + } else { + return orgUrl; + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + // 完毕,关闭所有链接 + try { + if (null != os) { + os.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + try { + if (null != is) { + is.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + //从微信下载图片时如果没有id对应的图片则下载一个空图片,不会存在返回为null的情况 + return null; + } + + /** + * 多线程下载网络文件到本地方法 + * @param pathurl 文件外网全路径 + * @param downloadPath 下载文件本地nginx代理地址(需要保存完整链接传此参数,返回全路径;为空默认返回半路径) + * @param orgUrl 文件原链接 + * @return 返回文件路径(传参数:downloadPath 返回全路径;为空默认返回半路径)) + */ + public static String multiThreadDownload(String pathurl,String downloadPath,String orgUrl) throws IOException { + //是否删除分片的目录 + boolean delete = true; + //获取后缀名 + String suffix; + if (pathurl.contains("?")) { + String substring = pathurl.substring(0, pathurl.lastIndexOf('?')); + suffix = substring.substring(substring.lastIndexOf('.') + 1, substring.length()); + } else { + suffix = pathurl.substring(pathurl.lastIndexOf('.') + 1, pathurl.length()); + } + //指定文件的名字 + String name = ""; + log.warn("后缀名:" + suffix); + URL url = new URL(pathurl); + + String filename = ""; + if(StringUtils.isNotBlank(downloadPath)){//定义最终合成文件的存放路径(目录+文件名) + filename = DownFileUtil.getFilename(true,pathurl); + } else { + filename = DownFileUtil.getFilename(false,orgUrl); + } + //从连接获取文件 + long start = System.currentTimeMillis(); + String data = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); + String path; + if(System.getProperty("os.name").toLowerCase().contains("win")){//定义临时分片文件存放目录,安装系统环境进行区分 + path = FileShowUtil.getOuternet() + "\\"+start + data; + } else{ + path = DownFileUtil.linuxPath + "/"+start+ data; + } + + //多线程网络文件下载 + spilt3(url, path); + merge(path, filename, delete); + + //本地文件传输 + log.warn("下载文件名:" + name + data + "." + suffix); + log.warn("耗时时间:" + (System.currentTimeMillis() - start)/60000+"min"); + if(StringUtils.isNotBlank(downloadPath)){ + String files = pathurl.substring(pathurl.lastIndexOf("/") + 1); + return DownFileUtil.getFilenameByServe(files,downloadPath); + } else { + return orgUrl; + } + } + + public static void spilt3(URL url, String to) throws IOException { + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setRequestMethod("GET"); + //设置允许接收消息 + urlConnection.setDoInput(true); + urlConnection.connect(); + + File file = new File(XssUtils.checkTraversal(to)); + if (!file.exists()) { + file.mkdirs(); + } + + int fileSize = urlConnection.getContentLength();//大小; + log.warn("文件总共大小:" + fileSize + "字节"); + int size = 20;//默认1m + + long mb100 = 1024 * 1024 * 100; //100mb + long gb1 = 1024 * 1024 * 1024; //1gb + long gb10 = 10L * 1024 * 1024 * 1024; //10gb + + if (mb100 <= fileSize && gb1 > fileSize) { + size = 10; + } else if (gb1 <= fileSize && gb10 > fileSize) { + size = 100; + } else if (gb10 <= fileSize){ + size = 1024; + } + + // 将MB单位转为为字节B + int m = size * 1024 * 1024; + double m1 = m; + + // 计算最终会分成几个文件 + int count = (int) Math.ceil(fileSize / m1); + log.warn("文件分配:" + count + "块"); + +// ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2 - 1); + ScheduledExecutorService service = new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors() * 2 - 1, + new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build()); + try { + CountDownLatch countDown = new CountDownLatch(count); + for (int i = 0; i < count; i++) { + int index = i; + service.execute(() -> { + int beginPoint = index * m; + int endPoint; + if (index == (count - 1)) { + endPoint = fileSize; + } else { + endPoint = beginPoint + m; + } + InputStream inputStream = null; + FileOutputStream fileOutputStream = null; + try { + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + //设置允许接收消息 + conn.setDoInput(true); + conn.connect(); + inputStream = conn.getInputStream(); + /**inputStream = urlConnection.getInputStream(); **/ + inputStream.skip(beginPoint); + + int start = beginPoint; + fileOutputStream = new FileOutputStream(to + "/" + index); + byte[] bytes = new byte[m]; + int len; + log.warn("第" + index + "分片开始下载: 起始字节数:" + beginPoint + ",结束字节数:" + endPoint); + while (start < endPoint) { + len = inputStream.read(bytes); + if ((start + len) >= endPoint) { + len = endPoint - start; + start = endPoint; + } else { + start += len; + } + if (len < 0) { + log.warn("len=" + len + ",index=" + index); + start = endPoint; + break; + } +// log.warn("线程名:"+Thread.currentThread().getName()+ +// "--bytef="+bytes.length+",i="+ finalI +",len="+len); + fileOutputStream.write(bytes, 0, len); + } + log.warn("------------------第" + index + "分片,下载完成-------------------------"); + countDown.countDown(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if(null != inputStream){ + inputStream.close(); + } + if (null != fileOutputStream) { + fileOutputStream.flush(); + fileOutputStream.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + } + countDown.await(); + service.shutdown(); + log.warn("分解成功"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void merge(String from, String to, Boolean delete) throws IOException { + File t = new File(to); + if(!t.getParentFile().exists()){ + t.getParentFile().mkdirs(); + } + FileInputStream in = null; + FileChannel inChannel = null; + + FileOutputStream out = new FileOutputStream(t, true); + FileChannel outChannel = out.getChannel(); + + try { + File f = new File(from); + // 获取目录下的每一个文件名,再将每个文件一次写入目标文件 + if (f.isDirectory()) { + List list = getAllFileAndSort(from); + // 记录新文件最后一个数据的位置 + long start = 0; + for (File file : list) { + + in = new FileInputStream(file); + inChannel = in.getChannel(); + + // 从inChannel中读取file.length()长度的数据,写入outChannel的start处 + outChannel.transferFrom(inChannel, start, file.length()); + start += file.length(); + in.close(); + inChannel.close(); + } + } + if (delete) { + deleteFile(f); + } + log.warn("合成成功"); + out.close(); + outChannel.close(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + //资源未释放:流 - 质量问题 -中危 + if(out!=null){ + try { + out.close(); + } catch (IOException e) { + System.out.println("关闭流异常out【org.jeecg.common.util.DownFileUtil.merge】"); + } + } + if(in!=null){ + try { + in.close(); + } catch (IOException e) { + System.out.println("关闭流异常in【org.jeecg.common.util.DownFileUtil.merge】"); + } + } + } + } + + private static List getAllFileAndSort(String dirPath) { + File dirFile = new File(dirPath); + File[] listFiles = dirFile.listFiles(); + List list = Arrays.asList(listFiles); + Collections.sort(list, (o1, o2) -> { + return Integer.parseInt(o1.getName()) - Integer.parseInt(o2.getName()); + }); + return list; + } + + + //删除File对象中抽象的路径方法 + private static void deleteFile(File dir) { + //将file封装的路径下对象转换为数组/food/stUserFoodInfo/list + File[] files = dir.listFiles(); + //判断这个数组为不为空,如果不为空,就执行内部代码 + if (files != null) { + for (File file : files) { + //判断是否为文件 + if (file.isFile()) { + //如果为文件,执行删除 + file.delete(); + } else { + //如果不为文件,就(递归)进入这个文件夹,删除文件 + deleteFile(file); + } + } + //删除全部文件后删除空文件夹,最后删除自己 + dir.delete(); + } + } + + + + /** + * 获取文件扩展名 + * @return + */ + public static String ext(String filename) { + int index = filename.lastIndexOf("."); + + if (index == -1) { + return null; + } + String result = filename.substring(index + 1); + return result; + } + + + + /** + * 获取文件名 + * @param flag + * true : 从全路径截取文件名称 + * false: 获取原目录和文件名 + * location /{ + * alias D:/file/; + * autoindex on; + * } + * @param filename + * @return + * @throws UnsupportedEncodingException + */ + public static String getFilename(boolean flag,String filename) throws UnsupportedEncodingException { + String fileNameAll; + String filenames = ""; + if(flag){//从全路径截取文件名称 + filenames = filename.substring(filename.lastIndexOf("/") + 1); + } else {//获取原目录和文件名 + filenames = filename; + } + if(System.getProperty("os.name").toLowerCase().contains("win")){ + fileNameAll = FileShowUtil.getOuternet() + filenames;//保存到本地路径(部署时,配置nginx文件转发的映射地址) + } else{ + fileNameAll = linuxPath + filenames;//保存到linux服务器路径(部署时,配置nginx文件转发的映射地址) + } + return fileNameAll; + } + + + /** + * 获取本地文件nginx转发下载地址 + * 8888 为本地配置的nginx文件代理服务器端口 + * @return String + */ + public static String getFilenameByServe(String filename,String downloadPath) throws UnknownHostException { + String filenames = filename.substring(filename.lastIndexOf("/") + 1); + return downloadPath+filenames; + } + + + /** + * 将网络文件进行Base64位编码 + * @param imageUrl + * @return + * @throws IOException + */ + public static String getBase64(String imageUrl) throws IOException { + File file = null; + String fileName = imageUrl.substring(imageUrl.lastIndexOf("."),imageUrl.length()); + URL urlfile; + InputStream inputStream = null; + OutputStream outputStream= null; + try { + file = File.createTempFile("face_image", fileName); + //下载 + urlfile = new URL(imageUrl); + inputStream = urlfile.openStream(); + outputStream= new FileOutputStream(file); + + int bytesRead = 0; + byte[] buffer = new byte[8192]; + while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + } catch (Exception e) { + e.printStackTrace(); + }finally { + try { + if (null != outputStream) { + outputStream.close(); + } + if (null != inputStream) { + inputStream.close(); + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + return getBase64ceshi(file); + } + + public static String getBase64ceshi(File file) throws IOException { + String base64Str = null; + FileInputStream inputStream = null; + try { + Base64.Encoder encoder = Base64.getEncoder(); + inputStream = new FileInputStream(file); + int available = inputStream.available(); + byte[] bytes = new byte[available]; + inputStream.read(bytes); + base64Str = encoder.encodeToString(bytes); + } catch (Exception e) { + e.printStackTrace(); + }finally { + if(null != inputStream){ + inputStream.close(); + } + } + return replaceEnter(base64Str); + } + + /** + * 替换编码后字符串中的空格 + * @param str + * @return + */ + public static String replaceEnter(String str){ + String reg ="[\n-\r]"; + Pattern p = Pattern.compile(reg); + Matcher m = p.matcher(str); + return m.replaceAll(""); + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DySmsEnum.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DySmsEnum.java new file mode 100644 index 0000000..b06460a --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DySmsEnum.java @@ -0,0 +1,77 @@ +package org.jeecg.common.util; + +import org.apache.commons.lang3.StringUtils; + +/** + * @Description: 短信枚举类 + * @author: jeecg-boot + */ +public enum DySmsEnum { + + /**登录短信模板编码*/ + LOGIN_TEMPLATE_CODE("SMS_175435174","JEECG","code"), + /**忘记密码短信模板编码*/ + FORGET_PASSWORD_TEMPLATE_CODE("SMS_175435174","JEECG","code"), + /**注册账号短信模板编码*/ + REGISTER_TEMPLATE_CODE("SMS_175430166","JEECG","code"), + /**会议通知*/ + MEET_NOTICE_TEMPLATE_CODE("SMS_201480469","H5活动之家","username,title,minute,time"), + /**我的计划通知*/ + PLAN_NOTICE_TEMPLATE_CODE("SMS_201470515","H5活动之家","username,title,time"); + + /** + * 短信模板编码 + */ + private String templateCode; + /** + * 签名 + */ + private String signName; + /** + * 短信模板必需的数据名称,多个key以逗号分隔,此处配置作为校验 + */ + private String keys; + + private DySmsEnum(String templateCode,String signName,String keys) { + this.templateCode = templateCode; + this.signName = signName; + this.keys = keys; + } + + public String getTemplateCode() { + return templateCode; + } + + public void setTemplateCode(String templateCode) { + this.templateCode = templateCode; + } + + public String getSignName() { + return signName; + } + + public void setSignName(String signName) { + this.signName = signName; + } + + public String getKeys() { + return keys; + } + + public void setKeys(String keys) { + this.keys = keys; + } + + public static DySmsEnum toEnum(String templateCode) { + if(StringUtils.isEmpty(templateCode)){ + return null; + } + for(DySmsEnum item : DySmsEnum.values()) { + if(item.getTemplateCode().equals(templateCode)) { + return item; + } + } + return null; + } +} + diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/Exceptions.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/Exceptions.java new file mode 100644 index 0000000..d20cef5 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/Exceptions.java @@ -0,0 +1,65 @@ + +package org.jeecg.common.util; + +import cn.hutool.core.io.FastStringWriter; + +import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.UndeclaredThrowableException; + +/** + * 异常处理工具类 + */ +public class Exceptions { + + /** + * 将CheckedException转换为UncheckedException. + * + * @param e Throwable + * @return {RuntimeException} + */ + public static RuntimeException unchecked(Throwable e) { + if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) { + return new IllegalArgumentException(e); + } else if (e instanceof InvocationTargetException) { + return new RuntimeException(((InvocationTargetException) e).getTargetException()); + } else if (e instanceof RuntimeException) { + return (RuntimeException) e; + } else { + return new RuntimeException(e); + } + } + + /** + * 代理异常解包 + * + * @param wrapped 包装过得异常 + * @return 解包后的异常 + */ + public static Throwable unwrap(Throwable wrapped) { + Throwable unwrapped = wrapped; + while (true) { + if (unwrapped instanceof InvocationTargetException) { + unwrapped = ((InvocationTargetException) unwrapped).getTargetException(); + } else if (unwrapped instanceof UndeclaredThrowableException) { + unwrapped = ((UndeclaredThrowableException) unwrapped).getUndeclaredThrowable(); + } else { + return unwrapped; + } + } + } + + /** + * 将ErrorStack转化为String. + * + * @param ex Throwable + * @return {String} + */ + public static String getStackTraceAsString(Throwable ex) { + FastStringWriter stringWriter = new FastStringWriter(); + ex.printStackTrace(new PrintWriter(stringWriter)); + return stringWriter.toString(); + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FileShowUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FileShowUtil.java new file mode 100644 index 0000000..8504bb2 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FileShowUtil.java @@ -0,0 +1,151 @@ +package org.jeecg.common.util; + +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.config.FileShowConfig; +import org.springframework.stereotype.Component; + +/** + * 图片展示 + */ +@Component +public class FileShowUtil { + + + /** + * 内网client文件访问域名前缀(nacos修改实时刷新无需重启项目) + * @return + */ + public static String getIntranet() { + FileShowConfig jeecgBaseConfig = SpringContextUtils.getBean(FileShowConfig.class); + return jeecgBaseConfig.intranet; + } + + /** + * 内网client下载文件盘符目录(nacos修改实时刷新无需重启项目) + * @return + */ + public static String getOuternet() { + FileShowConfig jeecgBaseConfig = SpringContextUtils.getBean(FileShowConfig.class); + return jeecgBaseConfig.outernet; + } + /** + * 内网client下载外网文件域名前缀(nacos修改实时刷新无需重启项目) + * @return + */ + public static String getFinalurlOut() { + FileShowConfig jeecgBaseConfig = SpringContextUtils.getBean(FileShowConfig.class); + return jeecgBaseConfig.finalurlOut; + } + + /** + * 对外接口闭餐后发送就餐统计数据(nacos修改实时刷新无需重启项目) + * @return + */ + public static String getExternalApi() { + FileShowConfig jeecgBaseConfig = SpringContextUtils.getBean(FileShowConfig.class); + return jeecgBaseConfig.externalApi; + } + + /** + * 补全图片链接 + * @param path + * @return + */ + public static String getDownUrl(String path) { + + if (StringUtils.isEmpty(path)) { + return getFinalurlOut()+"default.png"; + } + if(path.contains("https") || path.contains("http")){ + if(path.contains("temp")){ + path = path.substring(path.indexOf("temp")); + }else if(path.contains("down")){ + path = path.substring(path.indexOf("down/") + "down/".length()); + }else { + return path; + } + } + path = path.replaceAll("\\\\","/"); + String allpath = getFinalurlOut() + path; + return allpath; + } + + public static String getShowUrl(String path) { + + + if (StringUtils.isEmpty(path)) { + return getIntranet()+"default.png"; + } + if(path.contains("https") || path.contains("http")){ + if(path.contains("temp")){ + path = path.substring(path.indexOf("temp")); + }else if(path.contains("down")){ + path = path.substring(path.indexOf("down/") + "down/".length()); + }else { + return path; + } + } + path = path.replaceAll("\\\\","/"); + String allpath = getIntranet() + path; + return allpath; + } + + + + + public static String getShowUrlSmall(String path) { + if (StringUtils.isEmpty(path)) { + return getIntranet()+"default.png"; + } + if(path.contains("http")){ + if(path.contains("temp")){ + path = path.substring(path.indexOf("temp")); + }else if(path.contains("down")){ + path = path.substring(path.indexOf("down/") + "down/".length()); + }else { + return path; + } + } + path = path.replaceAll("\\\\","/"); + String allpath = getIntranet() + path; + String imageFileName = allpath.substring(allpath.lastIndexOf("/")+1); + String str1 = allpath.substring(0, allpath.lastIndexOf("/")+1); + return str1+ CommonConstant.THUMBNAIL_PREFIX+imageFileName; + } + + + public static String getShowUrlSmallByClient(String path) { + if (StringUtils.isEmpty(path)) { + return getFinalurlOut()+"default.png"; + } + if(path.contains("http")){ + if(path.contains("temp")){ + path = path.substring(path.indexOf("temp")); + }else if(path.contains("down")){ + path = path.substring(path.indexOf("down/") + "down/".length()); + }else { + return path; + } + } + path = path.replaceAll("\\\\","/"); + String allpath = getFinalurlOut() + path; + String imageFileName = allpath.substring(allpath.lastIndexOf("/")+1); + String str1 = allpath.substring(0, allpath.lastIndexOf("/")+1); + return str1+ CommonConstant.THUMBNAIL_PREFIX+imageFileName; + } + + + /** + * 截取全路径 + * @param url + * @return + */ + public static String clearShowUrl(String url){ + String showUrl = getIntranet(); + if(StringUtils.isNotBlank(url) && url.contains(showUrl)){ + return url.replace(showUrl, ""); + } + return url; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FillRuleUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FillRuleUtil.java new file mode 100644 index 0000000..ae2c1ee --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FillRuleUtil.java @@ -0,0 +1,57 @@ +package org.jeecg.common.util; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.handler.IFillRuleHandler; + + +/** + * 规则值自动生成工具类 + * + * @author qinfeng + * @举例: 自动生成订单号;自动生成当前日期 + */ +@Slf4j +public class FillRuleUtil { + + /** + * @param ruleCode ruleCode + * @return + */ + @SuppressWarnings("unchecked") + public static Object executeRule(String ruleCode, JSONObject formData) { + if (!StringUtils.isEmpty(ruleCode)) { + try { + // 获取 Service + ServiceImpl impl = (ServiceImpl) SpringContextUtils.getBean("sysFillRuleServiceImpl"); + // 根据 ruleCode 查询出实体 + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("rule_code", ruleCode); + JSONObject entity = JSON.parseObject(JSON.toJSONString(impl.getOne(queryWrapper))); + if (entity == null) { + log.warn("填值规则:" + ruleCode + " 不存在"); + return null; + } + // 获取必要的参数 + String ruleClass = entity.getString("ruleClass"); + JSONObject params = entity.getJSONObject("ruleParams"); + if (params == null) { + params = new JSONObject(); + } + if (formData == null) { + formData = new JSONObject(); + } + // 通过反射执行配置的类里的方法 + IFillRuleHandler ruleHandler = (IFillRuleHandler) Class.forName(ruleClass).newInstance(); + return ruleHandler.execute(params, formData); + } catch (Exception e) { + e.printStackTrace(); + } + } + return null; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/HTMLUtils.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/HTMLUtils.java new file mode 100644 index 0000000..e84d6f3 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/HTMLUtils.java @@ -0,0 +1,32 @@ +package org.jeecg.common.util; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.util.HtmlUtils; + +/** + * HTML 工具类 + * @author: jeecg-boot + * @date: 2022/3/30 14:43 + */ +@SuppressWarnings("AlibabaClassNamingShouldBeCamel") +public class HTMLUtils { + + /** + * 获取HTML内的文本,不包含标签 + * + * @param html HTML 代码 + */ + public static String getInnerText(String html) { + if (StringUtils.isNotBlank(html)) { + //去掉 html 的标签 + String content = html.replaceAll("]+>", ""); + // 将多个空格合并成一个空格 + content = content.replaceAll("( )+", " "); + // 反向转义字符 + content = HtmlUtils.htmlUnescape(content); + return content.trim(); + } + return ""; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/ImportExcelUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/ImportExcelUtil.java new file mode 100644 index 0000000..ead7ee6 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/ImportExcelUtil.java @@ -0,0 +1,97 @@ +package org.jeecg.common.util; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.extension.service.IService; +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.constant.CommonConstant; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +/** + * 导出返回信息 + * @author: jeecg-boot + */ +@Slf4j +public class ImportExcelUtil { + + public static Result imporReturnRes(int errorLines,int successLines,List errorMessage) throws IOException { + if (errorLines == 0) { + return Result.ok("共" + successLines + "行数据全部导入成功!"); + } else { + JSONObject result = new JSONObject(5); + int totalCount = successLines + errorLines; + result.put("totalCount", totalCount); + result.put("errorCount", errorLines); + result.put("successCount", successLines); + result.put("msg", "总上传行数:" + totalCount + ",已导入行数:" + successLines + ",错误行数:" + errorLines); + String fileUrl = PmsUtil.saveErrorTxtByList(errorMessage, "userImportExcelErrorLog"); + int lastIndex = fileUrl.lastIndexOf(File.separator); + String fileName = fileUrl.substring(lastIndex + 1); + result.put("fileUrl", "/sys/common/static/" + fileUrl); + result.put("fileName", fileName); + Result res = Result.ok(result); + res.setCode(201); + res.setMessage("文件导入成功,但有错误。"); + return res; + } + } + + public static List importDateSave(List list, Class serviceClass, List errorMessage, String errorFlag) { + IService bean =(IService) SpringContextUtils.getBean(serviceClass); + for (int i = 0; i < list.size(); i++) { + try { + boolean save = bean.save(list.get(i)); + if(!save){ + throw new Exception(errorFlag); + } + } catch (Exception e) { + String message = e.getMessage().toLowerCase(); + int lineNumber = i + 1; + // 通过索引名判断出错信息 + if (message.contains(CommonConstant.SQL_INDEX_UNIQ_SYS_ROLE_CODE)) { + errorMessage.add("第 " + lineNumber + " 行:角色编码已经存在,忽略导入。"); + } else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_JOB_CLASS_NAME)) { + errorMessage.add("第 " + lineNumber + " 行:任务类名已经存在,忽略导入。"); + }else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_CODE)) { + errorMessage.add("第 " + lineNumber + " 行:职务编码已经存在,忽略导入。"); + }else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_DEPART_ORG_CODE)) { + errorMessage.add("第 " + lineNumber + " 行:部门编码已经存在,忽略导入。"); + }else { + errorMessage.add("第 " + lineNumber + " 行:未知错误,忽略导入"); + log.error(e.getMessage(), e); + } + } + } + return errorMessage; + } + + public static List importDateSaveOne(Object obj, Class serviceClass,List errorMessage,int i,String errorFlag) { + IService bean =(IService) SpringContextUtils.getBean(serviceClass); + try { + boolean save = bean.save(obj); + if(!save){ + throw new Exception(errorFlag); + } + } catch (Exception e) { + String message = e.getMessage().toLowerCase(); + int lineNumber = i + 1; + // 通过索引名判断出错信息 + if (message.contains(CommonConstant.SQL_INDEX_UNIQ_SYS_ROLE_CODE)) { + errorMessage.add("第 " + lineNumber + " 行:角色编码已经存在,忽略导入。"); + } else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_JOB_CLASS_NAME)) { + errorMessage.add("第 " + lineNumber + " 行:任务类名已经存在,忽略导入。"); + }else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_CODE)) { + errorMessage.add("第 " + lineNumber + " 行:职务编码已经存在,忽略导入。"); + }else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_DEPART_ORG_CODE)) { + errorMessage.add("第 " + lineNumber + " 行:部门编码已经存在,忽略导入。"); + }else { + errorMessage.add("第 " + lineNumber + " 行:未知错误,忽略导入"); + log.error(e.getMessage(), e); + } + } + return errorMessage; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/IpUtils.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/IpUtils.java new file mode 100644 index 0000000..9954cf7 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/IpUtils.java @@ -0,0 +1,61 @@ +package org.jeecg.common.util; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.HttpServletRequest; + +/** + * IP地址 + * + * @Author scott + * @email jeecgos@163.com + * @Date 2019年01月14日 + */ +public class IPUtils { + private static Logger logger = LoggerFactory.getLogger(IPUtils.class); + + /** + * 获取IP地址 + * + * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址 + * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址 + */ + public static String getIpAddr(HttpServletRequest request) { + String ip = null; + try { + ip = request.getHeader("x-forwarded-for"); + if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Client-IP"); + } + if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); + } + if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + } catch (Exception e) { + logger.error("IPUtils ERROR ", e); + } + +// //使用代理,则获取第一个IP地址 +// if(StringUtils.isEmpty(ip) && ip.length() > 15) { +// if(ip.indexOf(",") > 0) { +// ip = ip.substring(0, ip.indexOf(",")); +// } +// } + + return ip; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/LocalUploadUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/LocalUploadUtil.java new file mode 100644 index 0000000..b77fef0 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/LocalUploadUtil.java @@ -0,0 +1,78 @@ +package org.jeecg.common.util; + +import cn.hutool.core.io.IoUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FilenameUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.util.FileCopyUtils; + +import java.io.*; + +/** + * 本地文件上传工具类 + */ +@Slf4j +public class LocalUploadUtil { + + + private static String uploadpath; + /** + * 文件上传根目录 设置(这里设置的路径要和file-server.application-dev.yml的file.path保持一致) + * @return + */ + public static String getUploadpath() { + return uploadpath; + } + + public static void setUploadpath(String uploadpath) { + LocalUploadUtil.uploadpath = uploadpath; + } + + /** + * 本地文件上传 + * @param mf 文件 + * @param bizPath 自定义路径 + * @return + */ + public static String uploadLocal(InputStream mf,String bizPath,String orgName){ + try { + String ctxPath = uploadpath; + String fileName = null; + String paths = ctxPath + File.separator + bizPath + File.separator; + String pathNew= FilenameUtils.getFullPath(paths);// 输出: C:\Users\Example\Documents\ + System.out.println(pathNew); + File file = new File(XssUtils.checkTraversal(pathNew)); + if (!file.exists()) { + file.mkdirs();// 创建文件根目录 + } + orgName = CommonUtils.getFileName(orgName); + if(orgName.indexOf(".")!=-1){ + fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf(".")); + }else{ + fileName = orgName+ "_" + System.currentTimeMillis(); + } + //解决路径遍历漏洞-路径:高危 +// String savePath = file.getPath() + File.separator + fileName; + fileName = FilenameUtils.getName(fileName);//// 输出: file.txt + String normalized = pathNew.replace("\\", "/"); + String path = XssUtils.checkTraversal(normalized); + String savePath = path + fileName; + File savefile = new File(savePath); + FileCopyUtils.copy(IoUtil.readBytes(mf), savefile); + String dbpath = null; + if(oConvertUtils.isNotEmpty(bizPath)){ + dbpath = bizPath + File.separator + fileName; + }else{ + dbpath = fileName; + } + if (dbpath.contains("\\")) { + dbpath = dbpath.replace("\\", "/"); + } + return dbpath; + } catch (IOException e) { + log.error(e.getMessage(), e); + } + return ""; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/Md5Util.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/Md5Util.java new file mode 100644 index 0000000..cf70fa9 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/Md5Util.java @@ -0,0 +1,43 @@ +package org.jeecg.common.util; + +import java.security.MessageDigest; + +public class MD5Util { + + public static String byteArrayToHexString(byte b[]) { + StringBuffer resultSb = new StringBuffer(); + for (int i = 0; i < b.length; i++){ + resultSb.append(byteToHexString(b[i])); + } + return resultSb.toString(); + } + + private static String byteToHexString(byte b) { + int n = b; + if (n < 0) { + n += 256; + } + int d1 = n / 16; + int d2 = n % 16; + return hexDigits[d1] + hexDigits[d2]; + } + + public static String MD5Encode(String origin, String charsetname) { + String resultString = null; + try { + resultString = new String(origin); + MessageDigest md = MessageDigest.getInstance("MD5"); + if (charsetname == null || "".equals(charsetname)) { + resultString = byteArrayToHexString(md.digest(resultString.getBytes())); + } else { + resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname))); + } + } catch (Exception exception) { + } + return resultString; + } + + private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", + "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/MinioUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/MinioUtil.java new file mode 100644 index 0000000..3a36cbe --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/MinioUtil.java @@ -0,0 +1,222 @@ +package org.jeecg.common.util; + +import io.minio.*; +import io.minio.http.Method; +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.constant.SymbolConstant; +import org.jeecg.common.util.filter.FileTypeFilter; +import org.jeecg.common.util.filter.StrAttackFilter; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; +import java.net.URLDecoder; + +/** + * minio文件上传工具类 + * @author: jeecg-boot + */ +@Slf4j +public class MinioUtil { + private static String minioUrl; + private static String minioName; + private static String minioPass; + private static String bucketName; + + public static void setMinioUrl(String minioUrl) { + MinioUtil.minioUrl = minioUrl; + } + + public static void setMinioName(String minioName) { + MinioUtil.minioName = minioName; + } + + public static void setMinioPass(String minioPass) { + MinioUtil.minioPass = minioPass; + } + + public static void setBucketName(String bucketName) { + MinioUtil.bucketName = bucketName; + } + + public static String getMinioUrl() { + return minioUrl; + } + + public static String getBucketName() { + return bucketName; + } + + private static MinioClient minioClient = null; + + /** + * 上传文件 + * @param file + * @return + */ + public static String upload(MultipartFile file, String bizPath, String customBucket) { + String fileUrl = ""; + //update-begin-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击 + bizPath=StrAttackFilter.filter(bizPath); + //update-end-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击 + String newBucket = bucketName; + if(oConvertUtils.isNotEmpty(customBucket)){ + newBucket = customBucket; + } + try { + initMinio(minioUrl, minioName,minioPass); + // 检查存储桶是否已经存在 + if(minioClient.bucketExists(BucketExistsArgs.builder().bucket(newBucket).build())) { + log.info("Bucket already exists."); + } else { + // 创建一个名为ota的存储桶 + minioClient.makeBucket(MakeBucketArgs.builder().bucket(newBucket).build()); + log.info("create a new bucket."); + } + //update-begin-author:liusq date:20210809 for: 过滤上传文件类型 + FileTypeFilter.fileTypeFilter(file); + //update-end-author:liusq date:20210809 for: 过滤上传文件类型 + InputStream stream = file.getInputStream(); + // 获取文件名 + String orgName = file.getOriginalFilename(); + if("".equals(orgName)){ + orgName=file.getName(); + } + orgName = CommonUtils.getFileName(orgName); + String objectName = bizPath+"/" + +( orgName.indexOf(".")==-1 + ?orgName + "_" + System.currentTimeMillis() + :orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf(".")) + ); + + // 使用putObject上传一个本地文件到存储桶中。 + if(objectName.startsWith(SymbolConstant.SINGLE_SLASH)){ + objectName = objectName.substring(1); + } + PutObjectArgs objectArgs = PutObjectArgs.builder().object(objectName) + .bucket(newBucket) + .contentType("application/octet-stream") + .stream(stream,stream.available(),-1).build(); + minioClient.putObject(objectArgs); + stream.close(); + fileUrl = minioUrl+newBucket+"/"+objectName; + }catch (Exception e){ + log.error(e.getMessage(), e); + } + return fileUrl; + } + + /** + * 文件上传 + * @param file + * @param bizPath + * @return + */ + public static String upload(MultipartFile file, String bizPath) { + return upload(file,bizPath,null); + } + + /** + * 获取文件流 + * @param bucketName + * @param objectName + * @return + */ + public static InputStream getMinioFile(String bucketName,String objectName){ + InputStream inputStream = null; + try { + initMinio(minioUrl, minioName, minioPass); + GetObjectArgs objectArgs = GetObjectArgs.builder().object(objectName) + .bucket(bucketName).build(); + inputStream = minioClient.getObject(objectArgs); + } catch (Exception e) { + log.info("文件获取失败" + e.getMessage()); + } + return inputStream; + } + + /** + * 删除文件 + * @param bucketName + * @param objectName + * @throws Exception + */ + public static void removeObject(String bucketName, String objectName) { + try { + initMinio(minioUrl, minioName,minioPass); + RemoveObjectArgs objectArgs = RemoveObjectArgs.builder().object(objectName) + .bucket(bucketName).build(); + minioClient.removeObject(objectArgs); + }catch (Exception e){ + log.info("文件删除失败" + e.getMessage()); + } + } + + /** + * 获取文件外链 + * @param bucketName + * @param objectName + * @param expires + * @return + */ + public static String getObjectUrl(String bucketName, String objectName, Integer expires) { + initMinio(minioUrl, minioName,minioPass); + try{ + //update-begin---author:liusq Date:20220121 for:获取文件外链报错提示method不能为空,导致文件下载和预览失败---- + GetPresignedObjectUrlArgs objectArgs = GetPresignedObjectUrlArgs.builder().object(objectName) + .bucket(bucketName) + .expiry(expires).method(Method.GET).build(); + //update-begin---author:liusq Date:20220121 for:获取文件外链报错提示method不能为空,导致文件下载和预览失败---- + String url = minioClient.getPresignedObjectUrl(objectArgs); + return URLDecoder.decode(url,"UTF-8"); + }catch (Exception e){ + log.info("文件路径获取失败" + e.getMessage()); + } + return null; + } + + /** + * 初始化客户端 + * @param minioUrl + * @param minioName + * @param minioPass + * @return + */ + private static MinioClient initMinio(String minioUrl, String minioName,String minioPass) { + if (minioClient == null) { + try { + minioClient = MinioClient.builder() + .endpoint(minioUrl) + .credentials(minioName, minioPass) + .build(); + } catch (Exception e) { + e.printStackTrace(); + } + } + return minioClient; + } + + /** + * 上传文件到minio + * @param stream + * @param relativePath + * @return + */ + public static String upload(InputStream stream,String relativePath) throws Exception { + initMinio(minioUrl, minioName,minioPass); + if(minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) { + log.info("Bucket already exists."); + } else { + // 创建一个名为ota的存储桶 + minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + log.info("create a new bucket."); + } + PutObjectArgs objectArgs = PutObjectArgs.builder().object(relativePath) + .bucket(bucketName) + .contentType("application/octet-stream") + .stream(stream,stream.available(),-1).build(); + minioClient.putObject(objectArgs); + stream.close(); + return minioUrl+bucketName+"/"+relativePath; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/MyClassLoader.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/MyClassLoader.java new file mode 100644 index 0000000..20289b6 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/MyClassLoader.java @@ -0,0 +1,105 @@ +package org.jeecg.common.util; + +import org.jeecg.common.constant.SymbolConstant; + +/** + * @Author 张代浩 + */ +public class MyClassLoader extends ClassLoader { + public static Class getClassByScn(String className) { + Class myclass = null; + try { + myclass = Class.forName(className); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + throw new RuntimeException(className+" not found!"); + } + return myclass; + } + + /** + * 获得类的全名,包括包名 + * @param object + * @return + */ + public static String getPackPath(Object object) { + // 检查用户传入的参数是否为空 + if (object == null) { + throw new java.lang.IllegalArgumentException("参数不能为空!"); + } + // 获得类的全名,包括包名 + String clsName = object.getClass().getName(); + return clsName; + } + + public static String getAppPath(Class cls) { + // 检查用户传入的参数是否为空 + if (cls == null) { + throw new java.lang.IllegalArgumentException("参数不能为空!"); + } + ClassLoader loader = cls.getClassLoader(); + // 获得类的全名,包括包名 + String clsName = cls.getName() + ".class"; + // 获得传入参数所在的包 + Package pack = cls.getPackage(); + String path = ""; + // 如果不是匿名包,将包名转化为路径 + if (pack != null) { + String packName = pack.getName(); + String javaSpot="java."; + String javaxSpot="javax."; + // 此处简单判定是否是Java基础类库,防止用户传入JDK内置的类库 + if (packName.startsWith(javaSpot) || packName.startsWith(javaxSpot)) { + throw new java.lang.IllegalArgumentException("不要传送系统类!"); + } + // 在类的名称中,去掉包名的部分,获得类的文件名 + clsName = clsName.substring(packName.length() + 1); + // 判定包名是否是简单包名,如果是,则直接将包名转换为路径, + if (packName.indexOf(SymbolConstant.SPOT) < 0) { + path = packName + "/"; + } else { + // 否则按照包名的组成部分,将包名转换为路径 + int start = 0, end = 0; + end = packName.indexOf("."); + StringBuilder pathBuilder = new StringBuilder(); + while (end != -1) { + pathBuilder.append(packName, start, end).append("/"); + start = end + 1; + end = packName.indexOf(".", start); + } + if(oConvertUtils.isNotEmpty(pathBuilder.toString())){ + path = pathBuilder.toString(); + } + path = path + packName.substring(start) + "/"; + } + } + // 调用ClassLoader的getResource方法,传入包含路径信息的类文件名 + java.net.URL url = loader.getResource(path + clsName); + // 从URL对象中获取路径信息 + String realPath = url.getPath(); + // 去掉路径信息中的协议名"file:" + int pos = realPath.indexOf("file:"); + if (pos > -1) { + realPath = realPath.substring(pos + 5); + } + // 去掉路径信息最后包含类文件信息的部分,得到类所在的路径 + pos = realPath.indexOf(path + clsName); + realPath = realPath.substring(0, pos - 1); + // 如果类文件被打包到JAR等文件中时,去掉对应的JAR等打包文件名 + if (realPath.endsWith(SymbolConstant.EXCLAMATORY_MARK)) { + realPath = realPath.substring(0, realPath.lastIndexOf("/")); + } + /*------------------------------------------------------------ + ClassLoader的getResource方法使用了utf-8对路径信息进行了编码,当路径 + 中存在中文和空格时,他会对这些字符进行转换,这样,得到的往往不是我们想要 + 的真实路径,在此,调用了URLDecoder的decode方法进行解码,以便得到原始的 + 中文及空格路径 + -------------------------------------------------------------*/ + try { + realPath = java.net.URLDecoder.decode(realPath, "utf-8"); + } catch (Exception e) { + throw new RuntimeException(e); + } + return realPath; + }// getAppPath定义结束 +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/MyUploadUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/MyUploadUtil.java new file mode 100644 index 0000000..d9b5333 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/MyUploadUtil.java @@ -0,0 +1,542 @@ +package org.jeecg.common.util; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.lang.UUID; +import lombok.extern.slf4j.Slf4j; +import net.coobird.thumbnailator.Thumbnails; +import org.apache.http.Consts; +import org.apache.http.HttpEntity; +import org.apache.http.HttpStatus; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.mime.HttpMultipartMode; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.entity.mime.content.StringBody; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.protocol.HTTP; +import org.jeecg.common.constant.CommonConstant; +import org.springframework.web.multipart.MultipartFile; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * minio文件上传工具类 + */ +@Slf4j +public class MyUploadUtil { + private static String muUrl; + private static String muToken; + private static String muFileExistPath; + private static String muFileLengthPath; + private static String muFileUploadPath; + private static String muFileListUploadPath; + private static String muDownPath; + + private static int timeout = 3000; + + public static String getMuUrl() { + return muUrl; + } + + public static void setMuUrl(String muUrl) { + MyUploadUtil.muUrl = muUrl; + } + + public static String getMuToken() { + return muToken; + } + + public static void setMuToken(String muToken) { + MyUploadUtil.muToken = muToken; + } + + public String getMuFileExistPath() { + return muFileExistPath; + } + + public static void setMuFileExistPath(String muFileExistPath) { + MyUploadUtil.muFileExistPath = muFileExistPath; + } + + public static String getMuFileLengthPath() { + return muFileLengthPath; + } + + public static void setMuFileLengthPath(String muFileLengthPath) { + MyUploadUtil.muFileLengthPath = muFileLengthPath; + } + + public static String getMuFileUploadPath() { + return muFileUploadPath; + } + + public static void setMuFileUploadPath(String muFileUploadPath) { + MyUploadUtil.muFileUploadPath = muFileUploadPath; + } + + public static String getMuFileListUploadPath() { + return muFileListUploadPath; + } + + public static void setMuFileListUploadPath(String muFileListUploadPath) { + MyUploadUtil.muFileListUploadPath = muFileListUploadPath; + } + + public static String getMuDownPath() { + return muDownPath; + } + + public static void setMuDownPath(String muDownPath) { + MyUploadUtil.muDownPath = muDownPath; + } + + /** + * 上传文件 + * + * @param file + * @return + */ + public static String upload(MultipartFile file, String bizPath) { + String fileUrl = ""; + String fileName = file.getOriginalFilename();// 获取文件名 + try { + fileUrl = upload(file.getInputStream(), bizPath, fileName); + } catch (Exception e) { + e.printStackTrace(); + } + return fileUrl; + } + + /** + * 上传文件 带缩略图 + * + * @param file + * @return + */ + public static String upload(MultipartFile file, String bizPath, Boolean needThumbnailFlag) { + String fileUrl = ""; + String fileName = file.getOriginalFilename();// 获取文件名 + try { + fileUrl = upload(file.getInputStream(), bizPath, fileName, needThumbnailFlag); + } catch (Exception e) { + e.printStackTrace(); + } + return fileUrl; + } + + /** + * 上传文件到myupload + * + * @param stream + * @return + */ + public static String upload(InputStream stream, String bizPath, String fileName) throws Exception { + //避免重复文件覆盖,这里处理一下 + if (fileName.indexOf(".") > -1) { + String fileNameLeft = fileName.substring(0, fileName.lastIndexOf(".")); + fileName = fileNameLeft + "_" + System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf(".")); + } else { + fileName += "_" + System.currentTimeMillis(); + } + String nowday = new SimpleDateFormat("yyyyMMdd").format(new Date()); + String path = bizPath + File.separator + nowday; + String url = muUrl + "/" + muFileUploadPath; + String fileUrl = ""; + //创建post方法连接实例,在post方法中传入待连接地址 + CloseableHttpClient httpClient = HttpClients.createDefault(); + CloseableHttpResponse httpResponse = null; + try { + HttpPost httppost = new HttpPost(url); + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout).build(); + httppost.setConfig(requestConfig); + httppost.addHeader("token", muToken); + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + //设置浏览器兼容模式 + builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); + //设置请求的编码格式 + builder.setCharset(Consts.UTF_8); + builder.addBinaryBody("file", stream, ContentType.APPLICATION_OCTET_STREAM, fileName); + ContentType contentType = ContentType.create(HTTP.PLAIN_TEXT_TYPE, HTTP.UTF_8); + StringBody stringBody = new StringBody(fileName, contentType); + builder.addPart("fileName", stringBody); + stringBody = new StringBody(path, contentType); + builder.addPart("path", stringBody); +// builder.addTextBody("fileName", fileName); +// builder.addTextBody("path", path); + HttpEntity reqEntity = builder.build(); + httppost.setEntity(reqEntity); + + httpResponse = httpClient.execute(httppost); + int backCode = httpResponse.getStatusLine().getStatusCode(); + if (backCode != HttpStatus.SC_OK) { + log.error("上传文件失败"); + } else { + fileUrl = path + File.separator + fileName; + } + } catch (IOException e) { + log.error("文件服务器连接失败"); + } finally { + //释放资源 + try { + httpClient.close(); + if (httpResponse != null) { + httpResponse.close(); + } + } catch (IOException e) { +// e.printStackTrace(); + } + } + return fileUrl; + } + + /** + * 上传文件到myupload + * + * @param stream + * @return + */ + public static String upload(InputStream stream, String bizPath, String fileName, Boolean needThumbnailFlag) throws Exception { + //避免重复文件覆盖,这里处理一下 + if (fileName.indexOf(".") > -1) { + //String fileNameLeft = fileName.substring(0, fileName.lastIndexOf(".")); + String fileNameLeft = UUID.randomUUID().toString(); + fileName = fileNameLeft + "_" + System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf(".")); + } else { + fileName += "_" + System.currentTimeMillis(); + } + String nowday = new SimpleDateFormat("yyyyMMdd").format(new Date()); + String path = bizPath + File.separator + nowday; + String url = muUrl + "/" + muFileUploadPath; + String fileUrl = ""; + //创建post方法连接实例,在post方法中传入待连接地址 + CloseableHttpClient httpClient = HttpClients.createDefault(); + CloseableHttpResponse httpResponse = null; + + byte[] bytes = IoUtil.readBytes(stream); + + try { + HttpPost httppost = new HttpPost(url); + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout).build(); + httppost.setConfig(requestConfig); + httppost.addHeader("token", muToken); + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + //设置浏览器兼容模式 + builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); + //设置请求的编码格式 + builder.setCharset(Consts.UTF_8); + builder.addBinaryBody("file", new ByteArrayInputStream(bytes), ContentType.APPLICATION_OCTET_STREAM, fileName); + + ContentType contentType = ContentType.create(HTTP.PLAIN_TEXT_TYPE, HTTP.UTF_8); + StringBody stringBody = new StringBody(fileName, contentType); + builder.addPart("fileName", stringBody); + stringBody = new StringBody(path, contentType); + builder.addPart("path", stringBody); +// builder.addTextBody("fileName", fileName); +// builder.addTextBody("path", path); + HttpEntity reqEntity = builder.build(); + httppost.setEntity(reqEntity); + + httpResponse = httpClient.execute(httppost); + + + int backCode = httpResponse.getStatusLine().getStatusCode(); + if (backCode != HttpStatus.SC_OK) { + log.error("上传文件失败"); + } else { + fileUrl = path + File.separator + fileName; + } + } catch (IOException e) { + log.error("文件服务器连接失败"); + } finally { + //释放资源 + try { + httpClient.close(); + if (httpResponse != null) { + httpResponse.close(); + } + } catch (IOException e) { +// e.printStackTrace(); + } + } + +// if (needThumbnailFlag) { +// uploadThumbnail(url, bytes, fileName, path); +// } + + return fileUrl; + } + + + /** + * 上传缩略图到myupload + * + * @return + */ +// private static void uploadThumbnail(String url, byte[] bytes, String fileName, String path) { +// //创建post方法连接实例,在post方法中传入待连接地址 +// CloseableHttpClient httpClient = HttpClients.createDefault(); +// CloseableHttpResponse httpResponse = null; +// try { +// String thumbnailName = CommonConstant.THUMBNAIL_PREFIX + fileName; +// +// BufferedImage bufferedImage = Thumbnails.of(new ByteArrayInputStream(bytes)) +// .scale(CommonConstant.THUMBNAIL_SCALE) +// .asBufferedImage(); +// +// ByteArrayOutputStream os = new ByteArrayOutputStream(); +// ImageIO.write(bufferedImage, "png", os); +// InputStream is = new ByteArrayInputStream(os.toByteArray()); +// +// HttpPost httppost = new HttpPost(url); +// RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout).build(); +// httppost.setConfig(requestConfig); +// httppost.addHeader("token", muToken); +// MultipartEntityBuilder builder = MultipartEntityBuilder.create(); +// //设置浏览器兼容模式 +// builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); +// //设置请求的编码格式 +// builder.setCharset(Consts.UTF_8); +// +// builder.addBinaryBody("file", is, ContentType.APPLICATION_OCTET_STREAM, thumbnailName); +// +// ContentType contentType = ContentType.create(HTTP.PLAIN_TEXT_TYPE, HTTP.UTF_8); +// StringBody stringBody = new StringBody(thumbnailName, contentType); +// builder.addPart("fileName", stringBody); +// stringBody = new StringBody(path, contentType); +// builder.addPart("path", stringBody); +// HttpEntity reqEntity = builder.build(); +// httppost.setEntity(reqEntity); +// +// httpResponse = httpClient.execute(httppost); +// +// int backCode = httpResponse.getStatusLine().getStatusCode(); +// if (backCode != HttpStatus.SC_OK) { +// log.error("上传缩略图失败"); +// } +// } catch (IOException e) { +// log.error("文件服务器连接失败"); +// } finally { +// //释放资源 +// try { +// httpClient.close(); +// if (httpResponse != null) { +// httpResponse.close(); +// } +// } catch (IOException e) { +// } +// } +// } + + /** + * 上传文件 固定目录 带缩略图(同名覆盖) + * + * @param file + * @return + */ + public static String uploadFixedDirectory(MultipartFile file, String bizPath, Boolean needThumbnailFlag) { + String fileUrl = ""; + String fileName = file.getOriginalFilename();// 获取文件名 + try { + fileUrl = fixedDirectory(file.getInputStream(), bizPath, fileName, needThumbnailFlag); + file.getInputStream().close(); + } catch (Exception e) { + e.printStackTrace(); + }finally{ + //资源未释放:流 - 质量问题 -中危 + if(file!=null){ + try { + file.getInputStream().close(); + } catch (IOException e) { + System.out.println("关闭流异常【org.jeecg.common.util.MyUploadUtil.upload(org.springframework.web.multipart.MultipartFile, java.lang.String, java.lang.Boolean)】"); + } + } + } + return fileUrl; + } + + /** + * 上传文件到myupload + * + * @param stream + * @return + */ + public static String fixedDirectory(InputStream stream, String bizPath, String fileName, Boolean needThumbnailFlag) throws Exception { + String path = bizPath; + String url = muUrl + "/" + muFileUploadPath; + String fileUrl = ""; + //创建post方法连接实例,在post方法中传入待连接地址 + CloseableHttpClient httpClient = HttpClients.createDefault(); + CloseableHttpResponse httpResponse = null; + + byte[] bytes = IoUtil.readBytes(stream); + + try { + HttpPost httppost = new HttpPost(url); + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout).build(); + httppost.setConfig(requestConfig); + httppost.addHeader("token", muToken); + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + //设置浏览器兼容模式 + builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); + //设置请求的编码格式 + builder.setCharset(Consts.UTF_8); + builder.addBinaryBody("file", new ByteArrayInputStream(bytes), ContentType.APPLICATION_OCTET_STREAM, fileName); + + ContentType contentType = ContentType.create(HTTP.PLAIN_TEXT_TYPE, HTTP.UTF_8); + StringBody stringBody = new StringBody(fileName, contentType); + builder.addPart("fileName", stringBody); + stringBody = new StringBody(path, contentType); + builder.addPart("path", stringBody); +// builder.addTextBody("fileName", fileName); +// builder.addTextBody("path", path); + HttpEntity reqEntity = builder.build(); + httppost.setEntity(reqEntity); + + httpResponse = httpClient.execute(httppost); + + + int backCode = httpResponse.getStatusLine().getStatusCode(); + if (backCode != HttpStatus.SC_OK) { + log.error("上传文件失败"); + } else { + fileUrl = path + File.separator + fileName; + } + } catch (IOException e) { + log.error("文件服务器连接失败"); + } finally { + //释放资源 + try { + httpClient.close(); + if (httpResponse != null) { + httpResponse.close(); + } + } catch (IOException e) { +// e.printStackTrace(); + } + } + + if (needThumbnailFlag) { + uploadThumbnailV2(url, bytes, fileName, path); + } + + return fileUrl; + } + + + /** + * 上传缩略图到myupload + * 限定压缩图片指定大小100kb,原图大于进行自动调节精度(经验数值)压缩,小于不压缩 + * @return + */ + private static void uploadThumbnailV2(String url, byte[] imageBytes, String fileName, String path) { + CloseableHttpResponse httpResponse = null; + CloseableHttpClient httpClient = HttpClients.createDefault(); + try { + //压缩后的图片byte[] + byte[] compressedBytes = compressPicForScale(imageBytes,100,fileName); + InputStream is = new ByteArrayInputStream(compressedBytes); + //压缩后的图片名称 + String thumbnailName = CommonConstant.THUMBNAIL_PREFIX + fileName; + + + HttpPost httppost = new HttpPost(url); + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout).build(); + httppost.setConfig(requestConfig); + httppost.addHeader("token", muToken); + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + //设置浏览器兼容模式 + builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); + //设置请求的编码格式 + builder.setCharset(Consts.UTF_8); + + builder.addBinaryBody("file", is, ContentType.APPLICATION_OCTET_STREAM, thumbnailName); + + ContentType contentType = ContentType.create(HTTP.PLAIN_TEXT_TYPE, HTTP.UTF_8); + StringBody stringBody = new StringBody(thumbnailName, contentType); + builder.addPart("fileName", stringBody); + stringBody = new StringBody(path, contentType); + builder.addPart("path", stringBody); + HttpEntity reqEntity = builder.build(); + httppost.setEntity(reqEntity); + + httpResponse = httpClient.execute(httppost); + + int backCode = httpResponse.getStatusLine().getStatusCode(); + if (backCode != HttpStatus.SC_OK) { + log.error("上传缩略图失败"); + } + } catch (IOException e) { + log.error("文件服务器连接失败"); + } finally { + //释放资源 + try { + httpClient.close(); + if (httpResponse != null) { + httpResponse.close(); + } + } catch (IOException e) { + } + } + + } + + + /** + * 根据指定大小压缩图片 + * + * @param imageBytes 源图片字节数组 + * @param desFileSize 指定图片大小,单位kb + * @param imageId 影像编号 + * @return 压缩质量后的图片字节数组 + */ + public static byte[] compressPicForScale(byte[] imageBytes, long desFileSize, String imageId) { + if (imageBytes == null || imageBytes.length <= 0 || imageBytes.length < desFileSize * 1024) { + return imageBytes; + } + long srcSize = imageBytes.length; + double accuracy = getAccuracy(srcSize / 1024); + try { + while (imageBytes.length > desFileSize * 1024) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(imageBytes.length); + Thumbnails.of(inputStream) + .scale(accuracy) + .outputQuality(accuracy) + .toOutputStream(outputStream); + imageBytes = outputStream.toByteArray(); + } + log.info("【图片压缩】imageId={" + imageId + "} | 图片原大小={" + srcSize / 1024 + "}kb | 压缩后大小={" + imageBytes.length / 1024 + "}kb"); + } catch (Exception e) { + log.error("【图片压缩】msg=图片压缩失败!", e); + } + return imageBytes; + } + + /** + * 自动调节精度(经验数值) + * + * @param size 源图片大小 + * @return 图片压缩质量比 + */ + private static double getAccuracy(long size) { + double accuracy; + if (size < 900) { + accuracy = 0.85; + } else if (size < 2047) { + accuracy = 0.6; + } else if (size < 3275) { + accuracy = 0.44; + } else { + accuracy = 0.4; + } + return accuracy; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/PasswordUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/PasswordUtil.java new file mode 100644 index 0000000..aaae509 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/PasswordUtil.java @@ -0,0 +1,191 @@ +package org.jeecg.common.util; + +import java.security.Key; +import java.security.SecureRandom; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +/** + * @Description: 密码工具类 + * @author: jeecg-boot + */ +public class PasswordUtil { + + /** + * JAVA6支持以下任意一种算法 PBEWITHMD5ANDDES PBEWITHMD5ANDTRIPLEDES + * PBEWITHSHAANDDESEDE PBEWITHSHA1ANDRC2_40 PBKDF2WITHHMACSHA1 + * */ + + /** + * 定义使用的算法为:PBEWITHMD5andDES算法 + * 加密算法 + */ + public static final String ALGORITHM = "PBEWithMD5AndDES"; + + /** + * 定义使用的算法为:PBEWITHMD5andDES算法 + * 密钥 + */ + public static final String SALT = "63293188"; + + /** + * 定义迭代次数为1000次 + */ + private static final int ITERATIONCOUNT = 1000; + + /** + * 获取加密算法中使用的盐值,解密中使用的盐值必须与加密中使用的相同才能完成操作. 盐长度必须为8字节 + * + * @return byte[] 盐值 + * */ + public static byte[] getSalt() throws Exception { + // 实例化安全随机数 + SecureRandom random = new SecureRandom(); + // 产出盐 + return random.generateSeed(8); + } + + public static byte[] getStaticSalt() { + // 产出盐 + return SALT.getBytes(); + } + + /** + * 根据PBE密码生成一把密钥 + * + * @param password + * 生成密钥时所使用的密码 + * @return Key PBE算法密钥 + * */ + private static Key getPbeKey(String password) { + // 实例化使用的算法 + SecretKeyFactory keyFactory; + SecretKey secretKey = null; + try { + keyFactory = SecretKeyFactory.getInstance(ALGORITHM); + // 设置PBE密钥参数 + PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray()); + // 生成密钥 + secretKey = keyFactory.generateSecret(keySpec); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return secretKey; + } + + /** + * 加密明文字符串 + * + * @param plaintext + * 待加密的明文字符串 + * @param password + * 生成密钥时所使用的密码 + * @param salt + * 盐值 + * @return 加密后的密文字符串 + * @throws Exception + */ + public static String encrypt(String plaintext, String password, String salt) { + + Key key = getPbeKey(password); + byte[] encipheredData = null; + PBEParameterSpec parameterSpec = new PBEParameterSpec(salt.getBytes(), ITERATIONCOUNT); + try { + Cipher cipher = Cipher.getInstance(ALGORITHM); + + cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec); + //update-begin-author:sccott date:20180815 for:中文作为用户名时,加密的密码windows和linux会得到不同的结果 gitee/issues/IZUD7 + encipheredData = cipher.doFinal(plaintext.getBytes("utf-8")); + //update-end-author:sccott date:20180815 for:中文作为用户名时,加密的密码windows和linux会得到不同的结果 gitee/issues/IZUD7 + } catch (Exception e) { + } + return bytesToHexString(encipheredData); + } + + /** + * 解密密文字符串 + * + * @param ciphertext + * 待解密的密文字符串 + * @param password + * 生成密钥时所使用的密码(如需解密,该参数需要与加密时使用的一致) + * @param salt + * 盐值(如需解密,该参数需要与加密时使用的一致) + * @return 解密后的明文字符串 + * @throws Exception + */ + public static String decrypt(String ciphertext, String password, String salt) { + + Key key = getPbeKey(password); + byte[] passDec = null; + PBEParameterSpec parameterSpec = new PBEParameterSpec(salt.getBytes(), ITERATIONCOUNT); + try { + Cipher cipher = Cipher.getInstance(ALGORITHM); + + cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec); + + passDec = cipher.doFinal(hexStringToBytes(ciphertext)); + } + + catch (Exception e) { + // TODO: handle exception + } + return new String(passDec); + } + + /** + * 将字节数组转换为十六进制字符串 + * + * @param src + * 字节数组 + * @return + */ + public static String bytesToHexString(byte[] src) { + StringBuilder stringBuilder = new StringBuilder(""); + if (src == null || src.length <= 0) { + return null; + } + for (int i = 0; i < src.length; i++) { + int v = src[i] & 0xFF; + String hv = Integer.toHexString(v); + if (hv.length() < 2) { + stringBuilder.append(0); + } + stringBuilder.append(hv); + } + return stringBuilder.toString(); + } + + /** + * 将十六进制字符串转换为字节数组 + * + * @param hexString + * 十六进制字符串 + * @return + */ + public static byte[] hexStringToBytes(String hexString) { + if (hexString == null || "".equals(hexString)) { + return null; + } + hexString = hexString.toUpperCase(); + int length = hexString.length() / 2; + char[] hexChars = hexString.toCharArray(); + byte[] d = new byte[length]; + for (int i = 0; i < length; i++) { + int pos = i * 2; + d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); + } + return d; + } + + private static byte charToByte(char c) { + return (byte) "0123456789ABCDEF".indexOf(c); + } + + +} \ No newline at end of file diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/PmsUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/PmsUtil.java new file mode 100644 index 0000000..0926202 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/PmsUtil.java @@ -0,0 +1,65 @@ +package org.jeecg.common.util; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.util.Date; +import java.util.List; + +/** + * @Description: PmsUtil + * @author: jeecg-boot + */ +@Slf4j +@Component +public class PmsUtil { + + + private static String uploadPath; + + @Value("${jeecg.path.upload}") + public void setUploadPath(String uploadPath) { + PmsUtil.uploadPath = uploadPath; + } + + public static String saveErrorTxtByList(List msg, String name) { + Date d = new Date(); + String saveDir = "logs" + File.separator + DateUtils.yyyyMMdd.get().format(d) + File.separator; + String saveFullDir = uploadPath + File.separator + saveDir; + + File saveFile = new File(saveFullDir); + if (!saveFile.exists()) { + saveFile.mkdirs(); + } + name += DateUtils.yyyymmddhhmmss.get().format(d) + Math.round(Math.random() * 10000); + String saveFilePath = saveFullDir + name + ".txt"; + + try { + //封装目的地 + BufferedWriter bw = new BufferedWriter(new FileWriter(saveFilePath)); + //遍历集合 + for (String s : msg) { + //写数据 + if (s.indexOf("_") > 0) { + String[] arr = s.split("_"); + bw.write("第" + arr[0] + "行:" + arr[1]); + } else { + bw.write(s); + } + //bw.newLine(); + bw.write("\r\n"); + } + //释放资源 + bw.flush(); + bw.close(); + } catch (Exception e) { + log.info("excel导入生成错误日志文件异常:" + e.getMessage()); + } + return saveDir + name + ".txt"; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/RandImageUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/RandImageUtil.java new file mode 100644 index 0000000..3b59a89 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/RandImageUtil.java @@ -0,0 +1,148 @@ +package org.jeecg.common.util; + +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Base64; + +/** + * 登录验证码工具类 + */ +public class RandImageUtil { + + public static final String key = "JEECG_LOGIN_KEY"; + + /** + * 定义图形大小 + */ + private static final int width = 105; + /** + * 定义图形大小 + */ + private static final int height = 35; + + /** + * 定义干扰线数量 + */ + private static final int count = 200; + + /** + * 干扰线的长度=1.414*lineWidth + */ + private static final int lineWidth = 2; + + /** + * 图片格式 + */ + private static final String IMG_FORMAT = "JPEG"; + + /** + * base64 图片前缀 + */ + private static final String BASE64_PRE = "data:image/jpg;base64,"; + + /** + * 直接通过response 返回图片 + * @param response + * @param resultCode + * @throws IOException + */ + public static void generate(HttpServletResponse response, String resultCode) throws IOException { + BufferedImage image = getImageBuffer(resultCode); + // 输出图象到页面 + ImageIO.write(image, IMG_FORMAT, response.getOutputStream()); + } + + /** + * 生成base64字符串 + * @param resultCode + * @return + * @throws IOException + */ + public static String generate(String resultCode) throws IOException { + BufferedImage image = getImageBuffer(resultCode); + + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + //写入流中 + ImageIO.write(image, IMG_FORMAT, byteStream); + //转换成字节 + byte[] bytes = byteStream.toByteArray(); + //转换成base64串 + String base64 = Base64.getEncoder().encodeToString(bytes).trim(); + base64 = base64.replaceAll("\n", "").replaceAll("\r", "");//删除 \r\n + + //写到指定位置 + //ImageIO.write(bufferedImage, "png", new File("")); + + return BASE64_PRE+base64; + } + + private static BufferedImage getImageBuffer(String resultCode){ + // 在内存中创建图象 + final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + // 获取图形上下文 + final Graphics2D graphics = (Graphics2D) image.getGraphics(); + // 设定背景颜色 + graphics.setColor(Color.WHITE); // ---1 + graphics.fillRect(0, 0, width, height); + // 设定边框颜色 +// graphics.setColor(getRandColor(100, 200)); // ---2 + graphics.drawRect(0, 0, width - 1, height - 1); + + try { + // 不安全的随机数-中危 + final SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); + // 随机产生干扰线,使图象中的认证码不易被其它程序探测到 + for (int i = 0; i < count; i++) { + graphics.setColor(getRandColor(150, 200)); // ---3 + + final int x = random.nextInt(width - lineWidth - 1) + 1; // 保证画在边框之内 + final int y = random.nextInt(height - lineWidth - 1) + 1; + final int xl = random.nextInt(lineWidth); + final int yl = random.nextInt(lineWidth); + graphics.drawLine(x, y, x + xl, y + yl); + } + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + // 取随机产生的认证码 + for (int i = 0; i < resultCode.length(); i++) { + // 将认证码显示到图象中,调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成 + // graphics.setColor(new Color(20 + random.nextInt(130), 20 + random + // .nextInt(130), 20 + random.nextInt(130))); + // 设置字体颜色 + graphics.setColor(Color.BLACK); + // 设置字体样式 +// graphics.setFont(new Font("Arial Black", Font.ITALIC, 18)); + graphics.setFont(new Font("Times New Roman", Font.BOLD, 24)); + // 设置字符,字符间距,上边距 + graphics.drawString(String.valueOf(resultCode.charAt(i)), (23 * i) + 8, 26); + } + // 图象生效 + graphics.dispose(); + return image; + } + + private static Color getRandColor(int fc, int bc) throws NoSuchAlgorithmException { // 取得给定范围随机颜色 +// final Random random = new Random(); + // 不安全的随机数-中危 + final SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); + if (fc > 255) { + fc = 255; + } + if (bc > 255) { + bc = 255; + } + + final int r = fc + random.nextInt(bc - fc); + final int g = fc + random.nextInt(bc - fc); + final int b = fc + random.nextInt(bc - fc); + + return new Color(r, g, b); + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/RedisUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/RedisUtil.java new file mode 100644 index 0000000..a690178 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/RedisUtil.java @@ -0,0 +1,652 @@ +package org.jeecg.common.util; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.*; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * redis 工具类 + * @Author Scott + * + */ +@Component +public class RedisUtil { + + @Autowired + private RedisTemplate redisTemplate; + @Autowired + private StringRedisTemplate stringRedisTemplate; + + /** + * 指定缓存失效时间 + * + * @param key 键 + * @param time 时间(秒) + * @return + */ + public boolean expire(String key, long time) { + try { + if (time > 0) { + redisTemplate.expire(key, time, TimeUnit.SECONDS); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 根据key 获取过期时间 + * + * @param key 键 不能为null + * @return 时间(秒) 返回0代表为永久有效 + */ + public long getExpire(String key) { + return redisTemplate.getExpire(key, TimeUnit.SECONDS); + } + + /** + * 判断key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public boolean hasKey(String key) { + try { + return redisTemplate.hasKey(key); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 删除缓存 + * + * @param key 可以传一个值 或多个 + */ + @SuppressWarnings("unchecked") + public void del(String... key) { + if (key != null && key.length > 0) { + if (key.length == 1) { + redisTemplate.delete(key[0]); + } else { + redisTemplate.delete((Collection) CollectionUtils.arrayToList(key)); + } + } + } + + // ============================String============================= + /** + * 普通缓存获取 + * + * @param key 键 + * @return 值 + */ + public Object get(String key) { + return key == null ? null : redisTemplate.opsForValue().get(key); + } + + /** + * 普通缓存放入 + * + * @param key 键 + * @param value 值 + * @return true成功 false失败 + */ + public boolean set(String key, Object value) { + try { + redisTemplate.opsForValue().set(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + } + + /** + * 普通缓存放入并设置时间 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 + * @return true成功 false 失败 + */ + public boolean set(String key, Object value, long time) { + try { + if (time > 0) { + redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); + } else { + set(key, value); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 递增 + * + * @param key 键 + * @param delta 要增加几(大于0) + * @return + */ + public long incr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("递增因子必须大于0"); + } + return redisTemplate.opsForValue().increment(key, delta); + } + + /** + * 递减 + * + * @param key 键 + * @param delta 要减少几(小于0) + * @return + */ + public long decr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("递减因子必须大于0"); + } + return redisTemplate.opsForValue().increment(key, -delta); + } + + // ================================Map================================= + /** + * HashGet + * + * @param key 键 不能为null + * @param item 项 不能为null + * @return 值 + */ + public Object hget(String key, String item) { + return redisTemplate.opsForHash().get(key, item); + } + + /** + * 获取hashKey对应的所有键值 + * + * @param key 键 + * @return 对应的多个键值 + */ + public Map hmget(String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * HashSet + * + * @param key 键 + * @param map 对应多个键值 + * @return true 成功 false 失败 + */ + public boolean hmset(String key, Map map) { + try { + redisTemplate.opsForHash().putAll(key, map); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * HashSet 并设置时间 + * + * @param key 键 + * @param map 对应多个键值 + * @param time 时间(秒) + * @return true成功 false失败 + */ + public boolean hmset(String key, Map map, long time) { + try { + redisTemplate.opsForHash().putAll(key, map); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @return true 成功 false失败 + */ + public boolean hset(String key, String item, Object value) { + try { + redisTemplate.opsForHash().put(key, item, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 + * @return true 成功 false失败 + */ + public boolean hset(String key, String item, Object value, long time) { + try { + redisTemplate.opsForHash().put(key, item, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 删除hash表中的值 + * + * @param key 键 不能为null + * @param item 项 可以使多个 不能为null + */ + public void hdel(String key, Object... item) { + redisTemplate.opsForHash().delete(key, item); + } + + /** + * 判断hash表中是否有该项的值 + * + * @param key 键 不能为null + * @param item 项 不能为null + * @return true 存在 false不存在 + */ + public boolean hHasKey(String key, String item) { + return redisTemplate.opsForHash().hasKey(key, item); + } + + /** + * hash递增 如果不存在,就会创建一个 并把新增后的值返回 + * + * @param key 键 + * @param item 项 + * @param by 要增加几(大于0) + * @return + */ + public double hincr(String key, String item, double by) { + return redisTemplate.opsForHash().increment(key, item, by); + } + + /** + * hash递减 + * + * @param key 键 + * @param item 项 + * @param by 要减少记(小于0) + * @return + */ + public double hdecr(String key, String item, double by) { + return redisTemplate.opsForHash().increment(key, item, -by); + } + + // ============================set============================= + /** + * 根据key获取Set中的所有值 + * + * @param key 键 + * @return + */ + public Set sGet(String key) { + try { + return redisTemplate.opsForSet().members(key); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 根据value从一个set中查询,是否存在 + * + * @param key 键 + * @param value 值 + * @return true 存在 false不存在 + */ + public boolean sHasKey(String key, Object value) { + try { + return redisTemplate.opsForSet().isMember(key, value); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将数据放入set缓存 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 成功个数 + */ + public long sSet(String key, Object... values) { + try { + return redisTemplate.opsForSet().add(key, values); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 将set数据放入缓存 + * + * @param key 键 + * @param time 时间(秒) + * @param values 值 可以是多个 + * @return 成功个数 + */ + public long sSetAndTime(String key, long time, Object... values) { + try { + Long count = redisTemplate.opsForSet().add(key, values); + if (time > 0) { + expire(key, time); + } + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 获取set缓存的长度 + * + * @param key 键 + * @return + */ + public long sGetSetSize(String key) { + try { + return redisTemplate.opsForSet().size(key); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 移除值为value的 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 移除的个数 + */ + public long setRemove(String key, Object... values) { + try { + Long count = redisTemplate.opsForSet().remove(key, values); + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + // ===============================list================================= + + /** + * 获取list缓存的内容 + * + * @param key 键 + * @param start 开始 + * @param end 结束 0 到 -1代表所有值 + * @return + */ + public List lGet(String key, long start, long end) { + try { + return redisTemplate.opsForList().range(key, start, end); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 截取集合元素长度,保留长度内的数据。 + * + * @param key 键 + * @param start 开始 下标 + * @param end 结束 -1代表 start 前的所有值 + * @return 0 到 -1代表所有值 + */ + public void lTrim(String key, long start, long end) { + try { + redisTemplate.opsForList().trim(key, start, end);; + } catch (Exception e) { + e.printStackTrace(); + } + } + + + /** + * 获取list缓存的长度 + * + * @param key 键 + * @return + */ + public long lGetListSize(String key) { + try { + return redisTemplate.opsForList().size(key); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 通过索引 获取list中的值 + * + * @param key 键 + * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 + * @return + */ + public Object lGetIndex(String key, long index) { + try { + return redisTemplate.opsForList().index(key, index); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @return + */ + public boolean lSet(String key, Object value) { + try { + redisTemplate.opsForList().rightPush(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + * @return + */ + public boolean lSet(String key, Object value, long time) { + try { + redisTemplate.opsForList().rightPush(key, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @return + */ + public boolean lSet(String key, List value) { + try { + redisTemplate.opsForList().rightPushAll(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + * @return + */ + public boolean lSet(String key, List value, long time) { + try { + redisTemplate.opsForList().rightPushAll(key, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 根据索引修改list中的某条数据 + * + * @param key 键 + * @param index 索引 + * @param value 值 + * @return + */ + public boolean lUpdateIndex(String key, long index, Object value) { + try { + redisTemplate.opsForList().set(key, index, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 移除N个值为value + * + * @param key 键 + * @param count 移除多少个 + * @param value 值 + * @return 移除的个数 + */ + public long lRemove(String key, long count, Object value) { + try { + Long remove = redisTemplate.opsForList().remove(key, count, value); + return remove; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 获取指定前缀的一系列key + * 使用scan命令代替keys, Redis是单线程处理,keys命令在KEY数量较多时, + * 操作效率极低【时间复杂度为O(N)】,该命令一旦执行会严重阻塞线上其它命令的正常请求 + * @param keyPrefix + * @return + */ + private Set keys(String keyPrefix) { + String realKey = keyPrefix + "*"; + + try { + return redisTemplate.execute((RedisCallback>) connection -> { + Set binaryKeys = new HashSet<>(); + Cursor cursor = connection.scan(ScanOptions.scanOptions().match(realKey).count(Integer.MAX_VALUE).build()); + while (cursor.hasNext()) { + binaryKeys.add(new String(cursor.next())); + } + + return binaryKeys; + }); + } catch (Throwable e) { + e.printStackTrace(); + } + + return null; + } + + /** + * 删除指定前缀的一系列key + * @param keyPrefix + */ + public void removeAll(String keyPrefix) { + try { + Set keys = keys(keyPrefix); + redisTemplate.delete(keys); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + public Long releaseRedisLock(String lockKey, String lockValue) { + // Lua脚本:只有锁的值匹配时才删除 + String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] " + + "then return redis.call('del', KEYS[1]) " + + "else return 0 end"; + + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptText(luaScript); + redisScript.setResultType(Long.class); + + // 执行Lua脚本(原子操作) + + Long result = stringRedisTemplate.execute( + redisScript, + Collections.singletonList(lockKey), + lockValue + ); + return result; + } + + public static void main(String[] args) { + RedisUtil redisUtil=new RedisUtil(); + redisUtil.get("111"); + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/ReflectHelper.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/ReflectHelper.java new file mode 100644 index 0000000..3e843ee --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/ReflectHelper.java @@ -0,0 +1,255 @@ +package org.jeecg.common.util; + +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.*; +import java.util.Map.Entry; +import java.util.regex.Pattern; + +/** + * @author 张代浩 + * @desc 通过反射来动态调用get 和 set 方法 + */ +@Slf4j +public class ReflectHelper { + + private Class cls; + + /** + * 传过来的对象 + */ + private Object obj; + + /** + * 存放get方法 + */ + private Hashtable getMethods = null; + /** + * 存放set方法 + */ + private Hashtable setMethods = null; + + /** + * 定义构造方法 -- 一般来说是个pojo + * + * @param o 目标对象 + */ + public ReflectHelper(Object o) { + obj = o; + initMethods(); + } + + /** + * @desc 初始化 + */ + public void initMethods() { + getMethods = new Hashtable(); + setMethods = new Hashtable(); + cls = obj.getClass(); + Method[] methods = cls.getMethods(); + // 定义正则表达式,从方法中过滤出getter / setter 函数. + String gs = "get(\\w+)"; + Pattern getM = Pattern.compile(gs); + String ss = "set(\\w+)"; + Pattern setM = Pattern.compile(ss); + // 把方法中的"set" 或者 "get" 去掉 + String rapl = "$1"; + String param; + for (int i = 0; i < methods.length; ++i) { + Method m = methods[i]; + String methodName = m.getName(); + if (Pattern.matches(gs, methodName)) { + param = getM.matcher(methodName).replaceAll(rapl).toLowerCase(); + getMethods.put(param, m); + } else if (Pattern.matches(ss, methodName)) { + param = setM.matcher(methodName).replaceAll(rapl).toLowerCase(); + setMethods.put(param, m); + } else { + // logger.info(methodName + " 不是getter,setter方法!"); + } + } + } + + /** + * @desc 调用set方法 + */ + public boolean setMethodValue(String property, Object object) { + Method m = setMethods.get(property.toLowerCase()); + if (m != null) { + try { + // 调用目标类的setter函数 + m.invoke(obj, object); + return true; + } catch (Exception ex) { + log.info("invoke getter on " + property + " error: " + ex.toString()); + return false; + } + } + return false; + } + + /** + * @desc 调用set方法 + */ + public Object getMethodValue(String property) { + Object value = null; + Method m = getMethods.get(property.toLowerCase()); + if (m != null) { + try { + /* + * 调用obj类的setter函数 + */ + value = m.invoke(obj, new Object[]{}); + + } catch (Exception ex) { + log.info("invoke getter on " + property + " error: " + ex.toString()); + } + } + return value; + } + + /** + * 把map中的内容全部注入到obj中 + * + * @param data + * @return + */ + public Object setAll(Map data) { + if (data == null || data.keySet().size() <= 0) { + return null; + } + for (Entry entry : data.entrySet()) { + this.setMethodValue(entry.getKey(), entry.getValue()); + } + return obj; + } + + /** + * 把map中的内容全部注入到obj中 + * + * @param o + * @param data + * @return + */ + public static Object setAll(Object o, Map data) { + ReflectHelper reflectHelper = new ReflectHelper(o); + reflectHelper.setAll(data); + return o; + } + + /** + * 把map中的内容全部注入到新实例中 + * + * @param clazz + * @param data + * @return + */ + @SuppressWarnings("unchecked") + public static T setAll(Class clazz, Map data) { + T o = null; + try { + o = clazz.newInstance(); + } catch (Exception e) { + e.printStackTrace(); + o = null; + return o; + } + return (T) setAll(o, data); + } + + /** + * 根据传入的class将mapList转换为实体类list + * + * @param mapist + * @param clazz + * @return + */ + public static List transList2Entrys(List> mapist, Class clazz) { + List list = new ArrayList(); + if (mapist != null && mapist.size() > 0) { + for (Map data : mapist) { + list.add(ReflectHelper.setAll(clazz, data)); + } + } + return list; + } + + /** + * 根据属性名获取属性值 + */ + public static Object getFieldValueByName(String fieldName, Object o) { + try { + String firstLetter = fieldName.substring(0, 1).toUpperCase(); + String getter = "get" + firstLetter + fieldName.substring(1); + Method method = o.getClass().getMethod(getter, new Class[]{}); + Object value = method.invoke(o, new Object[]{}); + return value; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 获取属性值 + */ + public static Object getFieldVal(String fieldName, Object o) { + try { + // 暴力反射获取属性 + Field filed = o.getClass().getDeclaredField(fieldName); + // 设置反射时取消Java的访问检查,暴力访问 + filed.setAccessible(true); + Object val = filed.get(o); + return val; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 获取属性名数组 + */ + public static String[] getFiledName(Object o) { + Field[] fields = o.getClass().getDeclaredFields(); + String[] fieldNames = new String[fields.length]; + for (int i = 0; i < fields.length; i++) { + //log.info(fields[i].getType()); + fieldNames[i] = fields[i].getName(); + } + return fieldNames; + } + + /** + * 获取属性类型(type),属性名(name),属性值(value)的map组成的list + */ + public static List getFiledsInfo(Object o) { + Field[] fields = o.getClass().getDeclaredFields(); + String[] fieldNames = new String[fields.length]; + List list = new ArrayList(); + Map infoMap = null; + for (int i = 0; i < fields.length; i++) { + infoMap = new HashMap<>(5); + infoMap.put("type", fields[i].getType().toString()); + infoMap.put("name", fields[i].getName()); + infoMap.put("value", getFieldValueByName(fields[i].getName(), o)); + list.add(infoMap); + } + return list; + } + + /** + * 获取对象的所有属性值,返回一个对象数组 + */ + public static Object[] getFiledValues(Object o) { + String[] fieldNames = getFiledName(o); + Object[] value = new Object[fieldNames.length]; + for (int i = 0; i < fieldNames.length; i++) { + value[i] = getFieldValueByName(fieldNames[i], o); + } + return value; + } + +} \ No newline at end of file diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/RestDesformUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/RestDesformUtil.java new file mode 100644 index 0000000..d684346 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/RestDesformUtil.java @@ -0,0 +1,121 @@ +package org.jeecg.common.util; + +import com.alibaba.fastjson.JSONObject; +import org.jeecg.common.api.vo.Result; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; + +/** + * 通过 RESTful 风格的接口操纵 desform 里的数据 + * + * @author sunjianlei + */ +public class RestDesformUtil { + + private static String domain = null; + private static String path = null; + + static { + domain = SpringContextUtils.getDomain(); + path = oConvertUtils.getString(SpringContextUtils.getApplicationContext().getEnvironment().getProperty("server.servlet.context-path")); + } + + /** + * 查询数据 + * + * @param desformCode + * @param dataId + * @param token + * @return + */ + public static Result queryOne(String desformCode, String dataId, String token) { + String url = getBaseUrl(desformCode, dataId).toString(); + HttpHeaders headers = getHeaders(token); + ResponseEntity result = RestUtil.request(url, HttpMethod.GET, headers, null, null, JSONObject.class); + return packageReturn(result); + } + + /** + * 新增数据 + * + * @param desformCode + * @param formData + * @param token + * @return + */ + public static Result addOne(String desformCode, JSONObject formData, String token) { + return addOrEditOne(desformCode, formData, token, HttpMethod.POST); + } + + /** + * 修改数据 + * + * @param desformCode + * @param formData + * @param token + * @return + */ + public static Result editOne(String desformCode, JSONObject formData, String token) { + return addOrEditOne(desformCode, formData, token, HttpMethod.PUT); + } + + private static Result addOrEditOne(String desformCode, JSONObject formData, String token, HttpMethod method) { + String url = getBaseUrl(desformCode).toString(); + HttpHeaders headers = getHeaders(token); + ResponseEntity result = RestUtil.request(url, method, headers, null, formData, JSONObject.class); + return packageReturn(result); + } + + /** + * 删除数据 + * + * @param desformCode + * @param dataId + * @param token + * @return + */ + public static Result removeOne(String desformCode, String dataId, String token) { + String url = getBaseUrl(desformCode, dataId).toString(); + HttpHeaders headers = getHeaders(token); + ResponseEntity result = RestUtil.request(url, HttpMethod.DELETE, headers, null, null, JSONObject.class); + return packageReturn(result); + } + + private static Result packageReturn(ResponseEntity result) { + if (result.getBody() != null) { + return result.getBody().toJavaObject(Result.class); + } + return Result.error("操作失败"); + } + + private static StringBuilder getBaseUrl() { + StringBuilder builder = new StringBuilder(domain).append(path); + builder.append("/desform/api"); + return builder; + } + + private static StringBuilder getBaseUrl(String desformCode, String dataId) { + StringBuilder builder = getBaseUrl(); + builder.append("/").append(desformCode); + if (dataId != null) { + builder.append("/").append(dataId); + } + return builder; + } + + private static StringBuilder getBaseUrl(String desformCode) { + return getBaseUrl(desformCode, null); + } + + private static HttpHeaders getHeaders(String token) { + HttpHeaders headers = new HttpHeaders(); + String mediaType = MediaType.APPLICATION_JSON_UTF8_VALUE; + headers.setContentType(MediaType.parseMediaType(mediaType)); + headers.set("Accept", mediaType); + headers.set("X-Access-Token", token); + return headers; + } + +} \ No newline at end of file diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/RestUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/RestUtil.java new file mode 100644 index 0000000..0fb4606 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/RestUtil.java @@ -0,0 +1,264 @@ +package org.jeecg.common.util; + +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.*; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.web.client.RestTemplate; + +import java.nio.charset.StandardCharsets; +import java.util.Iterator; +import java.util.Map; + +/** + * 调用 Restful 接口 Util + * + * @author sunjianlei + */ +@Slf4j +public class RestUtil { + + private static String domain = null; + + public static String getDomain() { + if (domain == null) { + domain = SpringContextUtils.getDomain(); + // issues/2959 + // 微服务版集成企业微信单点登录 + // 因为微服务版没有端口号,导致 SpringContextUtils.getDomain() 方法获取的域名的端口号变成了:-1所以出问题了,只需要把这个-1给去掉就可以了。 + String port=":-1"; + if (domain.endsWith(port)) { + domain = domain.substring(0, domain.length() - 3); + } + } + return domain; + } + + public static String path = null; + + public static String getPath() { + if (path == null) { + path = SpringContextUtils.getApplicationContext().getEnvironment().getProperty("server.servlet.context-path"); + } + return oConvertUtils.getString(path); + } + + public static String getBaseUrl() { + String basepath = getDomain() + getPath(); + log.info(" RestUtil.getBaseUrl: " + basepath); + return basepath; + } + + /** + * RestAPI 调用器 + */ + private final static RestTemplate RT; + + static { + SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); + requestFactory.setConnectTimeout(3000); + requestFactory.setReadTimeout(3000); + RT = new RestTemplate(requestFactory); + // 解决乱码问题 + RT.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); + } + + public static RestTemplate getRestTemplate() { + return RT; + } + + /** + * 发送 get 请求 + */ + public static JSONObject get(String url) { + return getNative(url, null, null).getBody(); + } + + /** + * 发送 get 请求 + */ + public static JSONObject get(String url, JSONObject variables) { + return getNative(url, variables, null).getBody(); + } + + /** + * 发送 get 请求 + */ + public static JSONObject get(String url, JSONObject variables, JSONObject params) { + return getNative(url, variables, params).getBody(); + } + + /** + * 发送 get 请求,返回原生 ResponseEntity 对象 + */ + public static ResponseEntity getNative(String url, JSONObject variables, JSONObject params) { + return request(url, HttpMethod.GET, variables, params); + } + + /** + * 发送 Post 请求 + */ + public static JSONObject post(String url) { + return postNative(url, null, null).getBody(); + } + + /** + * 发送 Post 请求 + */ + public static JSONObject post(String url, JSONObject params) { + return postNative(url, null, params).getBody(); + } + + /** + * 发送 Post 请求 + */ + public static JSONObject post(String url, JSONObject variables, JSONObject params) { + return postNative(url, variables, params).getBody(); + } + + /** + * 发送 POST 请求,返回原生 ResponseEntity 对象 + */ + public static ResponseEntity postNative(String url, JSONObject variables, JSONObject params) { + return request(url, HttpMethod.POST, variables, params); + } + + /** + * 发送 put 请求 + */ + public static JSONObject put(String url) { + return putNative(url, null, null).getBody(); + } + + /** + * 发送 put 请求 + */ + public static JSONObject put(String url, JSONObject params) { + return putNative(url, null, params).getBody(); + } + + /** + * 发送 put 请求 + */ + public static JSONObject put(String url, JSONObject variables, JSONObject params) { + return putNative(url, variables, params).getBody(); + } + + /** + * 发送 put 请求,返回原生 ResponseEntity 对象 + */ + public static ResponseEntity putNative(String url, JSONObject variables, JSONObject params) { + return request(url, HttpMethod.PUT, variables, params); + } + + /** + * 发送 delete 请求 + */ + public static JSONObject delete(String url) { + return deleteNative(url, null, null).getBody(); + } + + /** + * 发送 delete 请求 + */ + public static JSONObject delete(String url, JSONObject variables, JSONObject params) { + return deleteNative(url, variables, params).getBody(); + } + + /** + * 发送 delete 请求,返回原生 ResponseEntity 对象 + */ + public static ResponseEntity deleteNative(String url, JSONObject variables, JSONObject params) { + return request(url, HttpMethod.DELETE, null, variables, params, JSONObject.class); + } + + /** + * 发送请求 + */ + public static ResponseEntity request(String url, HttpMethod method, JSONObject variables, JSONObject params) { + return request(url, method, getHeaderApplicationJson(), variables, params, JSONObject.class); + } + + /** + * 发送请求 + * + * @param url 请求地址 + * @param method 请求方式 + * @param headers 请求头 可空 + * @param variables 请求url参数 可空 + * @param params 请求body参数 可空 + * @param responseType 返回类型 + * @return ResponseEntity + */ + public static ResponseEntity request(String url, HttpMethod method, HttpHeaders headers, JSONObject variables, Object params, Class responseType) { + log.info(" RestUtil --- request --- url = "+ url); + if (StringUtils.isEmpty(url)) { + throw new RuntimeException("url 不能为空"); + } + if (method == null) { + throw new RuntimeException("method 不能为空"); + } + if (headers == null) { + headers = new HttpHeaders(); + } + // 请求体 + String body = ""; + if (params != null) { + if (params instanceof JSONObject) { + body = ((JSONObject) params).toJSONString(); + + } else { + body = params.toString(); + } + } + // 拼接 url 参数 + if (variables != null && !variables.isEmpty()) { + url += ("?" + asUrlVariables(variables)); + } + // 发送请求 + HttpEntity request = new HttpEntity<>(body, headers); + return RT.exchange(url, method, request, responseType); + } + + /** + * 获取JSON请求头 + */ + public static HttpHeaders getHeaderApplicationJson() { + return getHeader(MediaType.APPLICATION_JSON_UTF8_VALUE); + } + + /** + * 获取请求头 + */ + public static HttpHeaders getHeader(String mediaType) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.parseMediaType(mediaType)); + headers.add("Accept", mediaType); + return headers; + } + + /** + * 将 JSONObject 转为 a=1&b=2&c=3...&n=n 的形式 + */ + public static String asUrlVariables(JSONObject variables) { + Map source = variables.getInnerMap(); + Iterator it = source.keySet().iterator(); + StringBuilder urlVariables = new StringBuilder(); + while (it.hasNext()) { + String key = it.next(); + String value = ""; + Object object = source.get(key); + if (object != null) { + if (!StringUtils.isEmpty(object.toString())) { + value = object.toString(); + } + } + urlVariables.append("&").append(key).append("=").append(value); + } + // 去掉第一个& + return urlVariables.substring(1); + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SpringContextUtils.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SpringContextUtils.java new file mode 100644 index 0000000..cd3d878 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SpringContextUtils.java @@ -0,0 +1,105 @@ +package org.jeecg.common.util; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.jeecg.common.constant.ServiceNameConstants; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +/** + * @Description: spring上下文工具类 + * @author: jeecg-boot + */ +@Component +public class SpringContextUtils implements ApplicationContextAware { + + /** + * 上下文对象实例 + */ + private static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + SpringContextUtils.applicationContext = applicationContext; + } + + /** + * 获取applicationContext + * + * @return + */ + public static ApplicationContext getApplicationContext() { + return applicationContext; + } + + /** + * 获取HttpServletRequest + */ + public static HttpServletRequest getHttpServletRequest() { + return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + } + /** + * 获取HttpServletResponse + */ + public static HttpServletResponse getHttpServletResponse() { + return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); + } + + /** + * 获取项目根路径 basePath + */ + public static String getDomain(){ + HttpServletRequest request = getHttpServletRequest(); + StringBuffer url = request.getRequestURL(); + //微服务情况下,获取gateway的basePath + String basePath = request.getHeader(ServiceNameConstants.X_GATEWAY_BASE_PATH); + if(oConvertUtils.isNotEmpty(basePath)){ + return basePath; + }else{ + return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString(); + } + } + + public static String getOrigin(){ + HttpServletRequest request = getHttpServletRequest(); + return request.getHeader("Origin"); + } + + /** + * 通过name获取 Bean. + * + * @param name + * @return + */ + public static Object getBean(String name) { + return getApplicationContext().getBean(name); + } + + /** + * 通过class获取Bean. + * + * @param clazz + * @param + * @return + */ + public static T getBean(Class clazz) { + return getApplicationContext().getBean(clazz); + } + + /** + * 通过name,以及Clazz返回指定的Bean + * + * @param name + * @param clazz + * @param + * @return + */ + public static T getBean(String name, Class clazz) { + return getApplicationContext().getBean(name, clazz); + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SqlInjectionUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SqlInjectionUtil.java new file mode 100644 index 0000000..99c4caf --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SqlInjectionUtil.java @@ -0,0 +1,259 @@ +package org.jeecg.common.util; + +import cn.hutool.crypto.SecureUtil; +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.exception.JeecgBootException; +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Field; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * sql注入处理工具类 + * + * @author zhoujf + */ +@Slf4j +public class SqlInjectionUtil { + /** + * sign 用于表字典加签的盐值【SQL漏洞】 + * (上线修改值 20200501,同步修改前端的盐值) + */ + private final static String TABLE_DICT_SIGN_SALT = "20200501"; + private final static String XSS_STR = "and |exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|or |+|user()"; + + /** + * 正则 user() 匹配更严谨 + */ + private final static String REGULAR_EXPRE_USER = "user[\\s]*\\([\\s]*\\)"; + /**正则 show tables*/ + private final static String SHOW_TABLES = "show\\s+tables"; + + /** + * 针对表字典进行额外的sign签名校验(增加安全机制) + * @param dictCode: + * @param sign: + * @param request: + * @Return: void + */ + public static void checkDictTableSign(String dictCode, String sign, HttpServletRequest request) { + //表字典SQL注入漏洞,签名校验 + String accessToken = request.getHeader("X-Access-Token"); + String signStr = dictCode + SqlInjectionUtil.TABLE_DICT_SIGN_SALT + accessToken; + String javaSign = SecureUtil.md5(signStr); + if (!javaSign.equals(sign)) { + log.error("表字典,SQL注入漏洞签名校验失败 :" + sign + "!=" + javaSign+ ",dictCode=" + dictCode); + throw new JeecgBootException("无权限访问!"); + } + log.info(" 表字典,SQL注入漏洞签名校验成功!sign=" + sign + ",dictCode=" + dictCode); + } + + /** + * sql注入过滤处理,遇到注入关键字抛异常 + * @param value + */ + public static void filterContent(String value) { + filterContent(value, null); + } + + /** + * sql注入过滤处理,遇到注入关键字抛异常 + * + * @param value + * @return + */ + public static void filterContent(String value, String customXssString) { + if (value == null || "".equals(value)) { + return; + } + // 统一转为小写 + value = value.toLowerCase(); + //SQL注入检测存在绕过风险 https://gitee.com/jeecg/jeecg-boot/issues/I4NZGE + value = value.replaceAll("/\\*.*\\*/",""); + + String[] xssArr = XSS_STR.split("\\|"); + for (int i = 0; i < xssArr.length; i++) { + if (value.indexOf(xssArr[i]) > -1) { + log.error("请注意,存在SQL注入关键词---> {}", xssArr[i]); + log.error("请注意,值可能存在SQL注入风险!---> {}", value); + throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value); + } + } + //update-begin-author:taoyan date:2022-7-13 for: 除了XSS_STR这些提前设置好的,还需要额外的校验比如 单引号 + if (customXssString != null) { + String[] xssArr2 = customXssString.split("\\|"); + for (int i = 0; i < xssArr2.length; i++) { + if (value.indexOf(xssArr2[i]) > -1) { + log.error("请注意,存在SQL注入关键词---> {}", xssArr2[i]); + log.error("请注意,值可能存在SQL注入风险!---> {}", value); + throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value); + } + } + } + //update-end-author:taoyan date:2022-7-13 for: 除了XSS_STR这些提前设置好的,还需要额外的校验比如 单引号 + if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){ + throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value); + } + return; + } + + /** + * sql注入过滤处理,遇到注入关键字抛异常 + * @param values + */ + public static void filterContent(String[] values) { + filterContent(values, null); + } + + /** + * sql注入过滤处理,遇到注入关键字抛异常 + * + * @param values + * @return + */ + public static void filterContent(String[] values, String customXssString) { + String[] xssArr = XSS_STR.split("\\|"); + for (String value : values) { + if (value == null || "".equals(value)) { + return; + } + // 统一转为小写 + value = value.toLowerCase(); + //SQL注入检测存在绕过风险 https://gitee.com/jeecg/jeecg-boot/issues/I4NZGE + value = value.replaceAll("/\\*.*\\*/",""); + + for (int i = 0; i < xssArr.length; i++) { + if (value.indexOf(xssArr[i]) > -1) { + log.error("请注意,存在SQL注入关键词---> {}", xssArr[i]); + log.error("请注意,值可能存在SQL注入风险!---> {}", value); + throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value); + } + } + //update-begin-author:taoyan date:2022-7-13 for: 除了XSS_STR这些提前设置好的,还需要额外的校验比如 单引号 + if (customXssString != null) { + String[] xssArr2 = customXssString.split("\\|"); + for (int i = 0; i < xssArr2.length; i++) { + if (value.indexOf(xssArr2[i]) > -1) { + log.error("请注意,存在SQL注入关键词---> {}", xssArr2[i]); + log.error("请注意,值可能存在SQL注入风险!---> {}", value); + throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value); + } + } + } + //update-end-author:taoyan date:2022-7-13 for: 除了XSS_STR这些提前设置好的,还需要额外的校验比如 单引号 + if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){ + throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value); + } + } + return; + } + + /** + * 【提醒:不通用】 + * 仅用于字典条件SQL参数,注入过滤 + * + * @param value + * @return + */ + //@Deprecated + public static void specialFilterContentForDictSql(String value) { + String specialXssStr = " exec | insert | select | delete | update | drop | count | chr | mid | master | truncate | char | declare |;|+|user()"; + String[] xssArr = specialXssStr.split("\\|"); + if (value == null || "".equals(value)) { + return; + } + // 统一转为小写 + value = value.toLowerCase(); + //SQL注入检测存在绕过风险 https://gitee.com/jeecg/jeecg-boot/issues/I4NZGE + value = value.replaceAll("/\\*.*\\*/",""); + + for (int i = 0; i < xssArr.length; i++) { + if (value.indexOf(xssArr[i]) > -1 || value.startsWith(xssArr[i].trim())) { + log.error("请注意,存在SQL注入关键词---> {}", xssArr[i]); + log.error("请注意,值可能存在SQL注入风险!---> {}", value); + throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value); + } + } + if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){ + throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value); + } + return; + } + + + /** + * 【提醒:不通用】 + * 仅用于Online报表SQL解析,注入过滤 + * @param value + * @return + */ + //@Deprecated + public static void specialFilterContentForOnlineReport(String value) { + String specialXssStr = " exec | insert | delete | update | drop | chr | mid | master | truncate | char | declare |user()"; + String[] xssArr = specialXssStr.split("\\|"); + if (value == null || "".equals(value)) { + return; + } + // 统一转为小写 + value = value.toLowerCase(); + //SQL注入检测存在绕过风险 https://gitee.com/jeecg/jeecg-boot/issues/I4NZGE + value = value.replaceAll("/\\*.*\\*/",""); + + for (int i = 0; i < xssArr.length; i++) { + if (value.indexOf(xssArr[i]) > -1 || value.startsWith(xssArr[i].trim())) { + log.error("请注意,存在SQL注入关键词---> {}", xssArr[i]); + log.error("请注意,值可能存在SQL注入风险!---> {}", value); + throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value); + } + } + + if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){ + throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value); + } + return; + } + + + /** + * 判断给定的字段是不是类中的属性 + * @param field 字段名 + * @param clazz 类对象 + * @return + */ + public static boolean isClassField(String field, Class clazz){ + Field[] fields = clazz.getDeclaredFields(); + for(int i=0;i fieldSet, Class clazz){ + Field[] fields = clazz.getDeclaredFields(); + for(String field: fieldSet){ + boolean exist = false; + for(int i=0;i>> 8); + + private final static String format(int intval) { + String formatted = Integer.toHexString(intval); + StringBuilder buf = new StringBuilder("00000000"); + buf.replace(8 - formatted.length(), 8, formatted); + return buf.toString(); + } + + private final static String format(short shortval) { + String formatted = Integer.toHexString(shortval); + StringBuilder buf = new StringBuilder("0000"); + buf.replace(4 - formatted.length(), 4, formatted); + return buf.toString(); + } + + private final static int getJvm() { + return JVM; + } + + private final static short getCount() { + synchronized (UUIDGenerator.class) { + if (counter < 0) { + counter = 0; + } + return counter++; + } + } + + /** + * Unique in a local network + */ + private final static int getIp() { + return IP; + } + + /** + * Unique down to millisecond + */ + private final static short getHiTime() { + return (short) (System.currentTimeMillis() >>> 32); + } + + private final static int getLoTime() { + return (int) System.currentTimeMillis(); + } + + private final static int toInt(byte[] bytes) { + int result = 0; + int length = 4; + for (int i = 0; i < length; i++) { + result = (result << 8) - Byte.MIN_VALUE + (int) bytes[i]; + } + return result; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/XssUtils.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/XssUtils.java new file mode 100644 index 0000000..0e47ba8 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/XssUtils.java @@ -0,0 +1,93 @@ +package org.jeecg.common.util; + +import org.jeecg.common.exception.JeecgBootException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @Description: 工具类XSSUtils,现在的做法是替换成空字符,CSDN的是进行转义,比如文字开头的"<"转成< + * @author: lsq + * @date: 2021年07月26日 19:13 + */ +public class XssUtils { + + private static final Logger log = LoggerFactory.getLogger(XssUtils.class); + + private static final Pattern TIME_PATTERNS = + Pattern.compile("^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$", Pattern.CASE_INSENSITIVE); + private static Pattern pathPattern = Pattern.compile("[\\s\\\\.:?<>|]"); + + private static final Pattern[] PATTERNS = new Pattern[]{ + //Script fragments + Pattern.compile("", Pattern.CASE_INSENSITIVE), + //src='...' +// Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), + //script tags + Pattern.compile("", Pattern.CASE_INSENSITIVE), + Pattern.compile("", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), + //eval(...) + Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), + //expression(...) + Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), + //javascript:... + Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE), + //vbscript:... + Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE), + //onload(...)=... + Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), + }; + + public static String scriptXss(String value) { + if (value != null) { + value = value.trim(); + //替换空格先去掉,有风险 + /*if (!JSONUtil.isTypeJSON(value)) { + if (!TIME_PATTERNS.matcher(value).find()) { + value = value.replaceAll(" ", ""); + } + }*/ + for (Pattern scriptPattern : PATTERNS) { + Matcher matcher = scriptPattern.matcher(value); + if (matcher.find()) { + log.warn("XSS [{}] WARNING: {}", scriptPattern.pattern(), value); + value = matcher.replaceAll(""); + } + } + return value; + } + return ""; + } + + public static String checkTraversal(String path) { + if (path == null || path.isEmpty()) { + return ""; + } + Path safeDir = Paths.get("/safety/check").normalize(); + try { + Files.createDirectories(safeDir); + } catch (IOException e) { + throw new JeecgBootException("路径校验异常"); + } + if (path.startsWith("/") || path.startsWith("\\")) { + path = path.substring(1); + } + Path normalizedPath = safeDir.resolve(path).normalize(); + if (!normalizedPath.startsWith(safeDir)) { + throw new JeecgBootException("非法的或不安全的请求参数"); + } else { + return path; + } + } +// +// public static void main(String[] args) { +// String s = scriptXss("javascript:eval()\\\\."); +// System.err.println("s======>" + s); +// } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/YouBianCodeUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/YouBianCodeUtil.java new file mode 100644 index 0000000..a08fa52 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/YouBianCodeUtil.java @@ -0,0 +1,176 @@ +package org.jeecg.common.util; + +import io.netty.util.internal.StringUtil; + +/** + * 流水号生成规则(按默认规则递增,数字从1-99开始递增,数字到99,递增字母;位数不够增加位数) + * A001 + * A001A002 + * @Author zhangdaihao + * + */ +public class YouBianCodeUtil { + + // 数字位数(默认生成3位的数字) + + /**代表数字位数*/ + private static final int NUM_LENGTH = 2; + + public static final int ZHANWEI_LENGTH = 1+ NUM_LENGTH; + + public static final char LETTER= 'Z'; + + /** + * 根据前一个code,获取同级下一个code + * 例如:当前最大code为D01A04,下一个code为:D01A05 + * + * @param code + * @return + */ + public static synchronized String getNextYouBianCode(String code) { + String newcode = ""; + if (oConvertUtils.isEmpty(code)) { + String zimu = "A"; + String num = getStrNum(1); + newcode = zimu + num; + } else { + String beforeCode = code.substring(0, code.length() - 1- NUM_LENGTH); + String afterCode = code.substring(code.length() - 1 - NUM_LENGTH,code.length()); + char afterCodeZimu = afterCode.substring(0, 1).charAt(0); + Integer afterCodeNum = Integer.parseInt(afterCode.substring(1)); +// org.jeecgframework.core.util.LogUtil.info(after_code); +// org.jeecgframework.core.util.LogUtil.info(after_code_zimu); +// org.jeecgframework.core.util.LogUtil.info(after_code_num); + + String nextNum = ""; + char nextZimu = 'A'; + // 先判断数字等于999*,则计数从1重新开始,递增 + if (afterCodeNum == getMaxNumByLength(NUM_LENGTH)) { + nextNum = getNextStrNum(0); + } else { + nextNum = getNextStrNum(afterCodeNum); + } + // 先判断数字等于999*,则字母从A重新开始,递增 + if(afterCodeNum == getMaxNumByLength(NUM_LENGTH)) { + nextZimu = getNextZiMu(afterCodeZimu); + }else{ + nextZimu = afterCodeZimu; + } + + // 例如Z99,下一个code就是Z99A01 + if (LETTER == afterCodeZimu && getMaxNumByLength(NUM_LENGTH) == afterCodeNum) { + newcode = code + (nextZimu + nextNum); + } else { + newcode = beforeCode + (nextZimu + nextNum); + } + } + return newcode; + + } + + /** + * 根据父亲code,获取下级的下一个code + * + * 例如:父亲CODE:A01 + * 当前CODE:A01B03 + * 获取的code:A01B04 + * + * @param parentCode 上级code + * @param localCode 同级code + * @return + */ + public static synchronized String getSubYouBianCode(String parentCode,String localCode) { + if(localCode!=null && localCode!=""){ + +// return parentCode + getNextYouBianCode(localCode); + return getNextYouBianCode(localCode); + + }else{ + parentCode = parentCode + "A"+ getNextStrNum(0); + } + return parentCode; + } + + + + /** + * 将数字前面位数补零 + * + * @param num + * @return + */ + private static String getNextStrNum(int num) { + return getStrNum(getNextNum(num)); + } + + /** + * 将数字前面位数补零 + * + * @param num + * @return + */ + private static String getStrNum(int num) { + String s = String.format("%0" + NUM_LENGTH + "d", num); + return s; + } + + /** + * 递增获取下个数字 + * + * @param num + * @return + */ + private static int getNextNum(int num) { + num++; + return num; + } + + /** + * 递增获取下个字母 + * + * @param zimu + * @return + */ + private static char getNextZiMu(char zimu) { + if (zimu == LETTER) { + return 'A'; + } + zimu++; + return zimu; + } + + /** + * 根据数字位数获取最大值 + * @param length + * @return + */ + private static int getMaxNumByLength(int length){ + if(length==0){ + return 0; + } + StringBuilder maxNum = new StringBuilder(); + for (int i=0;i dbSources = new HashMap<>(); + private static RedisTemplate redisTemplate; + + private static RedisTemplate getRedisTemplate() { + if (redisTemplate == null) { + redisTemplate = (RedisTemplate) SpringContextUtils.getBean("redisTemplate"); + } + return redisTemplate; + } + + /** + * 获取多数据源缓存 + * + * @param dbKey + * @return + */ + public static DynamicDataSourceModel getCacheDynamicDataSourceModel(String dbKey) { + String redisCacheKey = CacheConstant.SYS_DYNAMICDB_CACHE + dbKey; + if (getRedisTemplate().hasKey(redisCacheKey)) { + return (DynamicDataSourceModel) getRedisTemplate().opsForValue().get(redisCacheKey); + } + CommonAPI commonApi = SpringContextUtils.getBean(CommonAPI.class); + DynamicDataSourceModel dbSource = commonApi.getDynamicDbSourceByCode(dbKey); + if (dbSource != null) { + getRedisTemplate().opsForValue().set(redisCacheKey, dbSource); + } + return dbSource; + } + + public static DruidDataSource getCacheBasicDataSource(String dbKey) { + return dbSources.get(dbKey); + } + + /** + * put 数据源缓存 + * + * @param dbKey + * @param db + */ + public static void putCacheBasicDataSource(String dbKey, DruidDataSource db) { + dbSources.put(dbKey, db); + } + + /** + * 清空数据源缓存 + */ + public static void cleanAllCache() { + //关闭数据源连接 + for(Map.Entry entry : dbSources.entrySet()){ + String dbkey = entry.getKey(); + DruidDataSource druidDataSource = entry.getValue(); + if(druidDataSource!=null && druidDataSource.isEnable()){ + druidDataSource.close(); + } + //清空redis缓存 + getRedisTemplate().delete(CacheConstant.SYS_DYNAMICDB_CACHE + dbkey); + } + //清空缓存 + dbSources.clear(); + } + + public static void removeCache(String dbKey) { + //关闭数据源连接 + DruidDataSource druidDataSource = dbSources.get(dbKey); + if(druidDataSource!=null && druidDataSource.isEnable()){ + druidDataSource.close(); + } + //清空redis缓存 + getRedisTemplate().delete(CacheConstant.SYS_DYNAMICDB_CACHE + dbKey); + //清空缓存 + dbSources.remove(dbKey); + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/dynamic/db/DbTypeUtils.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/dynamic/db/DbTypeUtils.java new file mode 100644 index 0000000..96a0a08 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/dynamic/db/DbTypeUtils.java @@ -0,0 +1,122 @@ +package org.jeecg.common.util.dynamic.db; + +import com.baomidou.mybatisplus.annotation.DbType; +import org.jeecg.common.constant.DataBaseConstant; + +import java.util.HashMap; +import java.util.Map; + +/** + * 数据库类型判断 + * 【有些数据库引擎是一样的,以达到复用目的】 + * @author: jeecg-boot + */ +public class DbTypeUtils { + + public static Map dialectMap = new HashMap(); + static{ + dialectMap.put("mysql", "org.hibernate.dialect.MySQL5InnoDBDialect"); + // mariadb数据库 1 -- + dialectMap.put("mariadb", "org.hibernate.dialect.MariaDBDialect"); + //oracle数据库 1 + dialectMap.put("oracle", "org.hibernate.dialect.OracleDialect"); + // TODO 没找到不确定 + dialectMap.put("oracle12c", "org.hibernate.dialect.OracleDialect"); + // db2数据库 1xx + dialectMap.put("db2", "org.hibernate.dialect.DB2390Dialect"); + // H2数据库 + dialectMap.put("h2", "org.hibernate.dialect.HSQLDialect"); + // HSQL数据库 1 + dialectMap.put("hsql", "org.hibernate.dialect.HSQLDialect"); + //SQLite数据库 应用平台mobile + dialectMap.put("sqlite", "org.jeecg.modules.online.config.dialect.SQLiteDialect"); + //PostgreSQL数据库1 -- + dialectMap.put("postgresql", "org.hibernate.dialect.PostgreSQLDialect"); + dialectMap.put("sqlserver2005", "org.hibernate.dialect.SQLServer2005Dialect"); + //sqlserver数据库1 + dialectMap.put("sqlserver", "org.hibernate.dialect.SQLServerDialect"); + //达梦数据库 [国产] 1-- + dialectMap.put("dm", "org.hibernate.dialect.DmDialect"); + //虚谷数据库 + dialectMap.put("xugu", "org.hibernate.dialect.HSQLDialect"); + //人大金仓 [国产] 1 + dialectMap.put("kingbasees", "org.hibernate.dialect.PostgreSQLDialect"); + // Phoenix HBase数据库 + dialectMap.put("phoenix", "org.hibernate.dialect.HSQLDialect"); + // Gauss 数据库 + dialectMap.put("zenith", "org.hibernate.dialect.PostgreSQLDialect"); + //阿里云PolarDB + dialectMap.put("clickhouse", "org.hibernate.dialect.MySQLDialect"); + // 南大通用数据库 TODO 没找到不确定 + dialectMap.put("gbase", "org.hibernate.dialect.PostgreSQLDialect"); + //神通数据库 [国产] TODO 没找到不确定 + dialectMap.put("oscar", "org.hibernate.dialect.PostgreSQLDialect"); + //Sybase ASE 数据库 + dialectMap.put("sybase", "org.hibernate.dialect.SybaseDialect"); + dialectMap.put("oceanbase", "org.hibernate.dialect.PostgreSQLDialect"); + dialectMap.put("Firebird", "org.hibernate.dialect.FirebirdDialect"); + //瀚高数据库 + dialectMap.put("highgo", "org.hibernate.dialect.HSQLDialect"); + dialectMap.put("other", "org.hibernate.dialect.PostgreSQLDialect"); + } + + public static boolean dbTypeIsMySql(DbType dbType) { + return dbTypeIf(dbType, DbType.MYSQL, DbType.MARIADB, DbType.CLICK_HOUSE, DbType.SQLITE); + } + + public static boolean dbTypeIsOracle(DbType dbType) { + return dbTypeIf(dbType, DbType.ORACLE, DbType.ORACLE_12C, DbType.DM); + } + + public static boolean dbTypeIsSqlServer(DbType dbType) { + return dbTypeIf(dbType, DbType.SQL_SERVER, DbType.SQL_SERVER2005); + } + + public static boolean dbTypeIsPostgre(DbType dbType) { + return dbTypeIf(dbType, DbType.POSTGRE_SQL, DbType.KINGBASE_ES, DbType.GAUSS); + } + + + + /** + * 根据枚举类 获取数据库类型的字符串 + * @param dbType + * @return + */ + public static String getDbTypeString(DbType dbType){ + if(DbType.DB2.equals(dbType)){ + return DataBaseConstant.DB_TYPE_DB2; + }else if(DbType.HSQL.equals(dbType)){ + return DataBaseConstant.DB_TYPE_HSQL; + }else if(dbTypeIsOracle(dbType)){ + return DataBaseConstant.DB_TYPE_ORACLE; + }else if(dbTypeIsSqlServer(dbType)){ + return DataBaseConstant.DB_TYPE_SQLSERVER; + }else if(dbTypeIsPostgre(dbType)){ + return DataBaseConstant.DB_TYPE_POSTGRESQL; + } + return DataBaseConstant.DB_TYPE_MYSQL; + } + + /** + * 根据枚举类 获取数据库方言字符串 + * @param dbType + * @return + */ + public static String getDbDialect(DbType dbType){ + return dialectMap.get(dbType.getDb()); + } + + /** + * 判断数据库类型 + */ + public static boolean dbTypeIf(DbType dbType, DbType... correctTypes) { + for (DbType type : correctTypes) { + if (type.equals(dbType)) { + return true; + } + } + return false; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/dynamic/db/DynamicDBUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/dynamic/db/DynamicDBUtil.java new file mode 100644 index 0000000..38b172f --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/dynamic/db/DynamicDBUtil.java @@ -0,0 +1,344 @@ +package org.jeecg.common.util.dynamic.db; + +import com.alibaba.druid.pool.DruidDataSource; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; +import org.jeecg.common.exception.JeecgBootException; +import org.jeecg.common.exception.JeecgBootException; +import org.jeecg.common.system.vo.DynamicDataSourceModel; +import org.jeecg.common.util.ReflectHelper; +import org.jeecg.common.util.oConvertUtils; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; + +import javax.sql.DataSource; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Spring JDBC 实时数据库访问 + * + * @author chenguobin + * @version 1.0 + * @date 2014-09-05 + */ +@Slf4j +public class DynamicDBUtil { + + /** + * 获取数据源【最底层方法,不要随便调用】 + * + * @param dbSource + * @return + */ + private static DruidDataSource getJdbcDataSource(final DynamicDataSourceModel dbSource) { + DruidDataSource dataSource = new DruidDataSource(); + + String driverClassName = dbSource.getDbDriver(); + String url = dbSource.getDbUrl(); + String dbUser = dbSource.getDbUsername(); + String dbPassword = dbSource.getDbPassword(); + dataSource.setDriverClassName(driverClassName); + dataSource.setUrl(url); + //dataSource.setValidationQuery("SELECT 1 FROM DUAL"); + dataSource.setTestWhileIdle(true); + dataSource.setTestOnBorrow(false); + dataSource.setTestOnReturn(false); + dataSource.setBreakAfterAcquireFailure(true); + dataSource.setConnectionErrorRetryAttempts(0); + dataSource.setUsername(dbUser); + dataSource.setMaxWait(30000); + dataSource.setPassword(dbPassword); + + log.info("******************************************"); + log.info("* *"); + log.info("*====【"+dbSource.getCode()+"】=====Druid连接池已启用 ====*"); + log.info("* *"); + log.info("******************************************"); + return dataSource; + } + + /** + * 通过 dbKey ,获取数据源 + * + * @param dbKey + * @return + */ + public static DruidDataSource getDbSourceByDbKey(final String dbKey) { + //获取多数据源配置 + DynamicDataSourceModel dbSource = DataSourceCachePool.getCacheDynamicDataSourceModel(dbKey); + //先判断缓存中是否存在数据库链接 + DruidDataSource cacheDbSource = DataSourceCachePool.getCacheBasicDataSource(dbKey); + if (cacheDbSource != null && !cacheDbSource.isClosed()) { + log.debug("--------getDbSourceBydbKey------------------从缓存中获取DB连接-------------------"); + return cacheDbSource; + } else { + DruidDataSource dataSource = getJdbcDataSource(dbSource); + if(dataSource!=null && dataSource.isEnable()){ + DataSourceCachePool.putCacheBasicDataSource(dbKey, dataSource); + }else{ + throw new JeecgBootException("动态数据源连接失败,dbKey:"+dbKey); + } + log.info("--------getDbSourceBydbKey------------------创建DB数据库连接-------------------"); + return dataSource; + } + } + + /** + * 关闭数据库连接池 + * + * @param dbKey + * @return + */ + public static void closeDbKey(final String dbKey) { + DruidDataSource dataSource = getDbSourceByDbKey(dbKey); + try { + if (dataSource != null && !dataSource.isClosed()) { + dataSource.getConnection().commit(); + dataSource.getConnection().close(); + dataSource.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + private static JdbcTemplate getJdbcTemplate(String dbKey) { + DruidDataSource dataSource = getDbSourceByDbKey(dbKey); + return new JdbcTemplate(dataSource); + } + + /** + * 根据数据源获取NamedParameterJdbcTemplate + * @param dbKey + * @return + */ + private static NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(String dbKey) { + DruidDataSource dataSource = getDbSourceByDbKey(dbKey); + return new NamedParameterJdbcTemplate(dataSource); + } + + /** + * Executes the SQL statement in this PreparedStatement object, + * which must be an SQL Data Manipulation Language (DML) statement, such as INSERT, UPDATE or + * DELETE; or an SQL statement that returns nothing, + * such as a DDL statement. + */ + public static int update(final String dbKey, String sql, Object... param) { + int effectCount; + JdbcTemplate jdbcTemplate = getJdbcTemplate(dbKey); + if (ArrayUtils.isEmpty(param)) { + effectCount = jdbcTemplate.update(sql); + } else { + effectCount = jdbcTemplate.update(sql, param); + } + return effectCount; + } + + /** + * 支持miniDao语法操作的Update + * + * @param dbKey 数据源标识 + * @param sql 执行sql语句,sql支持minidao语法逻辑 + * @param data sql语法中需要判断的数据及sql拼接注入中需要的数据 + * @return + */ + public static int updateByHash(final String dbKey, String sql, HashMap data) { + int effectCount; + JdbcTemplate jdbcTemplate = getJdbcTemplate(dbKey); + //根据模板获取sql + sql = FreemarkerParseFactory.parseTemplateContent(sql, data); + NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate.getDataSource()); + effectCount = namedParameterJdbcTemplate.update(sql, data); + return effectCount; + } + + public static Object findOne(final String dbKey, String sql, Object... param) { + List> list; + list = findList(dbKey, sql, param); + if (oConvertUtils.listIsEmpty(list)) { + log.error("Except one, but not find actually"); + return null; + } + if (list.size() > 1) { + log.error("Except one, but more than one actually"); + } + return list.get(0); + } + + /** + * 支持miniDao语法操作的查询 返回HashMap + * + * @param dbKey 数据源标识 + * @param sql 执行sql语句,sql支持minidao语法逻辑 + * @param data sql语法中需要判断的数据及sql拼接注入中需要的数据 + * @return + */ + public static Object findOneByHash(final String dbKey, String sql, HashMap data) { + List> list; + list = findListByHash(dbKey, sql, data); + if (oConvertUtils.listIsEmpty(list)) { + log.error("Except one, but not find actually"); + } + if (list.size() > 1) { + log.error("Except one, but more than one actually"); + } + return list.get(0); + } + + /** + * 直接sql查询 根据clazz返回单个实例 + * + * @param dbKey 数据源标识 + * @param sql 执行sql语句 + * @param clazz 返回实例的Class + * @param param + * @return + */ + @SuppressWarnings("unchecked") + public static Object findOne(final String dbKey, String sql, Class clazz, Object... param) { + Map map = (Map) findOne(dbKey, sql, param); + return ReflectHelper.setAll(clazz, map); + } + + /** + * 支持miniDao语法操作的查询 返回单个实例 + * + * @param dbKey 数据源标识 + * @param sql 执行sql语句,sql支持minidao语法逻辑 + * @param clazz 返回实例的Class + * @param data sql语法中需要判断的数据及sql拼接注入中需要的数据 + * @return + */ + @SuppressWarnings("unchecked") + public static Object findOneByHash(final String dbKey, String sql, Class clazz, HashMap data) { + Map map = (Map) findOneByHash(dbKey, sql, data); + return ReflectHelper.setAll(clazz, map); + } + + public static List> findList(final String dbKey, String sql, Object... param) { + List> list; + JdbcTemplate jdbcTemplate = getJdbcTemplate(dbKey); + + if (ArrayUtils.isEmpty(param)) { + list = jdbcTemplate.queryForList(sql); + } else { + list = jdbcTemplate.queryForList(sql, param); + } + return list; + } + + /** + * 查询数量 + * @param dbKey + * @param sql + * @param param + * @return + */ + public static Map queryCount(String dbKey, String sql, Map param){ + NamedParameterJdbcTemplate npJdbcTemplate = getNamedParameterJdbcTemplate(dbKey); + return npJdbcTemplate.queryForMap(sql, param); + } + + /** + * 查询列表数据 + * @param dbKey + * @param sql + * @param param + * @return + */ + public static List> findListByNamedParam(final String dbKey, String sql, Map param) { + NamedParameterJdbcTemplate npJdbcTemplate = getNamedParameterJdbcTemplate(dbKey); + List> list = npJdbcTemplate.queryForList(sql, param); + return list; + } + + /** + * 支持miniDao语法操作的查询 + * + * @param dbKey 数据源标识 + * @param sql 执行sql语句,sql支持minidao语法逻辑 + * @param data sql语法中需要判断的数据及sql拼接注入中需要的数据 + * @return + */ + public static List> findListByHash(final String dbKey, String sql, HashMap data) { + List> list; + JdbcTemplate jdbcTemplate = getJdbcTemplate(dbKey); + //根据模板获取sql + sql = FreemarkerParseFactory.parseTemplateContent(sql, data); + NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate.getDataSource()); + list = namedParameterJdbcTemplate.queryForList(sql, data); + return list; + } + + /** + * 此方法只能返回单列,不能返回实体类 + * @param dbKey 数据源的key + * @param sql sal + * @param clazz 类 + * @param param 参数 + * @param + * @return + */ + public static List findList(final String dbKey, String sql, Class clazz, Object... param) { + List list; + JdbcTemplate jdbcTemplate = getJdbcTemplate(dbKey); + + if (ArrayUtils.isEmpty(param)) { + list = jdbcTemplate.queryForList(sql, clazz); + } else { + list = jdbcTemplate.queryForList(sql, clazz, param); + } + return list; + } + + /** + * 支持miniDao语法操作的查询 返回单列数据list + * + * @param dbKey 数据源标识 + * @param sql 执行sql语句,sql支持minidao语法逻辑 + * @param clazz 类型Long、String等 + * @param data sql语法中需要判断的数据及sql拼接注入中需要的数据 + * @return + */ + public static List findListByHash(final String dbKey, String sql, Class clazz, HashMap data) { + List list; + JdbcTemplate jdbcTemplate = getJdbcTemplate(dbKey); + //根据模板获取sql + sql = FreemarkerParseFactory.parseTemplateContent(sql, data); + NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate.getDataSource()); + list = namedParameterJdbcTemplate.queryForList(sql, data, clazz); + return list; + } + + /** + * 直接sql查询 返回实体类列表 + * + * @param dbKey 数据源标识 + * @param sql 执行sql语句,sql支持 minidao 语法逻辑 + * @param clazz 返回实体类列表的class + * @param param sql拼接注入中需要的数据 + * @return + */ + public static List findListEntities(final String dbKey, String sql, Class clazz, Object... param) { + List> queryList = findList(dbKey, sql, param); + return ReflectHelper.transList2Entrys(queryList, clazz); + } + + /** + * 支持miniDao语法操作的查询 返回实体类列表 + * + * @param dbKey 数据源标识 + * @param sql 执行sql语句,sql支持minidao语法逻辑 + * @param clazz 返回实体类列表的class + * @param data sql语法中需要判断的数据及sql拼接注入中需要的数据 + * @return + */ + public static List findListEntitiesByHash(final String dbKey, String sql, Class clazz, HashMap data) { + List> queryList = findListByHash(dbKey, sql, data); + return ReflectHelper.transList2Entrys(queryList, clazz); + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/dynamic/db/FreemarkerParseFactory.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/dynamic/db/FreemarkerParseFactory.java new file mode 100644 index 0000000..85c77b5 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/dynamic/db/FreemarkerParseFactory.java @@ -0,0 +1,182 @@ +package org.jeecg.common.util.dynamic.db; + +import freemarker.cache.StringTemplateLoader; +import freemarker.core.ParseException; +import freemarker.template.Configuration; +import freemarker.template.Template; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.constant.DataBaseConstant; +import org.jeecg.common.constant.SymbolConstant; +import org.jeecgframework.codegenerate.generate.util.SimpleFormat; + +import java.io.StringWriter; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * @author 赵俊夫 + * @version V1.0 + * @Title:FreemarkerHelper + * @description:Freemarker引擎协助类 + * @date Jul 5, 2013 2:58:29 PM + */ +@Slf4j +public class FreemarkerParseFactory { + + private static final String ENCODE = "utf-8"; + /** + * 参数格式化工具类 + */ + private static final String MINI_DAO_FORMAT = "DaoFormat"; + + /** + * 文件缓存 + */ + private static final Configuration TPL_CONFIG = new Configuration(); + /** + * SQL 缓存 + */ + private static final Configuration SQL_CONFIG = new Configuration(); + + private static StringTemplateLoader stringTemplateLoader = new StringTemplateLoader(); + + /**使用内嵌的(?ms)打开单行和多行模式*/ + private final static Pattern NOTES_PATTERN = Pattern + .compile("(?ms)/\\*.*?\\*/|^\\s*//.*?$"); + + static { + TPL_CONFIG.setClassForTemplateLoading( + new FreemarkerParseFactory().getClass(), "/"); + TPL_CONFIG.setNumberFormat("0.#####################"); + SQL_CONFIG.setTemplateLoader(stringTemplateLoader); + SQL_CONFIG.setNumberFormat("0.#####################"); + //classic_compatible设置,解决报空指针错误 + SQL_CONFIG.setClassicCompatible(true); + } + + /** + * 判断模板是否存在 + * + * @throws Exception + */ + public static boolean isExistTemplate(String tplName) throws Exception { + try { + Template mytpl = TPL_CONFIG.getTemplate(tplName, "UTF-8"); + if (mytpl == null) { + return false; + } + } catch (Exception e) { + //update-begin--Author:scott Date:20180320 for:解决问题 - 错误提示sql文件不存在,实际问题是sql freemarker用法错误----- + if (e instanceof ParseException) { + log.error(e.getMessage(), e.fillInStackTrace()); + throw new Exception(e); + } + log.debug("----isExistTemplate----" + e.toString()); + //update-end--Author:scott Date:20180320 for:解决问题 - 错误提示sql文件不存在,实际问题是sql freemarker用法错误------ + return false; + } + return true; + } + + /** + * 解析ftl模板 + * + * @param tplName 模板名 + * @param paras 参数 + * @return + */ + public static String parseTemplate(String tplName, Map paras) { + try { + log.debug(" minidao sql templdate : " + tplName); + StringWriter swriter = new StringWriter(); + Template mytpl = TPL_CONFIG.getTemplate(tplName, ENCODE); + if (paras.containsKey(MINI_DAO_FORMAT)) { + throw new RuntimeException("DaoFormat 是 minidao 保留关键字,不允许使用 ,请更改参数定义!"); + } + paras.put(MINI_DAO_FORMAT, new SimpleFormat()); + mytpl.process(paras, swriter); + String sql = getSqlText(swriter.toString()); + paras.remove(MINI_DAO_FORMAT); + return sql; + } catch (Exception e) { + log.error(e.getMessage(), e.fillInStackTrace()); + log.error("发送一次的模板key:{ " + tplName + " }"); + //System.err.println(e.getMessage()); + //System.err.println("模板名:{ "+ tplName +" }"); + throw new RuntimeException("解析SQL模板异常"); + } + } + + /** + * 解析ftl + * + * @param tplContent 模板内容 + * @param paras 参数 + * @return String 模板解析后内容 + */ + public static String parseTemplateContent(String tplContent, + Map paras) { + try { + String sqlUnderline="sql_"; + StringWriter swriter = new StringWriter(); + if (stringTemplateLoader.findTemplateSource(sqlUnderline + tplContent.hashCode()) == null) { + stringTemplateLoader.putTemplate(sqlUnderline + tplContent.hashCode(), tplContent); + } + Template mytpl = SQL_CONFIG.getTemplate(sqlUnderline + tplContent.hashCode(), ENCODE); + if (paras.containsKey(MINI_DAO_FORMAT)) { + throw new RuntimeException("DaoFormat 是 minidao 保留关键字,不允许使用 ,请更改参数定义!"); + } + paras.put(MINI_DAO_FORMAT, new SimpleFormat()); + mytpl.process(paras, swriter); + String sql = getSqlText(swriter.toString()); + paras.remove(MINI_DAO_FORMAT); + return sql; + } catch (Exception e) { + log.error(e.getMessage(), e.fillInStackTrace()); + log.error("发送一次的模板key:{ " + tplContent + " }"); + //System.err.println(e.getMessage()); + //System.err.println("模板内容:{ "+ tplContent +" }"); + throw new RuntimeException("解析SQL模板异常"); + } + } + + /** + * 除去无效字段,去掉注释 不然批量处理可能报错 去除无效的等于 + */ + private static String getSqlText(String sql) { + // 将注释替换成"" + sql = NOTES_PATTERN.matcher(sql).replaceAll(""); + sql = sql.replaceAll("\\n", " ").replaceAll("\\t", " ") + .replaceAll("\\s{1,}", " ").trim(); + // 去掉 最后是 where这样的问题 + //where空格 "where " + String whereSpace = DataBaseConstant.SQL_WHERE+" "; + //"where and" + String whereAnd = DataBaseConstant.SQL_WHERE+" and"; + //", where" + String commaWhere = SymbolConstant.COMMA+" "+DataBaseConstant.SQL_WHERE; + //", " + String commaSpace = SymbolConstant.COMMA + " "; + if (sql.endsWith(DataBaseConstant.SQL_WHERE) || sql.endsWith(whereSpace)) { + sql = sql.substring(0, sql.lastIndexOf("where")); + } + // 去掉where and 这样的问题 + int index = 0; + while ((index = StringUtils.indexOfIgnoreCase(sql, whereAnd, index)) != -1) { + sql = sql.substring(0, index + 5) + + sql.substring(index + 9, sql.length()); + } + // 去掉 , where 这样的问题 + index = 0; + while ((index = StringUtils.indexOfIgnoreCase(sql, commaWhere, index)) != -1) { + sql = sql.substring(0, index) + + sql.substring(index + 1, sql.length()); + } + // 去掉 最后是 ,这样的问题 + if (sql.endsWith(SymbolConstant.COMMA) || sql.endsWith(commaSpace)) { + sql = sql.substring(0, sql.lastIndexOf(",")); + } + return sql; + } +} \ No newline at end of file diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/encryption/AesEncryptUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/encryption/AesEncryptUtil.java new file mode 100644 index 0000000..670f3eb --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/encryption/AesEncryptUtil.java @@ -0,0 +1,124 @@ +package org.jeecg.common.util.encryption; + +import org.apache.shiro.codec.Base64; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +/** + * @Description: AES 加密 + * @author: jeecg-boot + * @date: 2022/3/30 11:48 + */ +public class AesEncryptUtil { + + /** + * 使用AES-128-CBC加密模式 key和iv可以相同 + */ + private static String KEY = EncryptedString.key; + private static String IV = EncryptedString.iv; + + /** + * 加密方法 + * @param data 要加密的数据 + * @param key 加密key + * @param iv 加密iv + * @return 加密的结果 + * @throws Exception + */ + public static String encrypt(String data, String key, String iv) throws Exception { + try { + + //"算法/模式/补码方式"NoPadding PkcsPadding + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + int blockSize = cipher.getBlockSize(); + + byte[] dataBytes = data.getBytes(); + int plaintextLength = dataBytes.length; + if (plaintextLength % blockSize != 0) { + plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize)); + } + + byte[] plaintext = new byte[plaintextLength]; + System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length); + + SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES"); + IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes()); + + cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec); + byte[] encrypted = cipher.doFinal(plaintext); + + return Base64.encodeToString(encrypted); + + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 解密方法 + * @param data 要解密的数据 + * @param key 解密key + * @param iv 解密iv + * @return 解密的结果 + * @throws Exception + */ + public static String desEncrypt(String data, String key, String iv) throws Exception { + //update-begin-author:taoyan date:2022-5-23 for:VUEN-1084 【vue3】online表单测试发现的新问题 6、解密报错 ---解码失败应该把异常抛出去,在外面处理 + byte[] encrypted1 = Base64.decode(data); + + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES"); + IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes()); + + cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec); + + byte[] original = cipher.doFinal(encrypted1); + String originalString = new String(original); + //加密解码后的字符串会出现\u0000 + return originalString.replaceAll("\\u0000", ""); + //update-end-author:taoyan date:2022-5-23 for:VUEN-1084 【vue3】online表单测试发现的新问题 6、解密报错 ---解码失败应该把异常抛出去,在外面处理 + } + + /** + * 使用默认的key和iv加密 + * @param data + * @return + * @throws Exception + */ + public static String encrypt(String data) throws Exception { + return encrypt(data, KEY, IV); + } + + /** + * 使用默认的key和iv解密 + * @param data + * @return + * @throws Exception + */ + public static String desEncrypt(String data) throws Exception { + return desEncrypt(data, KEY, IV); + } + + + +// /** +// * 测试 +// */ +// public static void main(String args[]) throws Exception { +// String test1 = "sa"; +// String test =new String(test1.getBytes(),"UTF-8"); +// String data = null; +// String key = KEY; +// String iv = IV; +// // /g2wzfqvMOeazgtsUVbq1kmJawROa6mcRAzwG1/GeJ4= +// data = encrypt(test, key, iv); +// System.out.println("数据:"+test); +// System.out.println("加密:"+data); +// String jiemi =desEncrypt(data, key, iv).trim(); +// System.out.println("解密:"+jiemi); +// } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/encryption/EncryptedString.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/encryption/EncryptedString.java new file mode 100644 index 0000000..adb7a8e --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/encryption/EncryptedString.java @@ -0,0 +1,22 @@ +package org.jeecg.common.util.encryption; + + +import lombok.Data; + +/** + * @Description: EncryptedString + * @author: jeecg-boot + */ +@Data +public class EncryptedString { + + /** + * 长度为16个字符 + */ + public static String key = "1234567890adbcde"; + + /** + * 长度为16个字符 + */ + public static String iv = "1234567890hjlkew"; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/filter/FileTypeFilter.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/filter/FileTypeFilter.java new file mode 100644 index 0000000..68f8de9 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/filter/FileTypeFilter.java @@ -0,0 +1,166 @@ +package org.jeecg.common.util.filter; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @Description: TODO + * @author: lsq + * @date: 2021年08月09日 15:29 + */ +public class FileTypeFilter { + + /**文件后缀*/ + private static String[] forbidType = {"jsp","php"}; + + /**初始化文件头类型,不够的自行补充*/ + final static HashMap FILE_TYPE_MAP = new HashMap<>(); + + static { + FILE_TYPE_MAP.put("3c25402070616765206c", "jsp"); + FILE_TYPE_MAP.put("3c3f7068700a0a2f2a2a0a202a205048", "php"); + /* fileTypeMap.put("ffd8ffe000104a464946", "jpg"); + fileTypeMap.put("89504e470d0a1a0a0000", "png"); + fileTypeMap.put("47494638396126026f01", "gif"); + fileTypeMap.put("49492a00227105008037", "tif"); + fileTypeMap.put("424d228c010000000000", "bmp"); + fileTypeMap.put("424d8240090000000000", "bmp"); + fileTypeMap.put("424d8e1b030000000000", "bmp"); + fileTypeMap.put("41433130313500000000", "dwg"); + fileTypeMap.put("3c21444f435459504520", "html"); + fileTypeMap.put("3c21646f637479706520", "htm"); + fileTypeMap.put("48544d4c207b0d0a0942", "css"); + fileTypeMap.put("696b2e71623d696b2e71", "js"); + fileTypeMap.put("7b5c727466315c616e73", "rtf"); + fileTypeMap.put("38425053000100000000", "psd"); + fileTypeMap.put("46726f6d3a203d3f6762", "eml"); + fileTypeMap.put("d0cf11e0a1b11ae10000", "doc"); + fileTypeMap.put("5374616E64617264204A", "mdb"); + fileTypeMap.put("252150532D41646F6265", "ps"); + fileTypeMap.put("255044462d312e350d0a", "pdf"); + fileTypeMap.put("2e524d46000000120001", "rmvb"); + fileTypeMap.put("464c5601050000000900", "flv"); + fileTypeMap.put("00000020667479706d70", "mp4"); + fileTypeMap.put("49443303000000002176", "mp3"); + fileTypeMap.put("000001ba210001000180", "mpg"); + fileTypeMap.put("3026b2758e66cf11a6d9", "wmv"); + fileTypeMap.put("52494646e27807005741", "wav"); + fileTypeMap.put("52494646d07d60074156", "avi"); + fileTypeMap.put("4d546864000000060001", "mid"); + fileTypeMap.put("504b0304140000000800", "zip"); + fileTypeMap.put("526172211a0700cf9073", "rar"); + fileTypeMap.put("235468697320636f6e66", "ini"); + fileTypeMap.put("504b03040a0000000000", "jar"); + fileTypeMap.put("4d5a9000030000000400", "exe"); + fileTypeMap.put("3c25402070616765206c", "jsp"); + fileTypeMap.put("4d616e69666573742d56", "mf"); + fileTypeMap.put("3c3f786d6c2076657273", "xml"); + fileTypeMap.put("494e5345525420494e54", "sql"); + fileTypeMap.put("7061636b616765207765", "java"); + fileTypeMap.put("406563686f206f66660d", "bat"); + fileTypeMap.put("1f8b0800000000000000", "gz"); + fileTypeMap.put("6c6f67346a2e726f6f74", "properties"); + fileTypeMap.put("cafebabe0000002e0041", "class"); + fileTypeMap.put("49545346030000006000", "chm"); + fileTypeMap.put("04000000010000001300", "mxp"); + fileTypeMap.put("504b0304140006000800", "docx"); + fileTypeMap.put("6431303a637265617465", "torrent"); + fileTypeMap.put("6D6F6F76", "mov"); + fileTypeMap.put("FF575043", "wpd"); + fileTypeMap.put("CFAD12FEC5FD746F", "dbx"); + fileTypeMap.put("2142444E", "pst"); + fileTypeMap.put("AC9EBD8F", "qdf"); + fileTypeMap.put("E3828596", "pwl"); + fileTypeMap.put("2E7261FD", "ram");*/ + } + + /** + * @param fileName + * @return String + * @description 通过文件后缀名获取文件类型 + */ + private static String getFileTypeBySuffix(String fileName) { + return fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()); + } + + /** + * 文件类型过滤 + * + * @param file + */ + public static void fileTypeFilter(MultipartFile file) throws Exception { + String suffix = getFileType(file); + for (String type : forbidType) { + if (type.contains(suffix)) { + throw new Exception("上传失败,文件类型异常:" + suffix); + } + } + } + + /** + * 通过读取文件头部获得文件类型 + * + * @param file + * @return 文件类型 + * @throws Exception + */ + + private static String getFileType(MultipartFile file) throws Exception { + String fileExtendName = null; + InputStream is; + try { + //is = new FileInputStream(file); + is = file.getInputStream(); + byte[] b = new byte[10]; + is.read(b, 0, b.length); + String fileTypeHex = String.valueOf(bytesToHexString(b)); + Iterator keyIter = FILE_TYPE_MAP.keySet().iterator(); + while (keyIter.hasNext()) { + String key = keyIter.next(); + // 验证前5个字符比较 + if (key.toLowerCase().startsWith(fileTypeHex.toLowerCase().substring(0, 5)) + || fileTypeHex.toLowerCase().substring(0, 5).startsWith(key.toLowerCase())) { + fileExtendName = FILE_TYPE_MAP.get(key); + break; + } + } + // 如果不是上述类型,则判断扩展名 + if (StringUtils.isBlank(fileExtendName)) { + String fileName = file.getOriginalFilename(); + return getFileTypeBySuffix(fileName); + } + is.close(); + return fileExtendName; + } catch (Exception exception) { + throw new Exception(exception.getMessage(), exception); + } + } + + /** + * 获得文件头部字符串 + * + * @param src + * @return + */ + private static String bytesToHexString(byte[] src) { + StringBuilder stringBuilder = new StringBuilder(); + if (src == null || src.length <= 0) { + return null; + } + for (int i = 0; i < src.length; i++) { + int v = src[i] & 0xFF; + String hv = Integer.toHexString(v); + if (hv.length() < 2) { + stringBuilder.append(0); + } + stringBuilder.append(hv); + } + return stringBuilder.toString(); + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/filter/StrAttackFilter.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/filter/StrAttackFilter.java new file mode 100644 index 0000000..0f522c2 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/filter/StrAttackFilter.java @@ -0,0 +1,25 @@ +package org.jeecg.common.util.filter; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** + * 文件上传字符串过滤特殊字符 + * @author: jeecg-boot + */ +public class StrAttackFilter { + + public static String filter(String str) throws PatternSyntaxException { + // 清除掉所有特殊字符 + String regEx = "[`_《》~!@#$%^&*()+=|{}':;',\\[\\].<>?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]"; + Pattern p = Pattern.compile(regEx); + Matcher m = p.matcher(str); + return m.replaceAll("").trim(); + } + +// public static void main(String[] args) { +// String filter = filter("@#jeecg/《》【bo】¥%……&*(o))))!@t<>,.,/?'\'~~`"); +// System.out.println(filter); +// } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/oConvertUtils.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/oConvertUtils.java new file mode 100644 index 0000000..dc926c3 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/oConvertUtils.java @@ -0,0 +1,689 @@ +package org.jeecg.common.util; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.BeanUtils; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.sql.Date; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * @Author 张代浩 + * + */ +@Slf4j +public class oConvertUtils { + public static boolean isEmpty(Object object) { + if (object == null) { + return (true); + } + if ("".equals(object)) { + return (true); + } + if ("null".equals(object)) { + return (true); + } + return (false); + } + + public static boolean isNotEmpty(Object object) { + if (object != null && !"".equals(object) && !"null".equals(object)) { + return (true); + } + return (false); + } + + public static String decode(String strIn, String sourceCode, String targetCode) { + String temp = code2code(strIn, sourceCode, targetCode); + return temp; + } + + public static String StrToUTF(String strIn, String sourceCode, String targetCode) { + strIn = ""; + try { + strIn = new String(strIn.getBytes("ISO-8859-1"), "GBK"); + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return strIn; + + } + + private static String code2code(String strIn, String sourceCode, String targetCode) { + String strOut = null; + if (strIn == null || "".equals(strIn.trim())) { + return strIn; + } + try { + byte[] b = strIn.getBytes(sourceCode); + for (int i = 0; i < b.length; i++) { + System.out.print(b[i] + " "); + } + strOut = new String(b, targetCode); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + return strOut; + } + + public static int getInt(String s, int defval) { + if (s == null || s == "") { + return (defval); + } + try { + return (Integer.parseInt(s)); + } catch (NumberFormatException e) { + return (defval); + } + } + + public static int getInt(String s) { + if (s == null || s == "") { + return 0; + } + try { + return (Integer.parseInt(s)); + } catch (NumberFormatException e) { + return 0; + } + } + + public static int getInt(String s, Integer df) { + if (s == null || s == "") { + return df; + } + try { + return (Integer.parseInt(s)); + } catch (NumberFormatException e) { + return 0; + } + } + + public static Integer[] getInts(String[] s) { + Integer[] integer = new Integer[s.length]; + if (s == null) { + return null; + } + for (int i = 0; i < s.length; i++) { + integer[i] = Integer.parseInt(s[i]); + } + return integer; + + } + + public static double getDouble(String s, double defval) { + if (s == null || s == "") { + return (defval); + } + try { + return (Double.parseDouble(s)); + } catch (NumberFormatException e) { + return (defval); + } + } + + public static double getDou(Double s, double defval) { + if (s == null) { + return (defval); + } + return s; + } + + /*public static Short getShort(String s) { + if (StringUtil.isNotEmpty(s)) { + return (Short.parseShort(s)); + } else { + return null; + } + }*/ + + public static int getInt(Object object, int defval) { + if (isEmpty(object)) { + return (defval); + } + try { + return (Integer.parseInt(object.toString())); + } catch (NumberFormatException e) { + return (defval); + } + } + + public static Integer getInt(Object object) { + if (isEmpty(object)) { + return null; + } + try { + return (Integer.parseInt(object.toString())); + } catch (NumberFormatException e) { + return null; + } + } + + public static int getInt(BigDecimal s, int defval) { + if (s == null) { + return (defval); + } + return s.intValue(); + } + + public static Integer[] getIntegerArry(String[] object) { + int len = object.length; + Integer[] result = new Integer[len]; + try { + for (int i = 0; i < len; i++) { + result[i] = new Integer(object[i].trim()); + } + return result; + } catch (NumberFormatException e) { + return null; + } + } + + public static String getString(String s) { + return (getString(s, "")); + } + +// /** +// * 转义成Unicode编码 +// * @param s +// * @return +// */ + /*public static String escapeJava(Object s) { + return StringEscapeUtils.escapeJava(getString(s)); + }*/ + + public static String getString(Object object) { + if (isEmpty(object)) { + return ""; + } + return (object.toString().trim()); + } + + public static String getString(int i) { + return (String.valueOf(i)); + } + + public static String getString(float i) { + return (String.valueOf(i)); + } + + public static String getString(String s, String defval) { + if (isEmpty(s)) { + return (defval); + } + return (s.trim()); + } + + public static String getString(Object s, String defval) { + if (isEmpty(s)) { + return (defval); + } + return (s.toString().trim()); + } + + public static long stringToLong(String str) { + Long test = new Long(0); + try { + test = Long.valueOf(str); + } catch (Exception e) { + } + return test.longValue(); + } + + /** + * 获取本机IP + */ + public static String getIp() { + String ip = null; + try { + InetAddress address = InetAddress.getLocalHost(); + ip = address.getHostAddress(); + + } catch (UnknownHostException e) { + e.printStackTrace(); + } + return ip; + } + + /** + * 判断一个类是否为基本数据类型。 + * + * @param clazz + * 要判断的类。 + * @return true 表示为基本数据类型。 + */ + private static boolean isBaseDataType(Class clazz) throws Exception { + return (clazz.equals(String.class) || clazz.equals(Integer.class) || clazz.equals(Byte.class) || clazz.equals(Long.class) || clazz.equals(Double.class) || clazz.equals(Float.class) || clazz.equals(Character.class) || clazz.equals(Short.class) || clazz.equals(BigDecimal.class) || clazz.equals(BigInteger.class) || clazz.equals(Boolean.class) || clazz.equals(Date.class) || clazz.isPrimitive()); + } + + /** + * @param request + * IP + * @return IP Address + */ + public static String getIpAddrByRequest(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + return ip; + } + + /** + * @return 本机IP + * @throws SocketException + */ + //使用DNS名称作为安全性的依据 - 中危 +// public static String getRealIp() throws SocketException { +// String localip = null;// 本地IP,如果没有配置外网IP则返回它 +// String netip = null;// 外网IP +// +// Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces(); +// InetAddress ip = null; +// boolean finded = false;// 是否找到外网IP +// while (netInterfaces.hasMoreElements() && !finded) { +// NetworkInterface ni = netInterfaces.nextElement(); +// Enumeration address = ni.getInetAddresses(); +// while (address.hasMoreElements()) { +// ip = address.nextElement(); +// if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {// 外网IP +// netip = ip.getHostAddress(); +// finded = true; +// break; +// } else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {// 内网IP +// localip = ip.getHostAddress(); +// } +// } +// } +// +// if (netip != null && !"".equals(netip)) { +// return netip; +// } else { +// return localip; +// } +// } + + private static final Pattern PATTERN = Pattern.compile("\\s*|\t|\r|\n"); + /** + * java去除字符串中的空格、回车、换行符、制表符 + * + * @param str + * @return + */ + public static String replaceBlank(String str) { + String dest = ""; + if (str != null) { +// Pattern p = Pattern.compile("\\s*|\t|\r|\n"); + Matcher m = PATTERN.matcher(str); + dest = m.replaceAll(""); + } + return dest; + + } + + /** + * 判断元素是否在数组内 + * + * @param substring + * @param source + * @return + */ + public static boolean isIn(String substring, String[] source) { + if (source == null || source.length == 0) { + return false; + } + for (int i = 0; i < source.length; i++) { + String aSource = source[i]; + if (aSource.equals(substring)) { + return true; + } + } + return false; + } + + /** + * 获取Map对象 + */ + public static Map getHashMap() { + return new HashMap(); + } + + /** + * SET转换MAP + * + * @param setobj + * @return + */ + public static Map SetToMap(Set setobj) { + Map map = getHashMap(); + for (Iterator iterator = setobj.iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + map.put(entry.getKey().toString(), entry.getValue() == null ? "" : entry.getValue().toString().trim()); + } + return map; + + } + + public static boolean isInnerIP(String ipAddress) { + boolean isInnerIp = false; + long ipNum = getIpNum(ipAddress); + /** + * 私有IP:A类 10.0.0.0-10.255.255.255 B类 172.16.0.0-172.31.255.255 C类 192.168.0.0-192.168.255.255 当然,还有127这个网段是环回地址 + **/ + long aBegin = getIpNum("10.0.0.0"); + long aEnd = getIpNum("10.255.255.255"); + long bBegin = getIpNum("172.16.0.0"); + long bEnd = getIpNum("172.31.255.255"); + long cBegin = getIpNum("192.168.0.0"); + long cEnd = getIpNum("192.168.255.255"); + isInnerIp = isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd) || "127.0.0.1".equals(ipAddress); + return isInnerIp; + } + + private static long getIpNum(String ipAddress) { + String[] ip = ipAddress.split("\\."); + long a = Integer.parseInt(ip[0]); + long b = Integer.parseInt(ip[1]); + long c = Integer.parseInt(ip[2]); + long d = Integer.parseInt(ip[3]); + + long ipNum = a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d; + return ipNum; + } + + private static boolean isInner(long userIp, long begin, long end) { + return (userIp >= begin) && (userIp <= end); + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。 + * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
+ * 例如:hello_world->helloWorld + * + * @param name + * 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String camelName(String name) { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) { + // 没必要转换 + return ""; + } else if (!name.contains("_")) { + // 不含下划线,仅将首字母小写 + //update-begin--Author:zhoujf Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能 + //update-begin--Author:zhoujf Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能 + return name.substring(0, 1).toLowerCase() + name.substring(1).toLowerCase(); + //update-end--Author:zhoujf Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能 + } + // 用下划线将原始字符串分割 + String camels[] = name.split("_"); + for (String camel : camels) { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) { + continue; + } + // 处理真正的驼峰片段 + if (result.length() == 0) { + // 第一个驼峰片段,全部字母都小写 + result.append(camel.toLowerCase()); + } else { + // 其他的驼峰片段,首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + } + return result.toString(); + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。 + * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
+ * 例如:hello_world,test_id->helloWorld,testId + * + * @param names + * 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String camelNames(String names) { + if(names==null|| "".equals(names)){ + return null; + } + StringBuffer sf = new StringBuffer(); + String[] fs = names.split(","); + for (String field : fs) { + field = camelName(field); + sf.append(field + ","); + } + String result = sf.toString(); + return result.substring(0, result.length() - 1); + } + + //update-begin--Author:zhoujf Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能 + /** + * 将下划线大写方式命名的字符串转换为驼峰式。(首字母写) + * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
+ * 例如:hello_world->HelloWorld + * + * @param name + * 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String camelNameCapFirst(String name) { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) { + // 没必要转换 + return ""; + } else if (!name.contains("_")) { + // 不含下划线,仅将首字母小写 + return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); + } + // 用下划线将原始字符串分割 + String camels[] = name.split("_"); + for (String camel : camels) { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) { + continue; + } + // 其他的驼峰片段,首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + //update-end--Author:zhoujf Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能 + + /** + * 将驼峰命名转化成下划线 + * @param para + * @return + */ + public static String camelToUnderline(String para){ + if(para.length()<3){ + return para.toLowerCase(); + } + StringBuilder sb=new StringBuilder(para); + int temp=0;//定位 + //从第三个字符开始 避免命名不规范 + for(int i=2;i clazz = object.getClass(); + List fieldList = new ArrayList<>(); + while (clazz != null) { + fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()))); + clazz = clazz.getSuperclass(); + } + Field[] fields = new Field[fieldList.size()]; + fieldList.toArray(fields); + return fields; + } + + /** + * 将map的key全部转成小写 + * @param list + * @return + */ + public static List> toLowerCasePageList(List> list){ + List> select = new ArrayList<>(); + for (Map row : list) { + Map resultMap = new HashMap<>(); + Set keySet = row.keySet(); + for (String key : keySet) { + String newKey = key.toLowerCase(); + resultMap.put(newKey, row.get(key)); + } + select.add(resultMap); + } + return select; + } + + /** + * 将entityList转换成modelList + * @param fromList + * @param tClass + * @param + * @param + * @return + */ + public static List entityListToModelList(List fromList, Class tClass){ + if(fromList == null || fromList.isEmpty()){ + return null; + } + List tList = new ArrayList<>(); + for(F f : fromList){ + T t = entityToModel(f, tClass); + tList.add(t); + } + return tList; + } + + public static T entityToModel(F entity, Class modelClass) { + log.debug("entityToModel : Entity属性的值赋值到Model"); + Object model = null; + if (entity == null || modelClass ==null) { + return null; + } + + try { + model = modelClass.newInstance(); + } catch (InstantiationException e) { + log.error("entityToModel : 实例化异常", e); + } catch (IllegalAccessException e) { + log.error("entityToModel : 安全权限异常", e); + } + BeanUtils.copyProperties(entity, model); + return (T)model; + } + + /** + * 判断 list 是否为空 + * + * @param list + * @return true or false + * list == null : true + * list.size() == 0 : true + */ + public static boolean listIsEmpty(Collection list) { + return (list == null || list.size() == 0); + } + + /** + * 判断 list 是否不为空 + * + * @param list + * @return true or false + * list == null : false + * list.size() == 0 : false + */ + public static boolean listIsNotEmpty(Collection list) { + return !listIsEmpty(list); + } + + /** + * 读取静态文本内容 + * @param url + * @return + */ + public static String readStatic(String url) { + String json = ""; + InputStream stream = null; + try { + //换个写法,解决springboot读取jar包中文件的问题 + stream = oConvertUtils.class.getClassLoader().getResourceAsStream(url.replace("classpath:", "")); + json = IOUtils.toString(stream,"UTF-8"); + stream.close(); + } catch (IOException e) { + log.error(e.getMessage(),e); + } + finally{ + //资源未释放:流 - 质量问题 -中危 + if(stream!=null){ + try { + stream.close(); + } catch (IOException e) { + System.out.println("关闭流异常【org.jeecg.common.util.oConvertUtils.readStatic】"); + } + } + } + return json; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/oss/OssBootUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/oss/OssBootUtil.java new file mode 100644 index 0000000..09338cb --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/oss/OssBootUtil.java @@ -0,0 +1,375 @@ +package org.jeecg.common.util.oss; + +import com.aliyun.oss.ClientConfiguration; +import com.aliyun.oss.OSSClient; +import com.aliyun.oss.common.auth.DefaultCredentialProvider; +import com.aliyun.oss.model.CannedAccessControlList; +import com.aliyun.oss.model.OSSObject; +import com.aliyun.oss.model.PutObjectResult; +import lombok.extern.slf4j.Slf4j; +import org.apache.tomcat.util.http.fileupload.FileItemStream; +import org.jeecg.common.util.CommonUtils; +import org.jeecg.common.util.filter.FileTypeFilter; +import org.jeecg.common.util.filter.StrAttackFilter; +import org.jeecg.common.util.oConvertUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLDecoder; +import java.util.Date; +import java.util.UUID; + +/** + * @Description: 阿里云 oss 上传工具类(高依赖版) + * @Date: 2019/5/10 + */ +@Slf4j +public class OssBootUtil { + + private static String endPoint; + private static String accessKeyId; + private static String accessKeySecret; + private static String bucketName; + private static String staticDomain; + + public static void setEndPoint(String endPoint) { + OssBootUtil.endPoint = endPoint; + } + + public static void setAccessKeyId(String accessKeyId) { + OssBootUtil.accessKeyId = accessKeyId; + } + + public static void setAccessKeySecret(String accessKeySecret) { + OssBootUtil.accessKeySecret = accessKeySecret; + } + + public static void setBucketName(String bucketName) { + OssBootUtil.bucketName = bucketName; + } + + public static void setStaticDomain(String staticDomain) { + OssBootUtil.staticDomain = staticDomain; + } + + public static String getStaticDomain() { + return staticDomain; + } + + public static String getEndPoint() { + return endPoint; + } + + public static String getAccessKeyId() { + return accessKeyId; + } + + public static String getAccessKeySecret() { + return accessKeySecret; + } + + public static String getBucketName() { + return bucketName; + } + + public static OSSClient getOssClient() { + return ossClient; + } + + /** + * oss 工具客户端 + */ + private static OSSClient ossClient = null; + + /** + * 上传文件至阿里云 OSS + * 文件上传成功,返回文件完整访问路径 + * 文件上传失败,返回 null + * + * @param file 待上传文件 + * @param fileDir 文件保存目录 + * @return oss 中的相对文件路径 + */ + public static String upload(MultipartFile file, String fileDir,String customBucket) { + String FILE_URL = null; + initOSS(endPoint, accessKeyId, accessKeySecret); + StringBuilder fileUrl = new StringBuilder(); + String newBucket = bucketName; + if(oConvertUtils.isNotEmpty(customBucket)){ + newBucket = customBucket; + } + try { + //判断桶是否存在,不存在则创建桶 + if(!ossClient.doesBucketExist(newBucket)){ + ossClient.createBucket(newBucket); + } + // 获取文件名 + String orgName = file.getOriginalFilename(); + if("" == orgName){ + orgName=file.getName(); + } + //update-begin-author:liusq date:20210809 for: 过滤上传文件类型 + FileTypeFilter.fileTypeFilter(file); + //update-end-author:liusq date:20210809 for: 过滤上传文件类型 + orgName = CommonUtils.getFileName(orgName); + String fileName = orgName.indexOf(".")==-1 + ?orgName + "_" + System.currentTimeMillis() + :orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf(".")); + if (!fileDir.endsWith("/")) { + fileDir = fileDir.concat("/"); + } + //update-begin-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击 + fileDir=StrAttackFilter.filter(fileDir); + //update-end-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击 + fileUrl = fileUrl.append(fileDir + fileName); + + if (oConvertUtils.isNotEmpty(staticDomain) && staticDomain.toLowerCase().startsWith("http")) { + FILE_URL = staticDomain + "/" + fileUrl; + } else { + FILE_URL = "https://" + newBucket + "." + endPoint + "/" + fileUrl; + } + PutObjectResult result = ossClient.putObject(newBucket, fileUrl.toString(), file.getInputStream()); + // 设置权限(公开读) +// ossClient.setBucketAcl(newBucket, CannedAccessControlList.PublicRead); + if (result != null) { + log.info("------OSS文件上传成功------" + fileUrl); + } + } catch (IOException e) { + e.printStackTrace(); + return null; + }catch (Exception e) { + e.printStackTrace(); + return null; + } finally{ + //资源未释放:流 - 质量问题 -中危 + if(file!=null){ + try { + file.getInputStream().close(); + } catch (IOException e) { + System.out.println("关闭流异常【】"); + } + } + } + return FILE_URL; + } + + /** + * 获取原始URL + * @param url: 原始URL + * @Return: java.lang.String + */ + public static String getOriginalUrl(String url) { + String originalDomain = "https://" + bucketName + "." + endPoint; + if(oConvertUtils.isNotEmpty(staticDomain) && url.indexOf(staticDomain)!=-1){ + url = url.replace(staticDomain,originalDomain); + } + return url; + } + + /** + * 文件上传 + * @param file + * @param fileDir + * @return + */ + public static String upload(MultipartFile file, String fileDir) { + return upload(file, fileDir,null); + } + + /** + * 上传文件至阿里云 OSS + * 文件上传成功,返回文件完整访问路径 + * 文件上传失败,返回 null + * + * @param file 待上传文件 + * @param fileDir 文件保存目录 + * @return oss 中的相对文件路径 + */ + public static String upload(FileItemStream file, String fileDir) { + String FILE_URL = null; + initOSS(endPoint, accessKeyId, accessKeySecret); + StringBuilder fileUrl = new StringBuilder(); + try { + String suffix = file.getName().substring(file.getName().lastIndexOf('.')); + String fileName = UUID.randomUUID().toString().replace("-", "") + suffix; + if (!fileDir.endsWith("/")) { + fileDir = fileDir.concat("/"); + } + fileDir = StrAttackFilter.filter(fileDir); + fileUrl = fileUrl.append(fileDir + fileName); + if (oConvertUtils.isNotEmpty(staticDomain) && staticDomain.toLowerCase().startsWith("http")) { + FILE_URL = staticDomain + "/" + fileUrl; + } else { + FILE_URL = "https://" + bucketName + "." + endPoint + "/" + fileUrl; + } + PutObjectResult result = ossClient.putObject(bucketName, fileUrl.toString(), file.openStream()); + // 设置权限(公开读) + ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead); + if (result != null) { + log.info("------OSS文件上传成功------" + fileUrl); + } + } catch (IOException e) { + e.printStackTrace(); + return null; + } + return FILE_URL; + } + + /** + * 删除文件 + * @param url + */ + public static void deleteUrl(String url) { + deleteUrl(url,null); + } + + /** + * 删除文件 + * @param url + */ + public static void deleteUrl(String url,String bucket) { + String newBucket = bucketName; + if(oConvertUtils.isNotEmpty(bucket)){ + newBucket = bucket; + } + String bucketUrl = ""; + if (oConvertUtils.isNotEmpty(staticDomain) && staticDomain.toLowerCase().startsWith("http")) { + bucketUrl = staticDomain + "/" ; + } else { + bucketUrl = "https://" + newBucket + "." + endPoint + "/"; + } + url = url.replace(bucketUrl,""); + ossClient.deleteObject(newBucket, url); + } + + /** + * 删除文件 + * @param fileName + */ + public static void delete(String fileName) { + ossClient.deleteObject(bucketName, fileName); + } + + /** + * 获取文件流 + * @param objectName + * @param bucket + * @return + */ + public static InputStream getOssFile(String objectName,String bucket){ + InputStream inputStream = null; + try{ + String newBucket = bucketName; + if(oConvertUtils.isNotEmpty(bucket)){ + newBucket = bucket; + } + initOSS(endPoint, accessKeyId, accessKeySecret); + //update-begin---author:liusq Date:20220120 for:替换objectName前缀,防止key不一致导致获取不到文件---- + objectName = OssBootUtil.replacePrefix(objectName,bucket); + //update-end---author:liusq Date:20220120 for:替换objectName前缀,防止key不一致导致获取不到文件---- + OSSObject ossObject = ossClient.getObject(newBucket,objectName); + inputStream = new BufferedInputStream(ossObject.getObjectContent()); + }catch (Exception e){ + log.info("文件获取失败" + e.getMessage()); + } + return inputStream; + } + + ///** + // * 获取文件流 + // * @param objectName + // * @return + // */ + //public static InputStream getOssFile(String objectName){ + // return getOssFile(objectName,null); + //} + + /** + * 获取文件外链 + * @param bucketName + * @param objectName + * @param expires + * @return + */ + public static String getObjectURL(String bucketName, String objectName, Date expires) { + initOSS(endPoint, accessKeyId, accessKeySecret); + try{ + //update-begin---author:liusq Date:20220120 for:替换objectName前缀,防止key不一致导致获取不到文件---- + objectName = OssBootUtil.replacePrefix(objectName,bucketName); + //update-end---author:liusq Date:20220120 for:替换objectName前缀,防止key不一致导致获取不到文件---- + if(ossClient.doesObjectExist(bucketName,objectName)){ + URL url = ossClient.generatePresignedUrl(bucketName,objectName,expires); + return URLDecoder.decode(url.toString(),"UTF-8"); + } + }catch (Exception e){ + log.info("文件路径获取失败" + e.getMessage()); + } + return null; + } + + /** + * 初始化 oss 客户端 + * + * @return + */ + private static OSSClient initOSS(String endpoint, String accessKeyId, String accessKeySecret) { + if (ossClient == null) { + ossClient = new OSSClient(endpoint, + new DefaultCredentialProvider(accessKeyId, accessKeySecret), + new ClientConfiguration()); + } + return ossClient; + } + + + /** + * 上传文件到oss + * @param stream + * @param relativePath + * @return + */ + public static String upload(InputStream stream, String relativePath) { + String FILE_URL = null; + String fileUrl = relativePath; + initOSS(endPoint, accessKeyId, accessKeySecret); + if (oConvertUtils.isNotEmpty(staticDomain) && staticDomain.toLowerCase().startsWith("http")) { + FILE_URL = staticDomain + "/" + relativePath; + } else { + FILE_URL = "https://" + bucketName + "." + endPoint + "/" + fileUrl; + } + PutObjectResult result = ossClient.putObject(bucketName, fileUrl.toString(),stream); + // 设置权限(公开读) + ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead); + if (result != null) { + log.info("------OSS文件上传成功------" + fileUrl); + } + return FILE_URL; + } + + /** + * 替换前缀,防止key不一致导致获取不到文件 + * @param objectName 文件上传路径 key + * @param customBucket 自定义桶 + * @date 2022-01-20 + * @author lsq + * @return + */ + private static String replacePrefix(String objectName,String customBucket){ + log.info("------replacePrefix---替换前---objectName:{}",objectName); + if(oConvertUtils.isNotEmpty(staticDomain)){ + objectName= objectName.replace(staticDomain+"/",""); + }else{ + String newBucket = bucketName; + if(oConvertUtils.isNotEmpty(customBucket)){ + newBucket = customBucket; + } + String path ="https://" + newBucket + "." + endPoint + "/"; + objectName = objectName.replace(path,""); + } + log.info("------replacePrefix---替换后---objectName:{}",objectName); + return objectName; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/AbstractQueryBlackListHandler.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/AbstractQueryBlackListHandler.java new file mode 100644 index 0000000..85af28d --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/AbstractQueryBlackListHandler.java @@ -0,0 +1,174 @@ +package org.jeecg.common.util.security; + +import lombok.extern.slf4j.Slf4j; + +import java.util.*; + +/** + * 查询表/字段 黑名单处理 + * @Author taoYan + * @Date 2022/3/17 11:21 + **/ +@Slf4j +public abstract class AbstractQueryBlackListHandler { + + /** + * key-表名 + * value-字段名,多个逗号隔开 + * 两种配置方式-- 全部配置成小写 + * ruleMap.put("sys_user", "*")sys_user所有的字段不支持查询 + * ruleMap.put("sys_user", "username,password")sys_user中的username和password不支持查询 + */ + public static Map ruleMap = new HashMap<>(); + + static { + ruleMap.put("sys_user", "password,salt"); + } + + + /** + * 根据 sql语句 获取表和字段信息,需要到具体的实现类重写此方法- + * 不同的场景 处理可能不太一样 需要自定义,但是返回值确定 + * @param sql + * @return + */ + protected abstract List getQueryTableInfo(String sql); + + + /** + * 校验sql语句 成功返回true + * @param sql + * @return + */ + public boolean isPass(String sql) { + List list = this.getQueryTableInfo(sql.toLowerCase()); + if(list==null){ + return true; + } + log.info("--获取sql信息--", list.toString()); + boolean flag = true; + for (QueryTable table : list) { + String name = table.getName(); + String fieldString = ruleMap.get(name); + // 有没有配置这张表 + if (fieldString != null) { + if ("*".equals(fieldString) || table.isAll()) { + flag = false; + log.warn("sql黑名单校验,表【"+name+"】禁止查询"); + break; + } else if (table.existSameField(fieldString)) { + flag = false; + break; + } + + } + } + return flag; + } + + /** + * 查询的表的信息 + */ + protected class QueryTable { + //表名 + private String name; + //表的别名 + private String alias; + // 字段名集合 + private Set fields; + // 是否查询所有字段 + private boolean all; + + public QueryTable() { + } + + public QueryTable(String name, String alias) { + this.name = name; + this.alias = alias; + this.all = false; + this.fields = new HashSet<>(); + } + + public void addField(String field) { + this.fields.add(field); + } + + public String getName() { + return name; + } + + public Set getFields() { + return new HashSet<>(fields); + } + + public void setName(String name) { + this.name = name; + } + + public void setFields(Set fields) { + this.fields = fields; + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public boolean isAll() { + return all; + } + + public void setAll(boolean all) { + this.all = all; + } + + /** + * 判断是否有相同字段 + * + * @param fieldString + * @return + */ + public boolean existSameField(String fieldString) { + String[] arr = fieldString.split(","); + for (String exp : fields) { + for (String config : arr) { + if (exp.equals(config)) { + // 非常明确的列直接比较 + log.warn("sql黑名单校验,表【"+name+"】中字段【"+config+"】禁止查询"); + return true; + } else { + // 使用表达式的列 只能判读字符串包含了 + String aliasColumn = config; + if (alias != null && alias.length() > 0) { + aliasColumn = alias + "." + config; + } + if (exp.indexOf(aliasColumn) > 0) { + log.warn("sql黑名单校验,表【"+name+"】中字段【"+config+"】禁止查询"); + return true; + } + } + } + } + return false; + } + + @Override + public String toString() { + return "QueryTable{" + + "name='" + name + '\'' + + ", alias='" + alias + '\'' + + ", fields=" + fields + + ", all=" + all + + '}'; + } + } + + public String getError(){ + // TODO + return "系统设置了安全规则,敏感表和敏感字段禁止查询,联系管理员授权!"; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/SecurityTools.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/SecurityTools.java new file mode 100644 index 0000000..9adc8bf --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/SecurityTools.java @@ -0,0 +1,80 @@ +package org.jeecg.common.util.security; + +import cn.hutool.core.codec.Base64Decoder; +import cn.hutool.core.codec.Base64Encoder; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.RSA; +import cn.hutool.crypto.asymmetric.Sign; +import cn.hutool.crypto.asymmetric.SignAlgorithm; +import cn.hutool.crypto.symmetric.AES; +import org.jeecg.common.util.security.entity.*; +import com.alibaba.fastjson.JSONObject; +import javax.crypto.SecretKey; +import java.security.KeyPair; + +/** + * @Description: SecurityTools + * @author: jeecg-boot + */ +public class SecurityTools { + public static final String ALGORITHM = "AES/ECB/PKCS5Padding"; + + public static SecurityResp valid(SecurityReq req) { + SecurityResp resp=new SecurityResp(); + String pubKey=req.getPubKey(); + String aesKey=req.getAesKey(); + String data=req.getData(); + String signData=req.getSignData(); + RSA rsa=new RSA(null, Base64Decoder.decode(pubKey)); + Sign sign= new Sign(SignAlgorithm.SHA1withRSA,null,pubKey); + + + + byte[] decryptAes = rsa.decrypt(aesKey, KeyType.PublicKey); + //log.info("rsa解密后的秘钥"+ Base64Encoder.encode(decryptAes)); + AES aes = SecureUtil.aes(decryptAes); + + String dencrptValue =aes.decryptStr(data); + //log.info("解密后报文"+dencrptValue); + resp.setData(JSONObject.parseObject(dencrptValue)); + + boolean verify = sign.verify(dencrptValue.getBytes(), Base64Decoder.decode(signData)); + resp.setSuccess(verify); + return resp; + } + + public static SecuritySignResp sign(SecuritySignReq req) { + SecretKey secretKey = SecureUtil.generateKey(ALGORITHM); + byte[] key= secretKey.getEncoded(); + String prikey=req.getPrikey(); + String data=req.getData(); + + AES aes = SecureUtil.aes(key); + aes.getSecretKey().getEncoded(); + String encrptData =aes.encryptBase64(data); + RSA rsa=new RSA(prikey,null); + byte[] encryptAesKey = rsa.encrypt(secretKey.getEncoded(), KeyType.PrivateKey); + //log.info(("rsa加密过的秘钥=="+Base64Encoder.encode(encryptAesKey)); + + Sign sign= new Sign(SignAlgorithm.SHA1withRSA,prikey,null); + byte[] signed = sign.sign(data.getBytes()); + + //log.info(("签名数据===》》"+Base64Encoder.encode(signed)); + + SecuritySignResp resp=new SecuritySignResp(); + resp.setAesKey(Base64Encoder.encode(encryptAesKey)); + resp.setData(encrptData); + resp.setSignData(Base64Encoder.encode(signed)); + return resp; + } + public static MyKeyPair generateKeyPair(){ + KeyPair keyPair= SecureUtil.generateKeyPair(SignAlgorithm.SHA1withRSA.getValue(),2048); + String priKey= Base64Encoder.encode(keyPair.getPrivate().getEncoded()); + String pubkey= Base64Encoder.encode(keyPair.getPublic().getEncoded()); + MyKeyPair resp=new MyKeyPair(); + resp.setPriKey(priKey); + resp.setPubKey(pubkey); + return resp; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/entity/MyKeyPair.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/entity/MyKeyPair.java new file mode 100644 index 0000000..d2cb849 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/entity/MyKeyPair.java @@ -0,0 +1,13 @@ +package org.jeecg.common.util.security.entity; + +import lombok.Data; + +/** + * @Description: MyKeyPair + * @author: jeecg-boot + */ +@Data +public class MyKeyPair { + private String priKey; + private String pubKey; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/entity/SecurityReq.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/entity/SecurityReq.java new file mode 100644 index 0000000..9714728 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/entity/SecurityReq.java @@ -0,0 +1,15 @@ +package org.jeecg.common.util.security.entity; + +import lombok.Data; + +/** + * @Description: SecurityReq + * @author: jeecg-boot + */ +@Data +public class SecurityReq { + private String data; + private String pubKey; + private String signData; + private String aesKey; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/entity/SecurityResp.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/entity/SecurityResp.java new file mode 100644 index 0000000..a62034c --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/entity/SecurityResp.java @@ -0,0 +1,14 @@ +package org.jeecg.common.util.security.entity; + +import com.alibaba.fastjson.JSONObject; +import lombok.Data; + +/** + * @Description: SecurityResp + * @author: jeecg-boot + */ +@Data +public class SecurityResp { + private Boolean success; + private JSONObject data; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/entity/SecuritySignReq.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/entity/SecuritySignReq.java new file mode 100644 index 0000000..9b43dcd --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/entity/SecuritySignReq.java @@ -0,0 +1,13 @@ +package org.jeecg.common.util.security.entity; + +import lombok.Data; + +/** + * @Description: SecuritySignReq + * @author: jeecg-boot + */ +@Data +public class SecuritySignReq { + private String data; + private String prikey; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/entity/SecuritySignResp.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/entity/SecuritySignResp.java new file mode 100644 index 0000000..ab618f4 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/entity/SecuritySignResp.java @@ -0,0 +1,14 @@ +package org.jeecg.common.util.security.entity; + +import lombok.Data; + +/** + * @Description: SecuritySignResp + * @author: jeecg-boot + */ +@Data +public class SecuritySignResp { + private String data; + private String signData; + private String aesKey; +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/superSearch/ObjectParseUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/superSearch/ObjectParseUtil.java new file mode 100644 index 0000000..5091e9c --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/superSearch/ObjectParseUtil.java @@ -0,0 +1,60 @@ +//package org.jeecg.common.util.superSearch; +// +//import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +// +///** +// * 判断类型,追加查询规则 +// * +// * @Author Scott +// * @Date 2019年02月14日 +// */ +//public class ObjectParseUtil { +// +// /** +// * +// * @param queryWrapper QueryWrapper +// * @param name 字段名字 +// * @param rule 查询规则 +// * @param value 查询条件值 +// */ +// public static void addCriteria(QueryWrapper queryWrapper, String name, QueryRuleEnum rule, Object value) { +// if (value == null || rule == null) { +// return; +// } +// switch (rule) { +// case GT: +// queryWrapper.gt(name, value); +// break; +// case GE: +// queryWrapper.ge(name, value); +// break; +// case LT: +// queryWrapper.lt(name, value); +// break; +// case LE: +// queryWrapper.le(name, value); +// break; +// case EQ: +// queryWrapper.eq(name, value); +// break; +// case NE: +// queryWrapper.ne(name, value); +// break; +// case IN: +// queryWrapper.in(name, (Object[]) value); +// break; +// case LIKE: +// queryWrapper.like(name, value); +// break; +// case LEFT_LIKE: +// queryWrapper.likeLeft(name, value); +// break; +// case RIGHT_LIKE: +// queryWrapper.likeRight(name, value); +// break; +// default: +// break; +// } +// } +// +//} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/superSearch/QueryRuleEnum.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/superSearch/QueryRuleEnum.java new file mode 100644 index 0000000..0bfb9d8 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/superSearch/QueryRuleEnum.java @@ -0,0 +1,71 @@ +//package org.jeecg.common.util.superSearch; +// +//import org.jeecg.common.util.oConvertUtils; +// +///** +// * Query 规则 常量 +// * @Author Scott +// * @Date 2019年02月14日 +// */ +//public enum QueryRuleEnum { +// +// /**查询规则 大于*/ +// GT(">","大于"), +// /**查询规则 大于等于*/ +// GE(">=","大于等于"), +// /**查询规则 小于*/ +// LT("<","小于"), +// /**查询规则 小于等于*/ +// LE("<=","小于等于"), +// /**查询规则 等于*/ +// EQ("=","等于"), +// /**查询规则 不等于*/ +// NE("!=","不等于"), +// /**查询规则 包含*/ +// IN("IN","包含"), +// /**查询规则 全模糊*/ +// LIKE("LIKE","全模糊"), +// /**查询规则 左模糊*/ +// LEFT_LIKE("LEFT_LIKE","左模糊"), +// /**查询规则 右模糊*/ +// RIGHT_LIKE("RIGHT_LIKE","右模糊"), +// /**查询规则 自定义SQL片段*/ +// SQL_RULES("EXTEND_SQL","自定义SQL片段"); +// +// private String value; +// +// private String msg; +// +// QueryRuleEnum(String value, String msg){ +// this.value = value; +// this.msg = msg; +// } +// +// public String getValue() { +// return value; +// } +// +// public void setValue(String value) { +// this.value = value; +// } +// +// public String getMsg() { +// return msg; +// } +// +// public void setMsg(String msg) { +// this.msg = msg; +// } +// +// public static QueryRuleEnum getByValue(String value){ +// if(oConvertUtils.isEmpty(value)) { +// return null; +// } +// for(QueryRuleEnum val :values()){ +// if (val.getValue().equals(value)){ +// return val; +// } +// } +// return null; +// } +//} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/superSearch/QueryRuleVo.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/superSearch/QueryRuleVo.java new file mode 100644 index 0000000..94d3408 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/superSearch/QueryRuleVo.java @@ -0,0 +1,15 @@ +//package org.jeecg.common.util.superSearch; +// +//import lombok.Data; +// +///** +// * @Description: QueryRuleVo +// * @author: jeecg-boot +// */ +//@Data +//public class QueryRuleVo { +// +// private String field; +// private String rule; +// private String val; +//} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/AutoPoiConfig.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/AutoPoiConfig.java new file mode 100644 index 0000000..26a7dd3 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/AutoPoiConfig.java @@ -0,0 +1,28 @@ +package org.jeecg.config; + +import org.jeecgframework.core.util.ApplicationContextUtil; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @Author: Scott + * @Date: 2018/2/7 + * @description: autopoi 配置类 + */ + +@Configuration +public class AutoPoiConfig { + + /** + * excel注解字典参数支持(导入导出字典值,自动翻译) + * 举例: @Excel(name = "性别", width = 15, dicCode = "sex") + * 1、导出的时候会根据字典配置,把值1,2翻译成:男、女; + * 2、导入的时候,会把男、女翻译成1,2存进数据库; + * @return + */ + @Bean + public ApplicationContextUtil applicationContextUtil() { + return new org.jeecgframework.core.util.ApplicationContextUtil(); + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/AutoPoiDictConfig.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/AutoPoiDictConfig.java new file mode 100644 index 0000000..12c4fd8 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/AutoPoiDictConfig.java @@ -0,0 +1,79 @@ +package org.jeecg.config; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Resource; + +import org.jeecg.common.api.CommonAPI; +import org.jeecg.common.system.vo.DictModel; +import org.jeecg.common.util.oConvertUtils; +import org.jeecgframework.dict.service.AutoPoiDictServiceI; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +import lombok.extern.slf4j.Slf4j; + +/** + * 描述:AutoPoi Excel注解支持字典参数设置 + * 举例: @Excel(name = "性别", width = 15, dicCode = "sex") + * 1、导出的时候会根据字典配置,把值1,2翻译成:男、女; + * 2、导入的时候,会把男、女翻译成1,2存进数据库; + * + * @Author:scott + * @since:2019-04-09 + * @Version:1.0 + */ +@Slf4j +@Service +public class AutoPoiDictConfig implements AutoPoiDictServiceI { + final static String EXCEL_SPLIT_TAG = "_"; + final static String TEMP_EXCEL_SPLIT_TAG = "---"; + + @Lazy + @Resource + private CommonAPI commonApi; + + /** + * 通过字典查询easypoi,所需字典文本 + * + * @Author:scott + * @since:2019-04-09 + * @return + */ + @Override + public String[] queryDict(String dicTable, String dicCode, String dicText) { + List dictReplaces = new ArrayList(); + List dictList = null; + // step.1 如果没有字典表则使用系统字典表 + if (oConvertUtils.isEmpty(dicTable)) { + dictList = commonApi.queryDictItemsByCode(dicCode); + } else { + try { + dicText = oConvertUtils.getString(dicText, dicCode); + dictList = commonApi.queryTableDictItemsByCode(dicTable, dicText, dicCode); + } catch (Exception e) { + log.error(e.getMessage(),e); + } + } + + + for (DictModel t : dictList) { + if(t!=null){ + //update-begin---author:scott Date:20211220 for:[issues/I4MBB3]@Excel dicText字段的值有下划线时,导入功能不能正确解析--- + if(t.getValue().contains(EXCEL_SPLIT_TAG)){ + String val = t.getValue().replace(EXCEL_SPLIT_TAG,TEMP_EXCEL_SPLIT_TAG); + dictReplaces.add(t.getText() + EXCEL_SPLIT_TAG + val); + }else{ + dictReplaces.add(t.getText() + EXCEL_SPLIT_TAG + t.getValue()); + } + //update-end---author:20211220 Date:20211220 for:[issues/I4MBB3]@Excel dicText字段的值有下划线时,导入功能不能正确解析--- + } + } + if (dictReplaces != null && dictReplaces.size() != 0) { + log.info("---AutoPoi--Get_DB_Dict------"+ dictReplaces.toString()); + return dictReplaces.toArray(new String[dictReplaces.size()]); + } + return null; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/CorsFilterCondition.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/CorsFilterCondition.java new file mode 100644 index 0000000..b43cc8e --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/CorsFilterCondition.java @@ -0,0 +1,23 @@ +package org.jeecg.config; + +import org.jeecg.common.constant.CommonConstant; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +/** + * 跨域配置加载条件 + * @author: jeecg-boot + */ +public class CorsFilterCondition implements Condition { + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + Object object = context.getEnvironment().getProperty(CommonConstant.CLOUD_SERVER_KEY); + //如果没有服务注册发现的配置 说明是单体应用 则加载跨域配置 返回true + if(object==null){ + return true; + } + return false; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/FileShowConfig.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/FileShowConfig.java new file mode 100644 index 0000000..e639b79 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/FileShowConfig.java @@ -0,0 +1,26 @@ +package org.jeecg.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 图片展示 + */ +@Component +@ConfigurationProperties(prefix = "spring.download-path") +@Data +public class FileShowConfig { + + + public String intranet; + + public String outernet; + + public String finalurlOut; + + public String externalApi; + + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/JeecgBaseConfig.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/JeecgBaseConfig.java new file mode 100644 index 0000000..3c38f7b --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/JeecgBaseConfig.java @@ -0,0 +1,94 @@ +package org.jeecg.config; + +import org.jeecg.config.vo.DomainUrl; +import org.jeecg.config.vo.Path; +import org.jeecg.config.vo.Shiro; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + + +/** + * 加载项目配置 + * @author: jeecg-boot + */ +@Component("jeecgBaseConfig") +@ConfigurationProperties(prefix = "jeecg") +public class JeecgBaseConfig { + /** + * 签名密钥串(字典等敏感接口) + * @TODO 降低使用成本加的默认值,实际以 yml配置 为准 + */ + private String signatureSecret = "dd05f1c54d63749eda95f9fa6d49v442a"; + /** + * 需要加强校验的接口清单 + */ + private String signUrls; + + /** + * 是否启用安全模式 + */ + private Boolean safeMode = false; + /** + * shiro拦截排除 + */ + private Shiro shiro; + /** + * 上传文件配置 + */ + private Path path; + + /** + * 前端页面访问地址 + * pc: http://localhost:3100 + * app: http://localhost:8051 + */ + private DomainUrl domainUrl; + + public Boolean getSafeMode() { + return safeMode; + } + + public void setSafeMode(Boolean safeMode) { + this.safeMode = safeMode; + } + + public String getSignatureSecret() { + return signatureSecret; + } + + public void setSignatureSecret(String signatureSecret) { + this.signatureSecret = signatureSecret; + } + + public Shiro getShiro() { + return shiro; + } + + public void setShiro(Shiro shiro) { + this.shiro = shiro; + } + + public Path getPath() { + return path; + } + + public void setPath(Path path) { + this.path = path; + } + + public DomainUrl getDomainUrl() { + return domainUrl; + } + + public void setDomainUrl(DomainUrl domainUrl) { + this.domainUrl = domainUrl; + } + + public String getSignUrls() { + return signUrls; + } + + public void setSignUrls(String signUrls) { + this.signUrls = signUrls; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/JeecgCloudCondition.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/JeecgCloudCondition.java new file mode 100644 index 0000000..34b8e9a --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/JeecgCloudCondition.java @@ -0,0 +1,23 @@ +package org.jeecg.config; + +import org.jeecg.common.constant.CommonConstant; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +/** + * 微服务环境加载条件 + * @author: jeecg-boot + */ +public class JeecgCloudCondition implements Condition { + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + Object object = context.getEnvironment().getProperty(CommonConstant.CLOUD_SERVER_KEY); + //如果没有服务注册发现的配置 说明是单体应用 + if(object==null){ + return false; + } + return true; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/LocalUploadConfig.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/LocalUploadConfig.java new file mode 100644 index 0000000..57e7c0f --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/LocalUploadConfig.java @@ -0,0 +1,25 @@ +package org.jeecg.config; + +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.util.LocalUploadUtil; +import org.jeecg.common.util.MyUploadUtil; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Minio文件上传配置文件 + */ +@Slf4j +@Configuration +public class LocalUploadConfig { + + @Value(value = "${jeecg.path.upload}") + private String uploadpath; + + @Bean + public void initLu(){ + LocalUploadUtil.setUploadpath(uploadpath); + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/RestTemplateConfig.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/RestTemplateConfig.java new file mode 100644 index 0000000..860889a --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/RestTemplateConfig.java @@ -0,0 +1,31 @@ +package org.jeecg.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +/** +* 优雅的http请求方式RestTemplate +* @author: jeecg-boot +* @Return: +*/ +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate(ClientHttpRequestFactory factory) { + return new RestTemplate(factory); + } + + @Bean + public ClientHttpRequestFactory simpleClientHttpRequestFactory() { + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + //ms毫秒 + factory.setReadTimeout(5000); + //ms毫秒 + factory.setConnectTimeout(15000); + return factory; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/Swagger2Config.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/Swagger2Config.java new file mode 100644 index 0000000..a3cdcb4 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/Swagger2Config.java @@ -0,0 +1,184 @@ +package org.jeecg.config; + + +import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; +import io.swagger.annotations.ApiOperation; +import org.jeecg.common.constant.CommonConstant; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.util.ReflectionUtils; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; +import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.ParameterBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.oas.annotations.EnableOpenApi; +import springfox.documentation.schema.ModelRef; +import springfox.documentation.service.*; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.contexts.SecurityContext; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; +import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @Author scott + */ +@Configuration +@EnableSwagger2 //开启 Swagger2 +@EnableKnife4j //开启 knife4j,可以不写 +@Import(BeanValidatorPluginsConfiguration.class) +public class Swagger2Config implements WebMvcConfigurer { + + /** + * + * 显示swagger-ui.html文档展示页,还必须注入swagger资源: + * + * @param registry + */ + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); + registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/"); + registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); + } + + /** + * swagger2的配置文件,这里可以配置swagger2的一些基本的内容,比如扫描的包等等 + * + * @return Docket + */ + @Bean(value = "defaultApi2") + public Docket defaultApi2() { + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(apiInfo()) + .select() + //此包路径下的类,才生成接口文档 + .apis(RequestHandlerSelectors.basePackage("org.jeecg")) + //加了ApiOperation注解的类,才生成接口文档 + .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class)) + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + .paths(PathSelectors.any()) + .build() + .securitySchemes(Collections.singletonList(securityScheme())) + .securityContexts(securityContexts()); + //.globalOperationParameters(setHeaderToken()); + } + + /*** + * oauth2配置 + * 需要增加swagger授权回调地址 + * http://localhost:8888/webjars/springfox-swagger-ui/o2c.html + * @return + */ + @Bean + SecurityScheme securityScheme() { + return new ApiKey(CommonConstant.X_ACCESS_TOKEN, CommonConstant.X_ACCESS_TOKEN, "header"); + } + /** + * JWT token + * @return + */ + private List setHeaderToken() { + ParameterBuilder tokenPar = new ParameterBuilder(); + List pars = new ArrayList<>(); + tokenPar.name(CommonConstant.X_ACCESS_TOKEN).description("token").modelRef(new ModelRef("string")).parameterType("header").required(false).build(); + pars.add(tokenPar.build()); + return pars; + } + + /** + * api文档的详细信息函数,注意这里的注解引用的是哪个 + * + * @return + */ + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + // //大标题 + .title("Jeecg-Boot 后台服务API接口文档") + // 版本号 + .version("1.0") +// .termsOfServiceUrl("NO terms of service") + // 描述 + .description("后台API接口") + // 作者 + .contact(new Contact("北京国炬信息技术有限公司","www.jeccg.com","jeecgos@163.com")) + // .contact("JEECG团队") + .license("The Apache License, Version 2.0") + .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html") + .build(); + } + + /** + * 新增 securityContexts 保持登录状态 + */ + private List securityContexts() { + return new ArrayList( + Collections.singleton(SecurityContext.builder() + .securityReferences(defaultAuth()) + .forPaths(PathSelectors.regex("^(?!auth).*$")) + .build()) + ); + } + + private List defaultAuth() { + AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + return new ArrayList( + Collections.singleton(new SecurityReference(CommonConstant.X_ACCESS_TOKEN, authorizationScopes))); + } + + /** + * 解决springboot2.6 和springfox不兼容问题 + * @return + */ + @Bean + public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { + return new BeanPostProcessor() { + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { + customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); + } + return bean; + } + + private void customizeSpringfoxHandlerMappings(List mappings) { + List copy = mappings.stream() + .filter(mapping -> mapping.getPatternParser() == null) + .collect(Collectors.toList()); + mappings.clear(); + mappings.addAll(copy); + } + + @SuppressWarnings("unchecked") + private List getHandlerMappings(Object bean) { + try { + Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); + field.setAccessible(true); + return (List) field.get(bean); + } catch (IllegalArgumentException | IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + }; + } + + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/ThreadPoolConfig.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/ThreadPoolConfig.java new file mode 100644 index 0000000..c2a5db1 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/ThreadPoolConfig.java @@ -0,0 +1,64 @@ +package org.jeecg.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 支持调度功能的线程池配置(修复setQueueCapacity不支持问题) + */ +@Configuration +public class ThreadPoolConfig { + + /** + * 核心线程数(调度线程池的核心线程会一直存活) + */ + private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2; + + /** + * 最大线程数 + */ + private static final int MAX_POOL_SIZE = 50; + + /** + * 空闲线程存活时间(仅对超出核心线程数的线程有效) + */ + private static final int KEEP_ALIVE_SECONDS = 60; + + @Bean(name = "asyncTaskExecutor") + public ScheduledExecutorService httpTaskExecutor() { + // 创建线程工厂,设置线程名称 + ThreadFactory threadFactory = new ThreadFactory() { + private final AtomicInteger counter = new AtomicInteger(1); + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, "http-client-schedule-thread-" + counter.getAndIncrement()); + thread.setDaemon(false); // 非守护线程 + thread.setPriority(Thread.NORM_PRIORITY); + return thread; + } + }; + + // 创建ScheduledThreadPoolExecutor实例 + ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor( + CORE_POOL_SIZE, + threadFactory, + new ThreadPoolExecutor.CallerRunsPolicy() // 任务满时让提交者线程执行,避免任务丢失 + ); + + // 配置参数(仅保留支持的方法) + executor.setMaximumPoolSize(MAX_POOL_SIZE); // 设置最大线程数 + executor.setKeepAliveTime(KEEP_ALIVE_SECONDS, TimeUnit.SECONDS); + executor.allowCoreThreadTimeOut(false); // 核心线程不超时销毁 + + // 对于ScheduledThreadPoolExecutor,队列是DelayedWorkQueue(无界) + // 如需限制任务总量,可通过拒绝策略+监控实现,例如: + // 1. 使用CallerRunsPolicy让提交线程等待,间接限制任务提交速度 + // 2. 结合监控告警,当队列任务过多时及时扩容或限流 + + return executor; + } +} + \ No newline at end of file diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebMvcConfiguration.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebMvcConfiguration.java new file mode 100644 index 0000000..59b2557 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebMvcConfiguration.java @@ -0,0 +1,155 @@ +package org.jeecg.config; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; +import io.micrometer.prometheus.PrometheusMeterRegistry; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.boot.actuate.trace.http.InMemoryHttpTraceRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.List; + +/** + * Spring Boot 2.0 解决跨域问题 + * + * @Author qinfeng + * + */ +@Configuration +public class WebMvcConfiguration implements WebMvcConfigurer { + + @Autowired + JeecgBaseConfig jeecgBaseConfig; + @Value("${spring.resource.static-locations:}") + private String staticLocations; + + @Autowired(required = false) + private PrometheusMeterRegistry prometheusMeterRegistry; + + /** + * 静态资源的配置 - 使得可以从磁盘中读取 Html、图片、视频、音频等 + */ + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/**") + //update-begin-author:taoyan date:20211116 for: jeecg.path.webapp配置无效 #3126 + .addResourceLocations("file:" + jeecgBaseConfig.getPath().getUpload() + "//") + .addResourceLocations("file:" + jeecgBaseConfig.getPath().getWebapp() + "//") + //update-end-author:taoyan date:20211116 for: jeecg.path.webapp配置无效 #3126 + .addResourceLocations(staticLocations.split(",")); + } + + /** + * 方案一: 默认访问根路径跳转 doc.html页面 (swagger文档页面) + * 方案二: 访问根路径改成跳转 index.html页面 (简化部署方案: 可以把前端打包直接放到项目的 webapp,上面的配置) + */ + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/").setViewName("doc.html"); + } + + @Bean + @Conditional(CorsFilterCondition.class) + public CorsFilter corsFilter() { + final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); + final CorsConfiguration corsConfiguration = new CorsConfiguration(); + //是否允许请求带有验证信息 + corsConfiguration.setAllowCredentials(true); + // 允许访问的客户端域名 + corsConfiguration.addAllowedOriginPattern("*"); + // 允许服务端访问的客户端请求头 + corsConfiguration.addAllowedHeader("*"); + // 允许访问的方法名,GET POST等 + corsConfiguration.addAllowedMethod("*"); + urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); + return new CorsFilter(urlBasedCorsConfigurationSource); + } + @Override + public void configureMessageConverters(List> converters) { + MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(objectMapper()); + converters.add(jackson2HttpMessageConverter); + } + + /** + * 自定义ObjectMapper + */ + @Bean + @Primary + public ObjectMapper objectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + //处理bigDecimal + objectMapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN); + objectMapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS); + //处理失败 + objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false); + objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, false); + //默认的处理日期时间格式 + objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); + JavaTimeModule javaTimeModule = new JavaTimeModule(); + javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss"))); + javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss"))); + objectMapper.registerModule(javaTimeModule); + return objectMapper; + } + + /** + * SpringBootAdmin的Httptrace不见了 + * https://blog.csdn.net/u013810234/article/details/110097201 + */ + @Bean + public InMemoryHttpTraceRepository getInMemoryHttpTrace(){ + return new InMemoryHttpTraceRepository(); + } + + + /** + * 解决metrics端点不显示jvm信息的问题(zyf) + */ + @Bean + InitializingBean forcePrometheusPostProcessor(BeanPostProcessor meterRegistryPostProcessor) { + return () -> meterRegistryPostProcessor.postProcessAfterInitialization(prometheusMeterRegistry, ""); + } + +// /** +// * 注册拦截器【拦截器拦截参数,自动切换数据源——后期实现多租户切换数据源功能】 +// * @param registry +// */ +// @Override +// public void addInterceptors(InterceptorRegistry registry) { +// registry.addInterceptor(new DynamicDatasourceInterceptor()).addPathPatterns("/test/dynamic/**"); +// } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebSocketConfig.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebSocketConfig.java new file mode 100644 index 0000000..597e7b4 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebSocketConfig.java @@ -0,0 +1,37 @@ +package org.jeecg.config; + +import org.jeecg.config.filter.WebsocketFilter; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +/** + * @Description: WebSocketConfig + * @author: jeecg-boot + */ +@Configuration +public class WebSocketConfig { + /** + * 注入ServerEndpointExporter, + * 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint + */ + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } + + @Bean + public WebsocketFilter websocketFilter(){ + return new WebsocketFilter(); + } + + @Bean + public FilterRegistrationBean getFilterRegistrationBean(){ + FilterRegistrationBean bean = new FilterRegistrationBean(); + bean.setFilter(websocketFilter()); + bean.addUrlPatterns("/websocket/*", "/eoaSocket/*", "/newsWebsocket/*", "/vxeSocket/*"); + return bean; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/activemq/BeanConfigEquipment.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/activemq/BeanConfigEquipment.java new file mode 100644 index 0000000..234427d --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/activemq/BeanConfigEquipment.java @@ -0,0 +1,114 @@ +package org.jeecg.config.activemq; + +import org.apache.activemq.ActiveMQConnectionFactory; +import org.apache.activemq.ActiveMQSession; +import org.apache.activemq.command.ActiveMQQueue; +import org.apache.activemq.command.ActiveMQTopic; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jms.config.JmsListenerContainerFactory; +import org.springframework.jms.config.SimpleJmsListenerContainerFactory; +import org.springframework.jms.core.JmsMessagingTemplate; + +import javax.jms.ConnectionFactory; +import javax.jms.Queue; +import javax.jms.Topic; + +/** + * client服务 与 equipment服务的消息服务配置 + */ +@Configuration +public class BeanConfigEquipment { + + + @Value(value="${activemq.device.brokerUrl}") + private String brokerUrlEquipment; + + @Value(value="${activemq.device.username}") + private String username; + + @Value(value="${activemq.device.password}") + private String password; + + + //==========topic================== + + /** + * 设备管理服务投递:设备数据,给前置服务的topic + */ + @Value("topic_msg") + private String topicMsg; + + /** + * 设备管理服务投递:app版本数据,给前置服务的topic + */ + @Value("topic_msg_app") + private String topicMsgApp; + + + /** + * 设备管理服务投递:其他数据,给前置服务的topic + */ + @Value("topic_msg_by_equipment") + private String topicMsgByEquipment; + + + @Bean(name = "topicMsg") + public Topic topicMsg() { + return new ActiveMQTopic(topicMsg); + } + + @Bean(name = "topicMsgApp") + public Topic topicMsgApp() { + return new ActiveMQTopic(topicMsgApp); + } + + @Bean(name = "topicMsgByEquipment") + public Topic topicMsgByEquipment() { + return new ActiveMQTopic(topicMsgByEquipment); + } + + + //=================================================== + + @Bean(name = "equipmentConnectionFactory") + public ConnectionFactory equipmentConnectionFactory(){ + return new ActiveMQConnectionFactory(username, password, brokerUrlEquipment); + } + + @Bean(name = "equipmentJmsMessageTemplate") + public JmsMessagingTemplate equipmentJmsMessageTemplate(){ + return new JmsMessagingTemplate(equipmentConnectionFactory()); + } + + // 在Queue模式中,对消息的监听需要对containerFactory进行配置 + @Bean("equipmentQueueListener") + public JmsListenerContainerFactory equipmentQueueJmsListenerContainerFactory(@Qualifier("equipmentConnectionFactory")ConnectionFactory connectionFactory){ + SimpleJmsListenerContainerFactory factory = new SimpleJmsListenerContainerFactory(); + // 关闭事务 + factory.setSessionTransacted(false); + // 手动确认消息 + factory.setSessionAcknowledgeMode(ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE); + factory.setConnectionFactory(connectionFactory); + factory.setPubSubDomain(false); + return factory; + } + + //在Topic模式中,对消息的监听需要对containerFactory进行配置 + @Bean("equipmentTopicListener") + public JmsListenerContainerFactory equipmentTopicJmsListenerContainerFactory(@Qualifier("equipmentConnectionFactory") ConnectionFactory connectionFactory){ + SimpleJmsListenerContainerFactory factory = new SimpleJmsListenerContainerFactory(); + // 关闭事务 + factory.setSessionTransacted(false); + // 手动确认消息 + factory.setSessionAcknowledgeMode(ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE); + factory.setConnectionFactory(connectionFactory); + factory.setPubSubDomain(true); + return factory; + } + + + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/SecurityFilter.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/SecurityFilter.java new file mode 100644 index 0000000..870d27d --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/SecurityFilter.java @@ -0,0 +1,18 @@ +package org.jeecg.config.filter; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + + +/** + * @author Jiang Shunzhi + */ +public class SecurityFilter implements Filter { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { + SecurityHttpServletRequestWrapper xssRequest = new SecurityHttpServletRequestWrapper((HttpServletRequest) request); + filterChain.doFilter(xssRequest, response); + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/SecurityHttpServletRequestWrapper.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/SecurityHttpServletRequestWrapper.java new file mode 100644 index 0000000..7c60800 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/SecurityHttpServletRequestWrapper.java @@ -0,0 +1,111 @@ +package org.jeecg.config.filter; + +import org.jeecg.common.util.XssUtils; +import org.springframework.util.StreamUtils; +import org.springframework.web.util.HtmlUtils; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Shunzhi Jiang + * @since 2023/8/15 + */ +public class SecurityHttpServletRequestWrapper extends HttpServletRequestWrapper { + + private final Map parameterMap = new HashMap<>(); + private byte[] requestBody = null; +// + public SecurityHttpServletRequestWrapper(HttpServletRequest request) { + super(request); + try { + Map params = request.getParameterMap(); + params.forEach((name, values) -> { + if (values == null || values.length == 0) { + return; + } + for (int i = 0; i < values.length; i++) { + values[i] = XssUtils.scriptXss(values[i]); + values[i] = HtmlUtils.htmlEscape(values[i]); + } + parameterMap.put(name, values); + }); + requestBody = StreamUtils.copyToByteArray(request.getInputStream()); + if (request.getContentType() != null && "application/json".equals(request.getContentType())) { + this.setRequestBody(getRequestBody()); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public String getParameter(String name) { + String[] values = getParameterValues(name); + if (values != null && values.length > 0) { + return values[0]; + } + return null; + } + + @Override + public Map getParameterMap() { + return parameterMap; + } + + @Override + public String[] getParameterValues(String name) { + return parameterMap.get(name); + } + + public String getRequestBody() throws UnsupportedEncodingException { + return new String(requestBody, StandardCharsets.UTF_8); + } + + public void setRequestBody(String jsonBody) throws UnsupportedEncodingException { + if (jsonBody != null && !jsonBody.isEmpty()) { + this.requestBody = XssUtils.scriptXss(jsonBody).getBytes(StandardCharsets.UTF_8); + } + } + + @Override + public ServletInputStream getInputStream() throws IOException { + if (requestBody == null) { + requestBody = new byte[0]; + } + final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody); + return new ServletInputStream() { + @Override + public int read() throws IOException { + return bais.read(); + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public void setReadListener(ReadListener listener) { + + } + }; + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/WebsocketFilter.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/WebsocketFilter.java new file mode 100644 index 0000000..bb84a79 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/WebsocketFilter.java @@ -0,0 +1,58 @@ +package org.jeecg.config.filter; + +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.api.CommonAPI; +import org.jeecg.common.util.RedisUtil; +import org.jeecg.common.util.SpringContextUtils; +import org.jeecg.common.util.TokenUtils; +import org.jeecg.common.util.oConvertUtils; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URLEncoder; + +/** + * websocket 前端将token放到子协议里传入 与后端建立连接时需要用到http协议,此处用于校验token的有效性 + * + * @Author taoYan + * @Date 2022/4/21 17:01 + **/ +@Slf4j +public class WebsocketFilter implements Filter { + + private static final String TOKEN_KEY = "Sec-WebSocket-Protocol"; + + private static CommonAPI commonApi; + + private static RedisUtil redisUtil; + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + if (commonApi == null) { + commonApi = SpringContextUtils.getBean(CommonAPI.class); + } + if (redisUtil == null) { + redisUtil = SpringContextUtils.getBean(RedisUtil.class); + } + HttpServletRequest request = (HttpServletRequest) servletRequest; + String token = request.getHeader(TOKEN_KEY); + + log.debug("Websocket连接 Token安全校验,Path = {},token:{}", request.getRequestURI(), token); + + try { + TokenUtils.verifyToken(token, commonApi, redisUtil); + } catch (Exception exception) { + //log.error("Websocket连接 Token安全校验失败,IP:{}, Token:{}, Path = {},异常:{}", oConvertUtils.getIpAddrByRequest(request), token, request.getRequestURI(), exception.getMessage()); + log.debug("Websocket连接 Token安全校验失败,IP:{}, Token:{}, Path = {},异常:{}", oConvertUtils.getIpAddrByRequest(request), token, request.getRequestURI(), exception.getMessage()); + return; + } + HttpServletResponse response = (HttpServletResponse) servletResponse; + //代码注入-HTTP响应截断:中危 + token = URLEncoder.encode(token, "UTF-8"); + response.setHeader(TOKEN_KEY, token); + filterChain.doFilter(servletRequest, servletResponse); + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/JeecgTenantParser.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/JeecgTenantParser.java new file mode 100644 index 0000000..5233fdb --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/JeecgTenantParser.java @@ -0,0 +1,130 @@ +//package org.jeecg.config.mybatis; +// +//import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser; +//import net.sf.jsqlparser.expression.BinaryExpression; +//import net.sf.jsqlparser.expression.Expression; +//import net.sf.jsqlparser.expression.Parenthesis; +//import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +//import net.sf.jsqlparser.expression.operators.conditional.OrExpression; +//import net.sf.jsqlparser.expression.operators.relational.*; +//import net.sf.jsqlparser.schema.Column; +//import net.sf.jsqlparser.schema.Table; +//import net.sf.jsqlparser.statement.select.*; +// +//import java.util.List; +// +///** +// * 复写租户条件 +// */ +//public class JeecgTenantParser extends TenantSqlParser { +// +// /** +// * @param expression +// * @param table +// * @return +// */ +// protected Expression processTableAlias(Expression expression, Table table) { +// String tableAliasName; +// if (table.getAlias() == null) { +// tableAliasName = table.getName(); +// } else { +// tableAliasName = table.getAlias().getName(); +// } +// +// // in +// if (expression instanceof InExpression) { +// InExpression in = (InExpression) expression; +// if (in.getLeftExpression() instanceof Column) { +// setTableAliasNameForColumn((Column) in.getLeftExpression(), tableAliasName); +// } +// +// // 比较操作 +// } else if (expression instanceof BinaryExpression) { +// BinaryExpression compare = (BinaryExpression) expression; +// if (compare.getLeftExpression() instanceof Column) { +// setTableAliasNameForColumn((Column) compare.getLeftExpression(), tableAliasName); +// } else if (compare.getRightExpression() instanceof Column) { +// setTableAliasNameForColumn((Column) compare.getRightExpression(), tableAliasName); +// } +// +// // between +// } else if (expression instanceof Between) { +// Between between = (Between) expression; +// if (between.getLeftExpression() instanceof Column) { +// setTableAliasNameForColumn((Column) between.getLeftExpression(), tableAliasName); +// } +// } +// return expression; +// } +// +// private void setTableAliasNameForColumn(Column column, String tableAliasName) { +// column.setColumnName(tableAliasName + "." + column.getColumnName()); +// } +// +// /** +// * 默认是按 tenant_id=1 按等于条件追加 +// * +// * @param currentExpression 现有的条件:比如你原来的sql查询条件 +// * @param table +// * @return +// */ +// @Override +// protected Expression builderExpression(Expression currentExpression, Table table) { +// final Expression tenantExpression = this.getTenantHandler().getTenantId(true); +// Expression appendExpression; +// if (!(tenantExpression instanceof SupportsOldOracleJoinSyntax)) { +// appendExpression = new EqualsTo(); +// ((EqualsTo) appendExpression).setLeftExpression(this.getAliasColumn(table)); +// ((EqualsTo) appendExpression).setRightExpression(tenantExpression); +// } else { +// appendExpression = processTableAlias(tenantExpression, table); +// } +// if (currentExpression == null) { +// return appendExpression; +// } +// if (currentExpression instanceof BinaryExpression) { +// BinaryExpression binaryExpression = (BinaryExpression) currentExpression; +// if (binaryExpression.getLeftExpression() instanceof FromItem) { +// processFromItem((FromItem) binaryExpression.getLeftExpression()); +// } +// if (binaryExpression.getRightExpression() instanceof FromItem) { +// processFromItem((FromItem) binaryExpression.getRightExpression()); +// } +// } else if (currentExpression instanceof InExpression) { +// InExpression inExp = (InExpression) currentExpression; +// ItemsList rightItems = inExp.getRightItemsList(); +// if (rightItems instanceof SubSelect) { +// processSelectBody(((SubSelect) rightItems).getSelectBody()); +// } +// } +// if (currentExpression instanceof OrExpression) { +// return new AndExpression(new Parenthesis(currentExpression), appendExpression); +// } else { +// return new AndExpression(currentExpression, appendExpression); +// } +// } +// +// @Override +// protected void processPlainSelect(PlainSelect plainSelect, boolean addColumn) { +// FromItem fromItem = plainSelect.getFromItem(); +// if (fromItem instanceof Table) { +// Table fromTable = (Table) fromItem; +// if (!this.getTenantHandler().doTableFilter(fromTable.getName())) { +// plainSelect.setWhere(builderExpression(plainSelect.getWhere(), fromTable)); +// if (addColumn) { +// plainSelect.getSelectItems().add(new SelectExpressionItem(new Column(this.getTenantHandler().getTenantIdColumn()))); +// } +// } +// } else { +// processFromItem(fromItem); +// } +// List joins = plainSelect.getJoins(); +// if (joins != null && joins.size() > 0) { +// joins.forEach(j -> { +// processJoin(j); +// processFromItem(j.getRightItem()); +// }); +// } +// } +// +//} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisInterceptor.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisInterceptor.java new file mode 100644 index 0000000..c9dc36f --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisInterceptor.java @@ -0,0 +1,166 @@ +package org.jeecg.config.mybatis; + +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.binding.MapperMethod.ParamMap; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlCommandType; +import org.apache.ibatis.plugin.*; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.util.oConvertUtils; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Field; +import java.util.Date; +import java.util.Properties; + +/** + * mybatis拦截器,自动注入创建人、创建时间、修改人、修改时间 + * @Author scott + * @Date 2019-01-19 + * + */ +@Slf4j +@Component +@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) }) +public class MybatisInterceptor implements Interceptor { + + @Override + public Object intercept(Invocation invocation) throws Throwable { + MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; + String sqlId = mappedStatement.getId(); + log.debug("------sqlId------" + sqlId); + SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); + Object parameter = invocation.getArgs()[1]; + log.debug("------sqlCommandType------" + sqlCommandType); + + if (parameter == null) { + return invocation.proceed(); + } + if (SqlCommandType.INSERT == sqlCommandType) { + LoginUser sysUser = this.getLoginUser(); + Field[] fields = oConvertUtils.getAllFields(parameter); + for (Field field : fields) { + log.debug("------field.name------" + field.getName()); + try { + if ("createBy".equals(field.getName())) { + field.setAccessible(true); + Object localCreateBy = field.get(parameter); + field.setAccessible(false); + if (localCreateBy == null || "".equals(localCreateBy)) { + if (sysUser != null) { + // 登录人账号 + field.setAccessible(true); + field.set(parameter, sysUser.getUsername()); + field.setAccessible(false); + } + } + } + // 注入创建时间 + if ("createTime".equals(field.getName())) { + field.setAccessible(true); + Object localCreateDate = field.get(parameter); + field.setAccessible(false); + if (localCreateDate == null || "".equals(localCreateDate)) { + field.setAccessible(true); + field.set(parameter, new Date()); + field.setAccessible(false); + } + } + //注入部门编码 + if ("sysOrgCode".equals(field.getName())) { + field.setAccessible(true); + Object localSysOrgCode = field.get(parameter); + field.setAccessible(false); + if (localSysOrgCode == null || "".equals(localSysOrgCode)) { + // 获取登录用户信息 + if (sysUser != null) { + field.setAccessible(true); + field.set(parameter, sysUser.getOrgCode()); + field.setAccessible(false); + } + } + } + } catch (Exception e) { + } + } + } + if (SqlCommandType.UPDATE == sqlCommandType) { + LoginUser sysUser = this.getLoginUser(); + Field[] fields = null; + if (parameter instanceof ParamMap) { + ParamMap p = (ParamMap) parameter; + //update-begin-author:scott date:20190729 for:批量更新报错issues/IZA3Q-- + String et = "et"; + if (p.containsKey(et)) { + parameter = p.get(et); + } else { + parameter = p.get("param1"); + } + //update-end-author:scott date:20190729 for:批量更新报错issues/IZA3Q- + + //update-begin-author:scott date:20190729 for:更新指定字段时报错 issues/#516- + if (parameter == null) { + return invocation.proceed(); + } + //update-end-author:scott date:20190729 for:更新指定字段时报错 issues/#516- + + fields = oConvertUtils.getAllFields(parameter); + } else { + fields = oConvertUtils.getAllFields(parameter); + } + + for (Field field : fields) { + log.debug("------field.name------" + field.getName()); + try { + if ("updateBy".equals(field.getName())) { + //获取登录用户信息 + if (sysUser != null) { + // 登录账号 + field.setAccessible(true); + field.set(parameter, sysUser.getUsername()); + field.setAccessible(false); + } + } + if ("updateTime".equals(field.getName())) { + field.setAccessible(true); + field.set(parameter, new Date()); + field.setAccessible(false); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + return invocation.proceed(); + } + + @Override + public Object plugin(Object target) { + return Plugin.wrap(target, this); + } + + @Override + public void setProperties(Properties properties) { + // TODO Auto-generated method stub + } + + //update-begin--Author:scott Date:20191213 for:关于使用Quzrtz 开启线程任务, #465 + /** + * 获取登录用户 + * @return + */ + private LoginUser getLoginUser() { + LoginUser sysUser = null; + try { + sysUser = SecurityUtils.getSubject().getPrincipal() != null ? (LoginUser) SecurityUtils.getSubject().getPrincipal() : null; + } catch (Exception e) { + //e.printStackTrace(); + sysUser = null; + } + return sysUser; + } + //update-end--Author:scott Date:20191213 for:关于使用Quzrtz 开启线程任务, #465 + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java new file mode 100644 index 0000000..c2dd3e6 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java @@ -0,0 +1,91 @@ +package org.jeecg.config.mybatis; + +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.LongValue; +import org.jeecg.common.util.oConvertUtils; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; + +/** + * 单数据源配置(jeecg.datasource.open = false时生效) + * @Author zhoujf + * + */ +@Configuration +@MapperScan(value={"org.jeecg.**.mapper*"}) +public class MybatisPlusSaasConfig { + /** + * tenant_id 字段名 + */ + private static final String TENANT_FIELD_NAME = "tenant_id"; + /** + * 哪些表需要做多租户 表需要添加一个字段 tenant_id + */ + private static final List tenantTable = new ArrayList(); + + static { + tenantTable.add("demo"); + +// //角色、菜单、部门 +// tenantTable.add("sys_role"); +// tenantTable.add("sys_permission"); +// tenantTable.add("sys_depart"); + } + + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor + interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() { + @Override + public Expression getTenantId() { + String tenant_id = oConvertUtils.getString(TenantContext.getTenant(),"0"); + return new LongValue(tenant_id); + } + + @Override + public String getTenantIdColumn(){ + return TENANT_FIELD_NAME; + } + + // 返回 true 表示不走租户逻辑 + @Override + public boolean ignoreTable(String tableName) { + for(String temp: tenantTable){ + if(temp.equalsIgnoreCase(tableName)){ + return false; + } + } + return true; + } + })); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); + return interceptor; + } + +// /** +// * 下个版本会删除,现在为了避免缓存出现问题不得不配置 +// * @return +// */ +// @Bean +// public ConfigurationCustomizer configurationCustomizer() { +// return configuration -> configuration.setUseDeprecatedExecutor(false); +// } +// /** +// * mybatis-plus SQL执行效率插件【生产环境可以关闭】 +// */ +// @Bean +// public PerformanceInterceptor performanceInterceptor() { +// return new PerformanceInterceptor(); +// } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/TenantContext.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/TenantContext.java new file mode 100644 index 0000000..23b4213 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/TenantContext.java @@ -0,0 +1,25 @@ +package org.jeecg.config.mybatis; + +import lombok.extern.slf4j.Slf4j; + +/** + * 多租户 tenant_id存储器 + */ +@Slf4j +public class TenantContext { + + private static ThreadLocal currentTenant = new ThreadLocal<>(); + + public static void setTenant(String tenant) { + log.debug(" setting tenant to " + tenant); + currentTenant.set(tenant); + } + + public static String getTenant() { + return currentTenant.get(); + } + + public static void clear(){ + currentTenant.remove(); + } +} \ No newline at end of file diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/ThreadLocalDataHelper.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/ThreadLocalDataHelper.java new file mode 100644 index 0000000..0b60721 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/ThreadLocalDataHelper.java @@ -0,0 +1,62 @@ +package org.jeecg.config.mybatis; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import java.util.concurrent.ConcurrentHashMap; + + +/** + * @Description: 本地线程变量存储工具类 + * @author: lsq + * @date: 2022年03月25日 11:42 + */ +public class ThreadLocalDataHelper { + /** + * 线程的本地变量 + */ + private static final ThreadLocal REQUEST_DATA = new ThreadLocal<>(); + + /** + * 存储本地参数 + */ + private static final ConcurrentHashMap DATA_MAP = new ConcurrentHashMap<>(); + + /** + * 设置请求参数 + * + * @param key 参数key + * @param value 参数值 + */ + public static void put(String key, Object value) { + if(ObjectUtil.isNotEmpty(value)) { + DATA_MAP.put(key, value); + REQUEST_DATA.set(DATA_MAP); + } + } + + /** + * 获取请求参数值 + * + * @param key 请求参数 + * @return + */ + public static T get(String key) { + ConcurrentHashMap dataMap = REQUEST_DATA.get(); + if (CollectionUtils.isNotEmpty(dataMap)) { + return (T) dataMap.get(key); + } + return null; + } + + /** + * 获取请求参数 + * + * @return 请求参数 MAP 对象 + */ + public static void clear() { + DATA_MAP.clear(); + REQUEST_DATA.remove(); + } + +} + diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/interceptor/DynamicDatasourceInterceptor.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/interceptor/DynamicDatasourceInterceptor.java new file mode 100644 index 0000000..3461cbd --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/interceptor/DynamicDatasourceInterceptor.java @@ -0,0 +1,55 @@ +package org.jeecg.config.mybatis.interceptor; + +import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 动态数据源切换拦截器 + * + * 测试:拦截参数,自动切换数据源 + * 未来规划:后面通过此机制,实现多租户切换数据源功能 + * @author zyf + */ +@Slf4j +public class DynamicDatasourceInterceptor implements HandlerInterceptor { + + /** + * 在请求处理之前进行调用(Controller方法调用之前) + */ + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + String requestURI = request.getRequestURI(); + log.info("经过多数据源Interceptor,当前路径是{}", requestURI); + //获取动态数据源名称 + String dsName = request.getParameter("dsName"); + String dsKey = "master"; + if (StringUtils.isNotEmpty(dsName)) { + dsKey = dsName; + } + DynamicDataSourceContextHolder.push(dsKey); + return true; + } + + /** + * 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后) + */ + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { + + } + + /** + * 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作) + */ + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { + DynamicDataSourceContextHolder.clear(); + } + +} \ No newline at end of file diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/JwtToken.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/JwtToken.java new file mode 100644 index 0000000..0507c54 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/JwtToken.java @@ -0,0 +1,28 @@ +package org.jeecg.config.shiro; + +import org.apache.shiro.authc.AuthenticationToken; + +/** + * @Author Scott + * @create 2018-07-12 15:19 + * @desc + **/ +public class JwtToken implements AuthenticationToken { + + private static final long serialVersionUID = 1L; + private String token; + + public JwtToken(String token) { + this.token = token; + } + + @Override + public Object getPrincipal() { + return token; + } + + @Override + public Object getCredentials() { + return token; + } +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java new file mode 100644 index 0000000..f3ea34f --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java @@ -0,0 +1,282 @@ +package org.jeecg.config.shiro; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.apache.shiro.mgt.DefaultSessionStorageEvaluator; +import org.apache.shiro.mgt.DefaultSubjectDAO; +import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.spring.LifecycleBeanPostProcessor; +import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; +import org.apache.shiro.web.mgt.DefaultWebSecurityManager; +import org.crazycake.shiro.IRedisManager; +import org.crazycake.shiro.RedisCacheManager; +import org.crazycake.shiro.RedisClusterManager; +import org.crazycake.shiro.RedisManager; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.config.JeecgBaseConfig; +import org.jeecg.config.shiro.filters.CustomShiroFilterFactoryBean; +import org.jeecg.config.shiro.filters.JwtFilter; +import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; +import org.springframework.core.env.Environment; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.util.StringUtils; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.JedisCluster; + +import javax.annotation.Resource; +import javax.servlet.Filter; +import java.util.*; + +/** + * @author: Scott + * @date: 2018/2/7 + * @description: shiro 配置类 + */ + +@Slf4j +@Configuration +public class ShiroConfig { + + @Resource + private LettuceConnectionFactory lettuceConnectionFactory; + @Autowired + private Environment env; + @Resource + private JeecgBaseConfig jeecgBaseConfig; + + /** + * Filter Chain定义说明 + * + * 1、一个URL可以配置多个Filter,使用逗号分隔 + * 2、当设置多个过滤器时,全部验证通过,才视为通过 + * 3、部分过滤器可指定参数,如perms,roles + */ + @Bean("shiroFilterFactoryBean") + public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { + CustomShiroFilterFactoryBean shiroFilterFactoryBean = new CustomShiroFilterFactoryBean(); + shiroFilterFactoryBean.setSecurityManager(securityManager); + // 拦截器 + Map filterChainDefinitionMap = new LinkedHashMap(); + + //支持yml方式,配置拦截排除 + if(jeecgBaseConfig.getShiro()!=null){ + String shiroExcludeUrls = jeecgBaseConfig.getShiro().getExcludeUrls(); + if(oConvertUtils.isNotEmpty(shiroExcludeUrls)){ + String[] permissionUrl = shiroExcludeUrls.split(","); + for(String url : permissionUrl){ + filterChainDefinitionMap.put(url,"anon"); + } + } + } + // 配置不会被拦截的链接 顺序判断 + + filterChainDefinitionMap.put("/sys/randomImage/**", "anon"); //登录验证码接口排除 + filterChainDefinitionMap.put("/sys/checkCaptcha", "anon"); //登录验证码接口排除 + filterChainDefinitionMap.put("/sys/login", "anon"); //四合一膳食监控获取token排除 + filterChainDefinitionMap.put("/sys/mLogin", "anon"); //登录接口排除 + filterChainDefinitionMap.put("/sys/ckAppLogin", "anon"); //仓库登陆接口排除 + filterChainDefinitionMap.put("/sys/thirdLogin/**", "anon"); //第三方登录 + filterChainDefinitionMap.put("/sys/getEncryptedString", "anon"); //获取加密串 + filterChainDefinitionMap.put("/zhstapi/pay/getEquipmentToken", "anon");//直连外网环境设备获取token(接口逻辑里已经验证设备编码有效) + + + filterChainDefinitionMap.put("/sys/sms", "anon");//短信验证码 + filterChainDefinitionMap.put("/sys/phoneLogin", "anon");//手机登录 + filterChainDefinitionMap.put("/sys/user/checkOnlyUser", "anon");//校验用户是否存在 + filterChainDefinitionMap.put("/sys/user/register", "anon");//用户注册 + filterChainDefinitionMap.put("/sys/user/phoneVerification", "anon");//用户忘记密码验证手机号 +// filterChainDefinitionMap.put("/sys/user/passwordChange", "anon");//硬编码密码-中危 + filterChainDefinitionMap.put("/auth/2step-code", "anon");//登录验证码 + filterChainDefinitionMap.put("/sys/common/static/**", "anon");//图片预览 &下载文件不限制token + filterChainDefinitionMap.put("/sys/common/pdf/**", "anon");//pdf预览 + filterChainDefinitionMap.put("/generic/**", "anon");//pdf预览需要文件 + + filterChainDefinitionMap.put("/sys/getEquipmentToken", "anon"); //app调用token接口 + filterChainDefinitionMap.put("/mp/performance/sportRecord/**", "anon"); + filterChainDefinitionMap.put("/sys/logout", "anon"); //登出接口排除 + filterChainDefinitionMap.put("/sys/user/passwordChange", "anon");//用户更改密码 + filterChainDefinitionMap.put("/businesslog/**", "anon");//设备日志 + filterChainDefinitionMap.put("/medical/oneMachine/**", "anon"); + filterChainDefinitionMap.put("/jydWatch/pushData/**", "anon"); + + filterChainDefinitionMap.put("/sys/getLoginQrcode/**", "anon"); //登录二维码 + filterChainDefinitionMap.put("/sys/getQrcodeToken/**", "anon"); //监听扫码 + filterChainDefinitionMap.put("/sys/checkAuth", "anon"); //授权接口排除 + filterChainDefinitionMap.put("/", "anon"); + filterChainDefinitionMap.put("/doc.html", "anon"); + filterChainDefinitionMap.put("/**/*.js", "anon"); + filterChainDefinitionMap.put("/**/*.css", "anon"); + filterChainDefinitionMap.put("/**/*.html", "anon"); + filterChainDefinitionMap.put("/**/*.svg", "anon"); + filterChainDefinitionMap.put("/**/*.pdf", "anon"); + filterChainDefinitionMap.put("/**/*.jpg", "anon"); + filterChainDefinitionMap.put("/**/*.png", "anon"); + filterChainDefinitionMap.put("/**/*.ico", "anon"); + + // update-begin--Author:sunjianlei Date:20190813 for:排除字体格式的后缀 + filterChainDefinitionMap.put("/**/*.ttf", "anon"); + filterChainDefinitionMap.put("/**/*.woff", "anon"); + filterChainDefinitionMap.put("/**/*.woff2", "anon"); + // update-begin--Author:sunjianlei Date:20190813 for:排除字体格式的后缀 + + filterChainDefinitionMap.put("/druid/**", "anon"); + filterChainDefinitionMap.put("/swagger-ui.html", "anon"); + filterChainDefinitionMap.put("/swagger**/**", "anon"); + filterChainDefinitionMap.put("/webjars/**", "anon"); + filterChainDefinitionMap.put("/v2/**", "anon"); + + filterChainDefinitionMap.put("/sys/annountCement/show/**", "anon"); + + //积木报表排除 + filterChainDefinitionMap.put("/jmreport/**", "anon"); + filterChainDefinitionMap.put("/**/*.js.map", "anon"); + filterChainDefinitionMap.put("/**/*.css.map", "anon"); + + //大屏模板例子 + filterChainDefinitionMap.put("/test/bigScreen/**", "anon"); + filterChainDefinitionMap.put("/bigscreen/template1/**", "anon"); + + //websocket排除 + filterChainDefinitionMap.put("/websocket/**", "anon");//系统通知和公告 + filterChainDefinitionMap.put("/newsWebsocket/**", "anon");//CMS模块 + filterChainDefinitionMap.put("/vxeSocket/**", "anon");//JVxeTable无痕刷新示例 + + + //性能监控 TODO 存在安全漏洞泄露TOEKN(durid连接池也有) + filterChainDefinitionMap.put("/actuator/**", "anon"); + + //测试模块排除 + filterChainDefinitionMap.put("/test/seata/**", "anon"); + + // 添加自己的过滤器并且取名为jwt + Map filterMap = new HashMap(1); + //如果cloudServer为空 则说明是单体 需要加载跨域配置【微服务跨域切换】 + Object cloudServer = env.getProperty(CommonConstant.CLOUD_SERVER_KEY); + filterMap.put("jwt", new JwtFilter(cloudServer==null)); + shiroFilterFactoryBean.setFilters(filterMap); + // + + insert into sys_log (id, log_type, log_content, method, operate_type, request_url, request_type, request_param, ip, userid, username, cost_time, create_time,create_by) + values( + #{dto.id,jdbcType=VARCHAR}, + #{dto.logType,jdbcType=INTEGER}, + #{dto.logContent,jdbcType=VARCHAR}, + #{dto.method,jdbcType=VARCHAR}, + #{dto.operateType,jdbcType=INTEGER}, + #{dto.requestUrl,jdbcType=VARCHAR}, + #{dto.requestType,jdbcType=VARCHAR}, + #{dto.requestParam,jdbcType=VARCHAR}, + #{dto.ip,jdbcType=VARCHAR}, + #{dto.userid,jdbcType=VARCHAR}, + #{dto.username,jdbcType=VARCHAR}, + #{dto.costTime,jdbcType=BIGINT}, + #{dto.createTime,jdbcType=TIMESTAMP}, + #{dto.createBy,jdbcType=VARCHAR} + ) + + + \ No newline at end of file diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/service/BaseCommonService.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/service/BaseCommonService.java new file mode 100644 index 0000000..6c1de28 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/service/BaseCommonService.java @@ -0,0 +1,35 @@ +package org.jeecg.modules.base.service; + +import org.jeecg.common.api.dto.LogDTO; +import org.jeecg.common.system.vo.LoginUser; + +/** + * common接口 + * @author: jeecg-boot + */ +public interface BaseCommonService { + + /** + * 保存日志 + * @param logDTO + */ + void addLog(LogDTO logDTO); + + /** + * 保存日志 + * @param logContent + * @param logType + * @param operateType + * @param user + */ + void addLog(String logContent, Integer logType, Integer operateType, LoginUser user); + + /** + * 保存日志 + * @param logContent + * @param logType + * @param operateType + */ + void addLog(String logContent, Integer logType, Integer operateType); + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/service/impl/BaseCommonServiceImpl.java b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/service/impl/BaseCommonServiceImpl.java new file mode 100644 index 0000000..8fe9737 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/modules/base/service/impl/BaseCommonServiceImpl.java @@ -0,0 +1,90 @@ +package org.jeecg.modules.base.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.util.IPUtils; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.api.dto.LogDTO; +import org.jeecg.modules.base.mapper.BaseCommonMapper; +import org.jeecg.modules.base.service.BaseCommonService; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.util.SpringContextUtils; +import org.jeecg.common.util.oConvertUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.*; + +/** + * @Description: common实现类 + * @author: jeecg-boot + */ +@Service +@Slf4j +public class BaseCommonServiceImpl implements BaseCommonService { + + @Resource + private BaseCommonMapper baseCommonMapper; + + @Override + public void addLog(LogDTO logDTO) { + if(oConvertUtils.isEmpty(logDTO.getId())){ + logDTO.setId(String.valueOf(IdWorker.getId())); + } + //保存日志(异常捕获处理,防止数据太大存储失败,导致业务失败)JT-238 + try { + logDTO.setCreateTime(new Date()); + baseCommonMapper.saveLog(logDTO); + } catch (Exception e) { + log.warn(" LogContent length : "+logDTO.getLogContent().length()); + log.warn(e.getMessage()); + } + } + + @Override + public void addLog(String logContent, Integer logType, Integer operatetype, LoginUser user) { + LogDTO sysLog = new LogDTO(); + sysLog.setId(String.valueOf(IdWorker.getId())); + //注解上的描述,操作日志内容 + sysLog.setLogContent(logContent); + sysLog.setLogType(logType); + sysLog.setOperateType(operatetype); + try { + //获取request + HttpServletRequest request = SpringContextUtils.getHttpServletRequest(); + //设置IP地址 + sysLog.setIp(IPUtils.getIpAddr(request)); + } catch (Exception e) { + sysLog.setIp("127.0.0.1"); + } + //获取登录用户信息 + if(user==null){ + try { + user = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + } catch (Exception e) { + //e.printStackTrace(); + } + } + if(user!=null){ + sysLog.setUserid(user.getUsername()); + sysLog.setUsername(user.getRealname()); + } + sysLog.setCreateTime(new Date()); + //保存日志(异常捕获处理,防止数据太大存储失败,导致业务失败)JT-238 + try { + baseCommonMapper.saveLog(sysLog); + } catch (Exception e) { + log.warn(" LogContent length : "+sysLog.getLogContent().length()); + log.warn(e.getMessage()); + } + } + + @Override + public void addLog(String logContent, Integer logType, Integer operateType) { + addLog(logContent, logType, operateType, null); + } + + + +} diff --git a/jeecg-boot-base-core/src/main/resources/static/pca.json b/jeecg-boot-base-core/src/main/resources/static/pca.json new file mode 100644 index 0000000..777dc3d --- /dev/null +++ b/jeecg-boot-base-core/src/main/resources/static/pca.json @@ -0,0 +1,51 @@ +{ + "86":{ + "110000":"北京市", + "120000":"天津市", + "130000":"河北省", + "140000":"山西省", + "150000":"内蒙古自治区", + "210000":"辽宁省", + "220000":"吉林省", + "230000":"黑龙江省", + "310000":"上海市", + "320000":"江苏省", + "330000":"浙江省", + "340000":"安徽省", + "350000":"福建省", + "360000":"江西省", + "370000":"山东省", + "410000":"河南省", + "420000":"湖北省", + "430000":"湖南省", + "440000":"广东省", + "450000":"广西壮族自治区", + "460000":"海南省", + "500000":"重庆市", + "510000":"四川省", + "520000":"贵州省", + "530000":"云南省", + "540000":"西藏自治区", + "610000":"陕西省", + "620000":"甘肃省", + "630000":"青海省", + "640000":"宁夏回族自治区", + "650000":"新疆维吾尔自治区", + "710000":"台湾省", + "910000":"港澳" + }, + "110000":{"110100":"市辖区"}, + "110100":{"110101":"东城区","110102":"西城区","110105":"朝阳区","110106":"丰台区","110107":"石景山区","110108":"海淀区","110109":"门头沟区","110111":"房山区","110112":"通州区","110113":"顺义区","110114":"昌平区","110115":"大兴区","110116":"怀柔区","110117":"平谷区","110118":"密云区","110119":"延庆区"}, + "120000":{"120100":"市辖区"}, + "120100":{"120101":"和平区","120102":"河东区","120103":"河西区","120104":"南开区","120105":"河北区","120106":"红桥区","120110":"东丽区","120111":"西青区","120112":"津南区","120113":"北辰区","120114":"武清区","120115":"宝坻区","120116":"滨海新区","120117":"宁河区","120118":"静海区","120119":"蓟州区"},"130000":{"130100":"石家庄市","130200":"唐山市","130300":"秦皇岛市","130400":"邯郸市","130500":"邢台市","130600":"保定市","130700":"张家口市","130800":"承德市","130900":"沧州市","131000":"廊坊市","131100":"衡水市","139001":"定州市","139002":"辛集市"},"130100":{"130102":"长安区","130104":"桥西区","130105":"新华区","130107":"井陉矿区","130108":"裕华区","130109":"藁城区","130110":"鹿泉区","130111":"栾城区","130121":"井陉县","130123":"正定县","130125":"行唐县","130126":"灵寿县","130127":"高邑县","130128":"深泽县","130129":"赞皇县","130130":"无极县","130131":"平山县","130132":"元氏县","130133":"赵县","130183":"晋州市","130184":"新乐市"},"130200":{"130202":"路南区","130203":"路北区","130204":"古冶区","130205":"开平区","130207":"丰南区","130208":"丰润区","130209":"曹妃甸区","130223":"滦县","130224":"滦南县","130225":"乐亭县","130227":"迁西县","130229":"玉田县","130281":"遵化市","130283":"迁安市"},"130300":{"130302":"海港区","130303":"山海关区","130304":"北戴河区","130306":"抚宁区","130321":"青龙满族自治县","130322":"昌黎县","130324":"卢龙县"},"130400":{"130402":"邯山区","130403":"丛台区","130404":"复兴区","130406":"峰峰矿区","130421":"邯郸县","130423":"临漳县","130424":"成安县","130425":"大名县","130426":"涉县","130427":"磁县","130428":"肥乡县","130429":"永年县","130430":"邱县","130431":"鸡泽县","130432":"广平县","130433":"馆陶县","130434":"魏县","130435":"曲周县","130481":"武安市"},"130500":{"130502":"桥东区","130503":"桥西区","130521":"邢台县","130522":"临城县","130523":"内丘县","130524":"柏乡县","130525":"隆尧县","130526":"任县","130527":"南和县","130528":"宁晋县","130529":"巨鹿县","130530":"新河县","130531":"广宗县","130532":"平乡县","130533":"威县","130534":"清河县","130535":"临西县","130581":"南宫市","130582":"沙河市"},"130600":{"130602":"竞秀区","130606":"莲池区","130607":"满城区","130608":"清苑区","130609":"徐水区","130623":"涞水县","130624":"阜平县","130626":"定兴县","130627":"唐县","130628":"高阳县","130629":"容城县","130630":"涞源县","130631":"望都县","130632":"安新县","130633":"易县","130634":"曲阳县","130635":"蠡县","130636":"顺平县","130637":"博野县","130638":"雄县","130681":"涿州市","130683":"安国市","130684":"高碑店市"},"130700":{"130702":"桥东区","130703":"桥西区","130705":"宣化区","130706":"下花园区","130708":"万全区","130709":"崇礼区","130722":"张北县","130723":"康保县","130724":"沽源县","130725":"尚义县","130726":"蔚县","130727":"阳原县","130728":"怀安县","130730":"怀来县","130731":"涿鹿县","130732":"赤城县"},"130800":{"130802":"双桥区","130803":"双滦区","130804":"鹰手营子矿区","130821":"承德县","130822":"兴隆县","130823":"平泉县","130824":"滦平县","130825":"隆化县","130826":"丰宁满族自治县","130827":"宽城满族自治县","130828":"围场满族蒙古族自治县"},"130900":{"130902":"新华区","130903":"运河区","130921":"沧县","130922":"青县","130923":"东光县","130924":"海兴县","130925":"盐山县","130926":"肃宁县","130927":"南皮县","130928":"吴桥县","130929":"献县","130930":"孟村回族自治县","130981":"泊头市","130982":"任丘市","130983":"黄骅市","130984":"河间市"},"131000":{"131002":"安次区","131003":"广阳区","131022":"固安县","131023":"永清县","131024":"香河县","131025":"大城县","131026":"文安县","131028":"大厂回族自治县","131081":"霸州市","131082":"三河市"},"131100":{"131102":"桃城区","131103":"冀州区","131121":"枣强县","131122":"武邑县","131123":"武强县","131124":"饶阳县","131125":"安平县","131126":"故城县","131127":"景县","131128":"阜城县","131182":"深州市"},"139001":{"1390011":"留早镇","13900111":"邢邑镇","139001001":"南城区街道","139001002":"北城区街道","139001003":"西城区街道","139001004":"长安路街道","139001101":"清风店镇","139001102":"庞村镇","139001103":"砖路镇","139001104":"明月店镇","139001105":"叮咛店镇","139001106":"东亭镇","139001107":"大辛庄镇","139001108":"东旺镇","139001109":"高蓬镇","139001111":"李亲顾镇","139001112":"子位镇","139001113":"开元镇","139001115":"周村镇","139001116":"息冢镇","139001203":"东留春乡","139001204":"号头庄回族乡","139001205":"杨家庄乡","139001206":"大鹿庄乡","139001208":"西城乡"},"139002":{"1390021":"辛集镇","1390022":"天宫营乡","1390025":"辛集经济开发区","139002101":"旧城镇","139002102":"张古庄镇","139002103":"位伯镇","139002104":"新垒头镇","139002105":"新城镇","139002106":"南智邱镇","139002107":"王口镇","139002201":"前营乡","139002202":"马庄乡","139002203":"和睦井乡","139002204":"田家庄乡","139002205":"中里厢乡","139002206":"小辛庄乡"},"140000":{"140100":"太原市","140200":"大同市","140300":"阳泉市","140400":"长治市","140500":"晋城市","140600":"朔州市","140700":"晋中市","140800":"运城市","140900":"忻州市","141000":"临汾市","141100":"吕梁市"},"140100":{"140105":"小店区","140106":"迎泽区","140107":"杏花岭区","140108":"尖草坪区","140109":"万柏林区","140110":"晋源区","140121":"清徐县","140122":"阳曲县","140123":"娄烦县","140181":"古交市"},"140200":{"140202":"城区","140203":"矿区","140211":"南郊区","140212":"新荣区","140221":"阳高县","140222":"天镇县","140223":"广灵县","140224":"灵丘县","140225":"浑源县","140226":"左云县","140227":"大同县"},"140300":{"140302":"城区","140303":"矿区","140311":"郊区","140321":"平定县","140322":"盂县"},"140400":{"140402":"城区","140411":"郊区","140421":"长治县","140423":"襄垣县","140424":"屯留县","140425":"平顺县","140426":"黎城县","140427":"壶关县","140428":"长子县","140429":"武乡县","140430":"沁县","140431":"沁源县","140481":"潞城市"},"140500":{"140502":"城区","140521":"沁水县","140522":"阳城县","140524":"陵川县","140525":"泽州县","140581":"高平市"},"140600":{"140602":"朔城区","140603":"平鲁区","140621":"山阴县","140622":"应县","140623":"右玉县","140624":"怀仁县"},"140700":{"140702":"榆次区","140721":"榆社县","140722":"左权县","140723":"和顺县","140724":"昔阳县","140725":"寿阳县","140726":"太谷县","140727":"祁县","140728":"平遥县","140729":"灵石县","140781":"介休市"},"140800":{"140802":"盐湖区","140821":"临猗县","140822":"万荣县","140823":"闻喜县","140824":"稷山县","140825":"新绛县","140826":"绛县","140827":"垣曲县","140828":"夏县","140829":"平陆县","140830":"芮城县","140881":"永济市","140882":"河津市"},"140900":{"140902":"忻府区","140921":"定襄县","140922":"五台县","140923":"代县","140924":"繁峙县","140925":"宁武县","140926":"静乐县","140927":"神池县","140928":"五寨县","140929":"岢岚县","140930":"河曲县","140931":"保德县","140932":"偏关县","140981":"原平市"}, + "141000":{"141002":"尧都区","141021":"曲沃县","141022":"翼城县","141023":"襄汾县","141024":"洪洞县","141025":"古县","141026":"安泽县","141027":"浮山县","141028":"吉县","141029":"乡宁县","141030":"大宁县","141031":"隰县","141032":"永和县","141033":"蒲县","141034":"汾西县","141081":"侯马市","141082":"霍州市"},"141100":{"141102":"离石区","141121":"文水县","141122":"交城县","141123":"兴县","141124":"临县","141125":"柳林县","141126":"石楼县","141127":"岚县","141128":"方山县","141129":"中阳县","141130":"交口县","141181":"孝义市","141182":"汾阳市"},"150000":{"150100":"呼和浩特市","150200":"包头市","150300":"乌海市","150400":"赤峰市","150500":"通辽市","150600":"鄂尔多斯市","150700":"呼伦贝尔市","150800":"巴彦淖尔市","150900":"乌兰察布市","152200":"兴安盟","152500":"锡林郭勒盟","152900":"阿拉善盟"},"150100":{"150102":"新城区","150103":"回民区","150104":"玉泉区","150105":"赛罕区","150121":"土默特左旗","150122":"托克托县","150123":"和林格尔县","150124":"清水河县","150125":"武川县"},"150200":{"150202":"东河区","150203":"昆都仑区","150204":"青山区","150205":"石拐区","150206":"白云鄂博矿区","150207":"九原区","150221":"土默特右旗","150222":"固阳县","150223":"达尔罕茂明安联合旗"},"150300":{"150302":"海勃湾区","150303":"海南区","150304":"乌达区"},"150400":{"150402":"红山区","150403":"元宝山区","150404":"松山区","150421":"阿鲁科尔沁旗","150422":"巴林左旗","150423":"巴林右旗","150424":"林西县","150425":"克什克腾旗","150426":"翁牛特旗","150428":"喀喇沁旗","150429":"宁城县","150430":"敖汉旗"},"150500":{"150502":"科尔沁区","150521":"科尔沁左翼中旗","150522":"科尔沁左翼后旗","150523":"开鲁县","150524":"库伦旗","150525":"奈曼旗","150526":"扎鲁特旗","150581":"霍林郭勒市"},"150600":{"150602":"东胜区","150603":"康巴什区","150621":"达拉特旗","150622":"准格尔旗","150623":"鄂托克前旗","150624":"鄂托克旗","150625":"杭锦旗","150626":"乌审旗","150627":"伊金霍洛旗"},"150700":{"150702":"海拉尔区","150703":"扎赉诺尔区","150721":"阿荣旗","150722":"莫力达瓦达斡尔族自治旗","150723":"鄂伦春自治旗","150724":"鄂温克族自治旗","150725":"陈巴尔虎旗","150726":"新巴尔虎左旗","150727":"新巴尔虎右旗","150781":"满洲里市","150782":"牙克石市","150783":"扎兰屯市","150784":"额尔古纳市","150785":"根河市"},"150800":{"150802":"临河区","150821":"五原县","150822":"磴口县","150823":"乌拉特前旗","150824":"乌拉特中旗","150825":"乌拉特后旗","150826":"杭锦后旗"},"150900":{"150902":"集宁区","150921":"卓资县","150922":"化德县","150923":"商都县","150924":"兴和县","150925":"凉城县","150926":"察哈尔右翼前旗","150927":"察哈尔右翼中旗","150928":"察哈尔右翼后旗","150929":"四子王旗","150981":"丰镇市"},"152200":{"152201":"乌兰浩特市","152202":"阿尔山市","152221":"科尔沁右翼前旗","152222":"科尔沁右翼中旗","152223":"扎赉特旗","152224":"突泉县"},"152500":{"152501":"二连浩特市","152502":"锡林浩特市","152522":"阿巴嘎旗","152523":"苏尼特左旗","152524":"苏尼特右旗","152525":"东乌珠穆沁旗","152526":"西乌珠穆沁旗","152527":"太仆寺旗","152528":"镶黄旗","152529":"正镶白旗","152530":"正蓝旗","152531":"多伦县"},"152900":{"152921":"阿拉善左旗","152922":"阿拉善右旗","152923":"额济纳旗"},"210000":{"210100":"沈阳市","210200":"大连市","210300":"鞍山市","210400":"抚顺市","210500":"本溪市","210600":"丹东市","210700":"锦州市","210800":"营口市","210900":"阜新市","211000":"辽阳市","211100":"盘锦市","211200":"铁岭市","211300":"朝阳市","211400":"葫芦岛市"},"210100":{"210102":"和平区","210103":"沈河区","210104":"大东区","210105":"皇姑区","210106":"铁西区","210111":"苏家屯区","210112":"浑南区","210113":"沈北新区","210114":"于洪区","210115":"辽中区","210123":"康平县","210124":"法库县","210181":"新民市"},"210200":{"210202":"中山区","210203":"西岗区","210204":"沙河口区","210211":"甘井子区","210212":"旅顺口区","210213":"金州区","210214":"普兰店区","210224":"长海县","210281":"瓦房店市","210283":"庄河市"},"210300":{"210302":"铁东区","210303":"铁西区","210304":"立山区","210311":"千山区","210321":"台安县","210323":"岫岩满族自治县","210381":"海城市"},"210400":{"210402":"新抚区","210403":"东洲区","210404":"望花区","210411":"顺城区","210421":"抚顺县","210422":"新宾满族自治县","210423":"清原满族自治县"},"210500":{"210502":"平山区","210503":"溪湖区","210504":"明山区","210505":"南芬区","210521":"本溪满族自治县","210522":"桓仁满族自治县"},"210600":{"210602":"元宝区","210603":"振兴区","210604":"振安区","210624":"宽甸满族自治县","210681":"东港市","210682":"凤城市"},"210700":{"210702":"古塔区","210703":"凌河区","210711":"太和区","210726":"黑山县","210727":"义县","210781":"凌海市","210782":"北镇市"},"210800":{"210802":"站前区","210803":"西市区","210804":"鲅鱼圈区","210811":"老边区","210881":"盖州市","210882":"大石桥市"},"210900":{"210902":"海州区","210903":"新邱区","210904":"太平区","210905":"清河门区","210911":"细河区","210921":"阜新蒙古族自治县","210922":"彰武县"},"211000":{"211002":"白塔区","211003":"文圣区","211004":"宏伟区","211005":"弓长岭区","211011":"太子河区","211021":"辽阳县","211081":"灯塔市"},"211100":{"211102":"双台子区","211103":"兴隆台区","211104":"大洼区","211122":"盘山县"},"211200":{"211202":"银州区","211204":"清河区","211221":"铁岭县","211223":"西丰县","211224":"昌图县","211281":"调兵山市","211282":"开原市"},"211300":{"211302":"双塔区","211303":"龙城区","211321":"朝阳县","211322":"建平县","211324":"喀喇沁左翼蒙古族自治县","211381":"北票市","211382":"凌源市"},"211400":{"211402":"连山区","211403":"龙港区","211404":"南票区","211421":"绥中县","211422":"建昌县","211481":"兴城市"},"220000":{"220100":"长春市","220200":"吉林市","220300":"四平市","220400":"辽源市","220500":"通化市","220600":"白山市","220700":"松原市","220800":"白城市","222400":"延边朝鲜族自治州"},"220100":{"220102":"南关区","220103":"宽城区","220104":"朝阳区","220105":"二道区","220106":"绿园区","220112":"双阳区","220113":"九台区","220122":"农安县","220182":"榆树市","220183":"德惠市"},"220200":{"220202":"昌邑区","220203":"龙潭区","220204":"船营区","220211":"丰满区","220221":"永吉县","220281":"蛟河市","220282":"桦甸市","220283":"舒兰市","220284":"磐石市"},"220300":{"220302":"铁西区","220303":"铁东区","220322":"梨树县","220323":"伊通满族自治县","220381":"公主岭市","220382":"双辽市"},"220400":{"220402":"龙山区","220403":"西安区","220421":"东丰县","220422":"东辽县"},"220500":{"220502":"东昌区","220503":"二道江区","220521":"通化县","220523":"辉南县","220524":"柳河县","220581":"梅河口市","220582":"集安市"},"220600":{"220602":"浑江区","220605":"江源区","220621":"抚松县","220622":"靖宇县","220623":"长白朝鲜族自治县","220681":"临江市"},"220700":{"220702":"宁江区","220721":"前郭尔罗斯蒙古族自治县","220722":"长岭县","220723":"乾安县","220781":"扶余市"},"220800":{"220802":"洮北区","220821":"镇赉县","220822":"通榆县","220881":"洮南市","220882":"大安市"},"222400":{"222401":"延吉市","222402":"图们市","222403":"敦化市","222404":"珲春市","222405":"龙井市","222406":"和龙市","222424":"汪清县","222426":"安图县"},"230000":{"230100":"哈尔滨市","230200":"齐齐哈尔市","230300":"鸡西市","230400":"鹤岗市","230500":"双鸭山市","230600":"大庆市","230700":"伊春市","230800":"佳木斯市","230900":"七台河市","231000":"牡丹江市","231100":"黑河市","231200":"绥化市","232700":"大兴安岭地区"},"230100":{"230102":"道里区","230103":"南岗区","230104":"道外区","230108":"平房区","230109":"松北区","230110":"香坊区","230111":"呼兰区","230112":"阿城区","230113":"双城区","230123":"依兰县","230124":"方正县","230125":"宾县","230126":"巴彦县","230127":"木兰县","230128":"通河县","230129":"延寿县","230183":"尚志市","230184":"五常市"},"230200":{"230202":"龙沙区","230203":"建华区","230204":"铁锋区","230205":"昂昂溪区","230206":"富拉尔基区","230207":"碾子山区","230208":"梅里斯达斡尔族区","230221":"龙江县","230223":"依安县","230224":"泰来县","230225":"甘南县","230227":"富裕县","230229":"克山县","230230":"克东县","230231":"拜泉县","230281":"讷河市"},"230300":{"230302":"鸡冠区","230303":"恒山区","230304":"滴道区","230305":"梨树区","230306":"城子河区","230307":"麻山区","230321":"鸡东县","230381":"虎林市","230382":"密山市"},"230400":{"230402":"向阳区","230403":"工农区","230404":"南山区","230405":"兴安区","230406":"东山区","230407":"兴山区","230421":"萝北县","230422":"绥滨县"},"230500":{"230502":"尖山区","230503":"岭东区","230505":"四方台区","230506":"宝山区","230521":"集贤县","230522":"友谊县","230523":"宝清县","230524":"饶河县"},"230600":{"230602":"萨尔图区","230603":"龙凤区","230604":"让胡路区","230605":"红岗区","230606":"大同区","230621":"肇州县","230622":"肇源县","230623":"林甸县","230624":"杜尔伯特蒙古族自治县"},"230700":{"230702":"伊春区","230703":"南岔区","230704":"友好区","230705":"西林区","230706":"翠峦区","230707":"新青区","230708":"美溪区","230709":"金山屯区","230710":"五营区","230711":"乌马河区","230712":"汤旺河区","230713":"带岭区","230714":"乌伊岭区","230715":"红星区","230716":"上甘岭区","230722":"嘉荫县","230781":"铁力市"},"230800":{"230803":"向阳区","230804":"前进区","230805":"东风区","230811":"郊区","230822":"桦南县","230826":"桦川县","230828":"汤原县","230881":"同江市","230882":"富锦市","230883":"抚远市"},"230900":{"230902":"新兴区","230903":"桃山区","230904":"茄子河区","230921":"勃利县"},"231000":{"231002":"东安区","231003":"阳明区","231004":"爱民区","231005":"西安区","231025":"林口县","231081":"绥芬河市","231083":"海林市","231084":"宁安市","231085":"穆棱市","231086":"东宁市"},"231100":{"231102":"爱辉区","231121":"嫩江县","231123":"逊克县","231124":"孙吴县","231181":"北安市","231182":"五大连池市"},"231200":{"231202":"北林区","231221":"望奎县","231222":"兰西县","231223":"青冈县","231224":"庆安县","231225":"明水县","231226":"绥棱县","231281":"安达市","231282":"肇东市","231283":"海伦市"},"232700":{"232721":"呼玛县","232722":"塔河县","232723":"漠河县"},"310000":{"310100":"市辖区"}, + "310100":{"310101":"黄浦区","310104":"徐汇区","310105":"长宁区","310106":"静安区","310107":"普陀区","310109":"虹口区","310110":"杨浦区","310112":"闵行区","310113":"宝山区","310114":"嘉定区","310115":"浦东新区","310116":"金山区","310117":"松江区","310118":"青浦区","310120":"奉贤区","310151":"崇明区"},"320000":{"320100":"南京市","320200":"无锡市","320300":"徐州市","320400":"常州市","320500":"苏州市","320600":"南通市","320700":"连云港市","320800":"淮安市","320900":"盐城市","321000":"扬州市","321100":"镇江市","321200":"泰州市","321300":"宿迁市"},"320100":{"320102":"玄武区","320104":"秦淮区","320105":"建邺区","320106":"鼓楼区","320111":"浦口区","320113":"栖霞区","320114":"雨花台区","320115":"江宁区","320116":"六合区","320117":"溧水区","320118":"高淳区"}, + "320200":{"320205":"锡山区","320206":"惠山区","320211":"滨湖区","320213":"梁溪区","320214":"新吴区","320281":"江阴市","320282":"宜兴市"},"320300":{"320302":"鼓楼区","320303":"云龙区","320305":"贾汪区","320311":"泉山区","320312":"铜山区","320321":"丰县","320322":"沛县","320324":"睢宁县","320381":"新沂市","320382":"邳州市"}, + "320400":{"320402":"天宁区","320404":"钟楼区","320411":"新北区","320412":"武进区","320413":"金坛区","320481":"溧阳市"},"320500":{"320505":"虎丘区","320506":"吴中区","320507":"相城区","320508":"姑苏区","320509":"吴江区","320581":"常熟市","320582":"张家港市","320583":"昆山市","320585":"太仓市"},"320600":{"320602":"崇川区","320611":"港闸区","320612":"通州区","320621":"海安县","320623":"如东县","320681":"启东市","320682":"如皋市","320684":"海门市"},"320700":{"320703":"连云区","320706":"海州区","320707":"赣榆区","320722":"东海县","320723":"灌云县","320724":"灌南县"},"320800":{"320803":"淮安区","320804":"淮阴区","320812":"清江浦区","320813":"洪泽区","320826":"涟水县","320830":"盱眙县","320831":"金湖县"},"320900":{"320902":"亭湖区","320903":"盐都区","320904":"大丰区","320921":"响水县","320922":"滨海县","320923":"阜宁县","320924":"射阳县","320925":"建湖县","320981":"东台市"},"321000":{"321002":"广陵区","321003":"邗江区","321012":"江都区","321023":"宝应县","321081":"仪征市","321084":"高邮市"},"321100":{"321102":"京口区","321111":"润州区","321112":"丹徒区","321181":"丹阳市","321182":"扬中市","321183":"句容市"},"321200":{"321202":"海陵区","321203":"高港区","321204":"姜堰区","321281":"兴化市","321282":"靖江市","321283":"泰兴市"},"321300":{"321302":"宿城区","321311":"宿豫区","321322":"沭阳县","321323":"泗阳县","321324":"泗洪县"},"330000":{"330100":"杭州市","330200":"宁波市","330300":"温州市","330400":"嘉兴市","330500":"湖州市","330600":"绍兴市","330700":"金华市","330800":"衢州市","330900":"舟山市","331000":"台州市","331100":"丽水市"},"330100":{"330102":"上城区","330103":"下城区","330104":"江干区","330105":"拱墅区","330106":"西湖区","330108":"滨江区","330109":"萧山区","330110":"余杭区","330111":"富阳区","330122":"桐庐县","330127":"淳安县","330182":"建德市","330185":"临安市"},"330200":{"330203":"海曙区","330204":"江东区","330205":"江北区","330206":"北仑区","330211":"镇海区","330212":"鄞州区","330225":"象山县","330226":"宁海县","330281":"余姚市","330282":"慈溪市","330283":"奉化市"},"330300":{"330302":"鹿城区","330303":"龙湾区","330304":"瓯海区","330305":"洞头区","330324":"永嘉县","330326":"平阳县","330327":"苍南县","330328":"文成县","330329":"泰顺县","330381":"瑞安市","330382":"乐清市"},"330400":{"330402":"南湖区","330411":"秀洲区","330421":"嘉善县","330424":"海盐县","330481":"海宁市","330482":"平湖市","330483":"桐乡市"},"330500":{"330502":"吴兴区","330503":"南浔区","330521":"德清县","330522":"长兴县","330523":"安吉县"},"330600":{"330602":"越城区","330603":"柯桥区","330604":"上虞区","330624":"新昌县","330681":"诸暨市","330683":"嵊州市"},"330700":{"330702":"婺城区","330703":"金东区","330723":"武义县","330726":"浦江县","330727":"磐安县","330781":"兰溪市","330782":"义乌市","330783":"东阳市","330784":"永康市"},"330800":{"330802":"柯城区","330803":"衢江区","330822":"常山县","330824":"开化县","330825":"龙游县","330881":"江山市"},"330900":{"330902":"定海区","330903":"普陀区","330921":"岱山县","330922":"嵊泗县"},"331000":{"331002":"椒江区","331003":"黄岩区","331004":"路桥区","331021":"玉环县","331022":"三门县","331023":"天台县","331024":"仙居县","331081":"温岭市","331082":"临海市"},"331100":{"331102":"莲都区","331121":"青田县","331122":"缙云县","331123":"遂昌县","331124":"松阳县","331125":"云和县","331126":"庆元县","331127":"景宁畲族自治县","331181":"龙泉市"},"340000":{"340100":"合肥市","340200":"芜湖市","340300":"蚌埠市","340400":"淮南市","340500":"马鞍山市","340600":"淮北市","340700":"铜陵市","340800":"安庆市","341000":"黄山市","341100":"滁州市","341200":"阜阳市","341300":"宿州市","341500":"六安市","341600":"亳州市","341700":"池州市","341800":"宣城市"},"340100":{"340102":"瑶海区","340103":"庐阳区","340104":"蜀山区","340111":"包河区","340121":"长丰县","340122":"肥东县","340123":"肥西县","340124":"庐江县","340181":"巢湖市"},"340200":{"340202":"镜湖区","340203":"弋江区","340207":"鸠江区","340208":"三山区","340221":"芜湖县","340222":"繁昌县","340223":"南陵县","340225":"无为县"},"340300":{"340302":"龙子湖区","340303":"蚌山区","340304":"禹会区","340311":"淮上区","340321":"怀远县","340322":"五河县","340323":"固镇县"},"340400":{"340402":"大通区","340403":"田家庵区","340404":"谢家集区","340405":"八公山区","340406":"潘集区","340421":"凤台县","340422":"寿县"},"340500":{"340503":"花山区","340504":"雨山区","340506":"博望区","340521":"当涂县","340522":"含山县","340523":"和县"},"340600":{"340602":"杜集区","340603":"相山区","340604":"烈山区","340621":"濉溪县"},"340700":{"340705":"铜官区","340706":"义安区","340711":"郊区","340722":"枞阳县"},"340800":{"340802":"迎江区","340803":"大观区","340811":"宜秀区","340822":"怀宁县","340824":"潜山县","340825":"太湖县","340826":"宿松县","340827":"望江县","340828":"岳西县","340881":"桐城市"},"341000":{"341002":"屯溪区","341003":"黄山区","341004":"徽州区","341021":"歙县","341022":"休宁县","341023":"黟县","341024":"祁门县"},"341100":{"341102":"琅琊区","341103":"南谯区","341122":"来安县","341124":"全椒县","341125":"定远县","341126":"凤阳县","341181":"天长市","341182":"明光市"},"341200":{"341202":"颍州区","341203":"颍东区","341204":"颍泉区","341221":"临泉县","341222":"太和县","341225":"阜南县","341226":"颍上县","341282":"界首市"},"341300":{"341302":"埇桥区","341321":"砀山县","341322":"萧县","341323":"灵璧县","341324":"泗县"},"341500":{"341502":"金安区","341503":"裕安区","341504":"叶集区","341522":"霍邱县","341523":"舒城县","341524":"金寨县","341525":"霍山县"},"341600":{"341602":"谯城区","341621":"涡阳县","341622":"蒙城县","341623":"利辛县"},"341700":{"341702":"贵池区","341721":"东至县","341722":"石台县","341723":"青阳县"},"341800":{"341802":"宣州区","341821":"郎溪县","341822":"广德县","341823":"泾县","341824":"绩溪县","341825":"旌德县","341881":"宁国市"},"350000":{"350100":"福州市","350200":"厦门市","350300":"莆田市","350400":"三明市","350500":"泉州市","350600":"漳州市","350700":"南平市","350800":"龙岩市","350900":"宁德市"},"350100":{"350102":"鼓楼区","350103":"台江区","350104":"仓山区","350105":"马尾区","350111":"晋安区","350121":"闽侯县","350122":"连江县","350123":"罗源县","350124":"闽清县","350125":"永泰县","350128":"平潭县","350181":"福清市","350182":"长乐市"},"350200":{"350203":"思明区","350205":"海沧区","350206":"湖里区","350211":"集美区","350212":"同安区","350213":"翔安区"},"350300":{"350302":"城厢区","350303":"涵江区","350304":"荔城区","350305":"秀屿区","350322":"仙游县"},"350400":{"350402":"梅列区","350403":"三元区","350421":"明溪县","350423":"清流县","350424":"宁化县","350425":"大田县","350426":"尤溪县","350427":"沙县","350428":"将乐县","350429":"泰宁县","350430":"建宁县","350481":"永安市"},"350500":{"350502":"鲤城区","350503":"丰泽区","350504":"洛江区","350505":"泉港区","350521":"惠安县","350524":"安溪县","350525":"永春县","350526":"德化县","350527":"金门县","350581":"石狮市","350582":"晋江市","350583":"南安市"},"350600":{"350602":"芗城区","350603":"龙文区","350622":"云霄县","350623":"漳浦县","350624":"诏安县","350625":"长泰县","350626":"东山县","350627":"南靖县","350628":"平和县","350629":"华安县","350681":"龙海市"},"350700":{"350702":"延平区","350703":"建阳区","350721":"顺昌县","350722":"浦城县","350723":"光泽县","350724":"松溪县","350725":"政和县","350781":"邵武市","350782":"武夷山市","350783":"建瓯市"},"350800":{"350802":"新罗区","350803":"永定区","350821":"长汀县","350823":"上杭县","350824":"武平县","350825":"连城县","350881":"漳平市"},"350900":{"350902":"蕉城区","350921":"霞浦县","350922":"古田县","350923":"屏南县","350924":"寿宁县","350925":"周宁县","350926":"柘荣县","350981":"福安市","350982":"福鼎市"},"360000":{"360100":"南昌市","360200":"景德镇市","360300":"萍乡市","360400":"九江市","360500":"新余市","360600":"鹰潭市","360700":"赣州市","360800":"吉安市","360900":"宜春市","361000":"抚州市","361100":"上饶市"},"360100":{"360102":"东湖区","360103":"西湖区","360104":"青云谱区","360105":"湾里区","360111":"青山湖区","360112":"新建区","360121":"南昌县","360123":"安义县","360124":"进贤县"},"360200":{"360202":"昌江区","360203":"珠山区","360222":"浮梁县","360281":"乐平市"},"360300":{"360302":"安源区","360313":"湘东区","360321":"莲花县","360322":"上栗县","360323":"芦溪县"},"360400":{"360402":"濂溪区","360403":"浔阳区","360421":"九江县","360423":"武宁县","360424":"修水县","360425":"永修县","360426":"德安县","360428":"都昌县","360429":"湖口县","360430":"彭泽县","360481":"瑞昌市","360482":"共青城市","360483":"庐山市"},"360500":{"360502":"渝水区","360521":"分宜县"},"360600":{"360602":"月湖区","360622":"余江县","360681":"贵溪市"},"360700":{"360702":"章贡区","360703":"南康区","360721":"赣县","360722":"信丰县","360723":"大余县","360724":"上犹县","360725":"崇义县","360726":"安远县","360727":"龙南县","360728":"定南县","360729":"全南县","360730":"宁都县","360731":"于都县","360732":"兴国县","360733":"会昌县","360734":"寻乌县","360735":"石城县","360781":"瑞金市"},"360800":{"360802":"吉州区","360803":"青原区","360821":"吉安县","360822":"吉水县","360823":"峡江县","360824":"新干县","360825":"永丰县","360826":"泰和县","360827":"遂川县","360828":"万安县","360829":"安福县","360830":"永新县","360881":"井冈山市"},"360900":{"360902":"袁州区","360921":"奉新县","360922":"万载县","360923":"上高县","360924":"宜丰县","360925":"靖安县","360926":"铜鼓县","360981":"丰城市","360982":"樟树市","360983":"高安市"},"361000":{"361002":"临川区","361021":"南城县","361022":"黎川县","361023":"南丰县","361024":"崇仁县","361025":"乐安县","361026":"宜黄县","361027":"金溪县","361028":"资溪县","361029":"东乡县","361030":"广昌县"},"361100":{"361102":"信州区","361103":"广丰区","361121":"上饶县","361123":"玉山县","361124":"铅山县","361125":"横峰县","361126":"弋阳县","361127":"余干县","361128":"鄱阳县","361129":"万年县","361130":"婺源县","361181":"德兴市"},"370000":{"370100":"济南市","370200":"青岛市","370300":"淄博市","370400":"枣庄市","370500":"东营市","370600":"烟台市","370700":"潍坊市","370800":"济宁市","370900":"泰安市","371000":"威海市","371100":"日照市","371200":"莱芜市","371300":"临沂市","371400":"德州市","371500":"聊城市","371600":"滨州市","371700":"菏泽市"},"370100":{"370102":"历下区","370103":"市中区","370104":"槐荫区","370105":"天桥区","370112":"历城区","370113":"长清区","370124":"平阴县","370125":"济阳县","370126":"商河县","370181":"章丘市"},"370200":{"370202":"市南区","370203":"市北区","370211":"黄岛区","370212":"崂山区","370213":"李沧区","370214":"城阳区","370281":"胶州市","370282":"即墨市","370283":"平度市","370285":"莱西市"},"370300":{"370302":"淄川区","370303":"张店区","370304":"博山区","370305":"临淄区","370306":"周村区","370321":"桓台县","370322":"高青县","370323":"沂源县"},"370400":{"370402":"市中区","370403":"薛城区","370404":"峄城区","370405":"台儿庄区","370406":"山亭区","370481":"滕州市"},"370500":{"370502":"东营区","370503":"河口区","370505":"垦利区","370522":"利津县","370523":"广饶县"},"370600":{"370602":"芝罘区","370611":"福山区","370612":"牟平区","370613":"莱山区","370634":"长岛县","370681":"龙口市","370682":"莱阳市","370683":"莱州市","370684":"蓬莱市","370685":"招远市","370686":"栖霞市","370687":"海阳市"},"370700":{"370702":"潍城区","370703":"寒亭区","370704":"坊子区","370705":"奎文区","370724":"临朐县","370725":"昌乐县","370781":"青州市","370782":"诸城市","370783":"寿光市","370784":"安丘市","370785":"高密市","370786":"昌邑市"},"370800":{"370811":"任城区","370812":"兖州区","370826":"微山县","370827":"鱼台县","370828":"金乡县","370829":"嘉祥县","370830":"汶上县","370831":"泗水县","370832":"梁山县","370881":"曲阜市","370883":"邹城市"},"370900":{"370902":"泰山区","370911":"岱岳区","370921":"宁阳县","370923":"东平县","370982":"新泰市","370983":"肥城市"},"371000":{"371002":"环翠区","371003":"文登区","371082":"荣成市","371083":"乳山市"},"371100":{"371102":"东港区","371103":"岚山区","371121":"五莲县","371122":"莒县"},"371200":{"371202":"莱城区","371203":"钢城区"},"371300":{"371302":"兰山区","371311":"罗庄区","371312":"河东区","371321":"沂南县","371322":"郯城县","371323":"沂水县","371324":"兰陵县","371325":"费县","371326":"平邑县","371327":"莒南县","371328":"蒙阴县","371329":"临沭县"},"371400":{"371402":"德城区","371403":"陵城区","371422":"宁津县","371423":"庆云县","371424":"临邑县","371425":"齐河县","371426":"平原县","371427":"夏津县","371428":"武城县","371481":"乐陵市","371482":"禹城市"},"371500":{"371502":"东昌府区","371521":"阳谷县","371522":"莘县","371523":"茌平县","371524":"东阿县","371525":"冠县","371526":"高唐县","371581":"临清市"},"371600":{"371602":"滨城区","371603":"沾化区","371621":"惠民县","371622":"阳信县","371623":"无棣县","371625":"博兴县","371626":"邹平县"},"371700":{"371702":"牡丹区","371703":"定陶区","371721":"曹县","371722":"单县","371723":"成武县","371724":"巨野县","371725":"郓城县","371726":"鄄城县","371728":"东明县"},"410000":{"410100":"郑州市","410200":"开封市","410300":"洛阳市","410400":"平顶山市","410500":"安阳市","410600":"鹤壁市","410700":"新乡市","410800":"焦作市","410900":"濮阳市","411000":"许昌市","411100":"漯河市","411200":"三门峡市","411300":"南阳市","411400":"商丘市","411500":"信阳市","411600":"周口市","411700":"驻马店市","419001":"济源市"},"410100":{"410102":"中原区","410103":"二七区","410104":"管城回族区","410105":"金水区","410106":"上街区","410108":"惠济区","410122":"中牟县","410181":"巩义市","410182":"荥阳市","410183":"新密市","410184":"新郑市","410185":"登封市"},"410200":{"410202":"龙亭区","410203":"顺河回族区","410204":"鼓楼区","410205":"禹王台区","410211":"金明区","410212":"祥符区","410221":"杞县","410222":"通许县","410223":"尉氏县","410225":"兰考县"},"410300":{"410302":"老城区","410303":"西工区","410304":"瀍河回族区","410305":"涧西区","410306":"吉利区","410311":"洛龙区","410322":"孟津县","410323":"新安县","410324":"栾川县","410325":"嵩县","410326":"汝阳县","410327":"宜阳县","410328":"洛宁县","410329":"伊川县","410381":"偃师市"},"410400":{"410402":"新华区","410403":"卫东区","410404":"石龙区","410411":"湛河区","410421":"宝丰县","410422":"叶县","410423":"鲁山县","410425":"郏县","410481":"舞钢市","410482":"汝州市"},"410500":{"410502":"文峰区","410503":"北关区","410505":"殷都区","410506":"龙安区","410522":"安阳县","410523":"汤阴县","410526":"滑县","410527":"内黄县","410581":"林州市"},"410600":{"410602":"鹤山区","410603":"山城区","410611":"淇滨区","410621":"浚县","410622":"淇县"},"410700":{"410702":"红旗区","410703":"卫滨区","410704":"凤泉区","410711":"牧野区","410721":"新乡县","410724":"获嘉县","410725":"原阳县","410726":"延津县","410727":"封丘县","410728":"长垣县","410781":"卫辉市","410782":"辉县市"},"410800":{"410802":"解放区","410803":"中站区","410804":"马村区","410811":"山阳区","410821":"修武县","410822":"博爱县","410823":"武陟县","410825":"温县","410882":"沁阳市","410883":"孟州市"},"410900":{"410902":"华龙区","410922":"清丰县","410923":"南乐县","410926":"范县","410927":"台前县","410928":"濮阳县"},"411000":{"411002":"魏都区","411023":"许昌县","411024":"鄢陵县","411025":"襄城县","411081":"禹州市","411082":"长葛市"},"411100":{"411102":"源汇区","411103":"郾城区","411104":"召陵区","411121":"舞阳县","411122":"临颍县"},"411200":{"411202":"湖滨区","411203":"陕州区","411221":"渑池县","411224":"卢氏县","411281":"义马市","411282":"灵宝市"},"411300":{"411302":"宛城区","411303":"卧龙区","411321":"南召县","411322":"方城县","411323":"西峡县","411324":"镇平县","411325":"内乡县","411326":"淅川县","411327":"社旗县","411328":"唐河县","411329":"新野县","411330":"桐柏县","411381":"邓州市"},"411400":{"411402":"梁园区","411403":"睢阳区","411421":"民权县","411422":"睢县","411423":"宁陵县","411424":"柘城县","411425":"虞城县","411426":"夏邑县","411481":"永城市"},"411500":{"411502":"浉河区","411503":"平桥区","411521":"罗山县","411522":"光山县","411523":"新县","411524":"商城县","411525":"固始县","411526":"潢川县","411527":"淮滨县","411528":"息县"},"411600":{"411602":"川汇区","411621":"扶沟县","411622":"西华县","411623":"商水县","411624":"沈丘县","411625":"郸城县","411626":"淮阳县","411627":"太康县","411628":"鹿邑县","411681":"项城市"},"411700":{"411702":"驿城区","411721":"西平县","411722":"上蔡县","411723":"平舆县","411724":"正阳县","411725":"确山县","411726":"泌阳县","411727":"汝南县","411728":"遂平县","411729":"新蔡县"},"419001":{"4190011":"济源市克井镇","41900111":"济源市下冶镇","419001001":"济源市沁园街道","419001002":"济源市济水街道","419001003":"济源市北海街道","419001004":"济源市天坛街道","419001005":"济源市玉泉街道","419001101":"济源市五龙口镇","419001102":"济源市轵城镇","419001103":"济源市承留镇","419001104":"济源市邵原镇","419001105":"济源市坡头镇","419001106":"济源市梨林镇","419001107":"济源市大峪镇","419001108":"济源市思礼镇","419001109":"济源市王屋镇"},"420000":{"420100":"武汉市","420200":"黄石市","420300":"十堰市","420500":"宜昌市","420600":"襄阳市","420700":"鄂州市","420800":"荆门市","420900":"孝感市","421000":"荆州市","421100":"黄冈市","421200":"咸宁市","421300":"随州市","422800":"恩施土家族苗族自治州","429004":"仙桃市","429005":"潜江市","429006":"天门市","429021":"神农架林区"},"420100":{"420102":"江岸区","420103":"江汉区","420104":"硚口区","420105":"汉阳区","420106":"武昌区","420107":"青山区","420111":"洪山区","420112":"东西湖区","420113":"汉南区","420114":"蔡甸区","420115":"江夏区","420116":"黄陂区","420117":"新洲区"},"420200":{"420202":"黄石港区","420203":"西塞山区","420204":"下陆区","420205":"铁山区","420222":"阳新县","420281":"大冶市"},"420300":{"420302":"茅箭区","420303":"张湾区","420304":"郧阳区","420322":"郧西县","420323":"竹山县","420324":"竹溪县","420325":"房县","420381":"丹江口市"},"420500":{"420502":"西陵区","420503":"伍家岗区","420504":"点军区","420505":"猇亭区","420506":"夷陵区","420525":"远安县","420526":"兴山县","420527":"秭归县","420528":"长阳土家族自治县","420529":"五峰土家族自治县","420581":"宜都市","420582":"当阳市","420583":"枝江市"},"420600":{"420602":"襄城区","420606":"樊城区","420607":"襄州区","420624":"南漳县","420625":"谷城县","420626":"保康县","420682":"老河口市","420683":"枣阳市","420684":"宜城市"},"420700":{"420702":"梁子湖区","420703":"华容区","420704":"鄂城区"},"420800":{"420802":"东宝区","420804":"掇刀区","420821":"京山县","420822":"沙洋县","420881":"钟祥市"},"420900":{"420902":"孝南区","420921":"孝昌县","420922":"大悟县","420923":"云梦县","420981":"应城市","420982":"安陆市","420984":"汉川市"},"421000":{"421002":"沙市区","421003":"荆州区","421022":"公安县","421023":"监利县","421024":"江陵县","421081":"石首市","421083":"洪湖市","421087":"松滋市"},"421100":{"421102":"黄州区","421121":"团风县","421122":"红安县","421123":"罗田县","421124":"英山县","421125":"浠水县","421126":"蕲春县","421127":"黄梅县","421181":"麻城市","421182":"武穴市"},"421200":{"421202":"咸安区","421221":"嘉鱼县","421222":"通城县","421223":"崇阳县","421224":"通山县","421281":"赤壁市"},"421300":{"421303":"曾都区","421321":"随县","421381":"广水市"},"422800":{"422801":"恩施市","422802":"利川市","422822":"建始县","422823":"巴东县","422825":"宣恩县","422826":"咸丰县","422827":"来凤县","422828":"鹤峰县"},"429004":{"4290041":"郑场镇","4290044":"工业园区","42900411":"张沟镇","429004001":"沙嘴街道","429004002":"干河街道","429004003":"龙华山","429004101":"毛嘴镇","429004102":"豆河镇","429004103":"三伏潭镇","429004104":"胡场镇","429004105":"长倘口镇","429004106":"西流河镇","429004107":"沙湖镇","429004108":"杨林尾镇","429004109":"彭场镇","429004111":"郭河镇","429004112":"沔城回族镇","429004113":"通海口镇","429004114":"陈场镇","429004401":"九合垸原种场","429004402":"沙湖原种场","429004404":"五湖渔场","429004405":"赵西垸林场","429004407":"畜禽良种场","429004408":"排湖风景区"},"429005":{"4290051":"竹根滩镇","4290054":"江汉石油管理局","42900545":"周矶管理区","429005001":"园林","429005002":"杨市","429005003":"周矶","429005004":"广华","429005005":"泰丰","429005006":"高场","429005101":"渔洋镇","429005102":"王场镇","429005103":"高石碑镇","429005104":"熊口镇","429005105":"老新镇","429005106":"浩口镇","429005107":"积玉口镇","429005108":"张金镇","429005109":"龙湾镇","429005401":"潜江经济开发区","429005451":"后湖管理区","429005452":"熊口管理区","429005453":"总口管理区","429005454":"白鹭湖管理区","429005455":"运粮湖管理区","429005457":"浩口原种场"},"429006":{"4290061":"多宝镇","42900611":"麻洋镇","42900612":"石河镇","42900645":"蒋湖农场","429006001":"竟陵街道","429006002":"侨乡街道开发区","429006003":"杨林街道","429006101":"拖市镇","429006102":"张港镇","429006103":"蒋场镇","429006104":"汪场镇","429006105":"渔薪镇","429006106":"黄潭镇","429006107":"岳口镇","429006108":"横林镇","429006109":"彭市镇","429006111":"多祥镇","429006112":"干驿镇","429006113":"马湾镇","429006114":"卢市镇","429006115":"小板镇","429006116":"九真镇","429006118":"皂市镇","429006119":"胡市镇","429006121":"佛子山镇","429006201":"净潭乡","429006451":"白茅湖农场","429006452":"沉湖管委会"},"429021":{"4290211":"松柏镇","4290212":"宋洛乡","429021101":"阳日镇","429021102":"木鱼镇","429021103":"红坪镇","429021104":"新华镇","429021105":"九湖镇","429021202":"下谷坪土家族乡"},"430000":{"430100":"长沙市","430200":"株洲市","430300":"湘潭市","430400":"衡阳市","430500":"邵阳市","430600":"岳阳市","430700":"常德市","430800":"张家界市","430900":"益阳市","431000":"郴州市","431100":"永州市","431200":"怀化市","431300":"娄底市","433100":"湘西土家族苗族自治州"},"430100":{"430102":"芙蓉区","430103":"天心区","430104":"岳麓区","430105":"开福区","430111":"雨花区","430112":"望城区","430121":"长沙县","430124":"宁乡县","430181":"浏阳市"},"430200":{"430202":"荷塘区","430203":"芦淞区","430204":"石峰区","430211":"天元区","430221":"株洲县","430223":"攸县","430224":"茶陵县","430225":"炎陵县","430281":"醴陵市"},"430300":{"430302":"雨湖区","430304":"岳塘区","430321":"湘潭县","430381":"湘乡市","430382":"韶山市"},"430400":{"430405":"珠晖区","430406":"雁峰区","430407":"石鼓区","430408":"蒸湘区","430412":"南岳区","430421":"衡阳县","430422":"衡南县","430423":"衡山县","430424":"衡东县","430426":"祁东县","430481":"耒阳市","430482":"常宁市"},"430500":{"430502":"双清区","430503":"大祥区","430511":"北塔区","430521":"邵东县","430522":"新邵县","430523":"邵阳县","430524":"隆回县","430525":"洞口县","430527":"绥宁县","430528":"新宁县","430529":"城步苗族自治县","430581":"武冈市"},"430600":{"430602":"岳阳楼区","430603":"云溪区","430611":"君山区","430621":"岳阳县","430623":"华容县","430624":"湘阴县","430626":"平江县","430681":"汨罗市","430682":"临湘市"},"430700":{"430702":"武陵区","430703":"鼎城区","430721":"安乡县","430722":"汉寿县","430723":"澧县","430724":"临澧县","430725":"桃源县","430726":"石门县","430781":"津市市"},"430800":{"430802":"永定区","430811":"武陵源区","430821":"慈利县","430822":"桑植县"},"430900":{"430902":"资阳区","430903":"赫山区","430921":"南县","430922":"桃江县","430923":"安化县","430981":"沅江市"},"431000":{"431002":"北湖区","431003":"苏仙区","431021":"桂阳县","431022":"宜章县","431023":"永兴县","431024":"嘉禾县","431025":"临武县","431026":"汝城县","431027":"桂东县","431028":"安仁县","431081":"资兴市"},"431100":{"431102":"零陵区","431103":"冷水滩区","431121":"祁阳县","431122":"东安县","431123":"双牌县","431124":"道县","431125":"江永县","431126":"宁远县","431127":"蓝山县","431128":"新田县","431129":"江华瑶族自治县"},"431200":{"431202":"鹤城区","431221":"中方县","431222":"沅陵县","431223":"辰溪县","431224":"溆浦县","431225":"会同县","431226":"麻阳苗族自治县","431227":"新晃侗族自治县","431228":"芷江侗族自治县","431229":"靖州苗族侗族自治县","431230":"通道侗族自治县","431281":"洪江市"},"431300":{"431302":"娄星区","431321":"双峰县","431322":"新化县","431381":"冷水江市","431382":"涟源市"},"433100":{"433101":"吉首市","433122":"泸溪县","433123":"凤凰县","433124":"花垣县","433125":"保靖县","433126":"古丈县","433127":"永顺县","433130":"龙山县"},"440000":{"440100":"广州市","440200":"韶关市","440300":"深圳市","440400":"珠海市","440500":"汕头市","440600":"佛山市","440700":"江门市","440800":"湛江市","440900":"茂名市","441200":"肇庆市","441300":"惠州市","441400":"梅州市","441500":"汕尾市","441600":"河源市","441700":"阳江市","441800":"清远市","441900":"东莞市","442000":"中山市","445100":"潮州市","445200":"揭阳市","445300":"云浮市"},"440100":{"440103":"荔湾区","440104":"越秀区","440105":"海珠区","440106":"天河区","440111":"白云区","440112":"黄埔区","440113":"番禺区","440114":"花都区","440115":"南沙区","440117":"从化区","440118":"增城区"},"440200":{"440203":"武江区","440204":"浈江区","440205":"曲江区","440222":"始兴县","440224":"仁化县","440229":"翁源县","440232":"乳源瑶族自治县","440233":"新丰县","440281":"乐昌市","440282":"南雄市"},"440300":{"440303":"罗湖区","440304":"福田区","440305":"南山区","440306":"宝安区","440307":"龙岗区","440308":"盐田区"},"440400":{"440402":"香洲区","440403":"斗门区","440404":"金湾区"},"440500":{"440507":"龙湖区","440511":"金平区","440512":"濠江区","440513":"潮阳区","440514":"潮南区","440515":"澄海区","440523":"南澳县"},"440600":{"440604":"禅城区","440605":"南海区","440606":"顺德区","440607":"三水区","440608":"高明区"},"440700":{"440703":"蓬江区","440704":"江海区","440705":"新会区","440781":"台山市","440783":"开平市","440784":"鹤山市","440785":"恩平市"},"440800":{"440802":"赤坎区","440803":"霞山区","440804":"坡头区","440811":"麻章区","440823":"遂溪县","440825":"徐闻县","440881":"廉江市","440882":"雷州市","440883":"吴川市"},"440900":{"440902":"茂南区","440904":"电白区","440981":"高州市","440982":"化州市","440983":"信宜市"},"441200":{"441202":"端州区","441203":"鼎湖区","441204":"高要区","441223":"广宁县","441224":"怀集县","441225":"封开县","441226":"德庆县","441284":"四会市"},"441300":{"441302":"惠城区","441303":"惠阳区","441322":"博罗县","441323":"惠东县","441324":"龙门县"},"441400":{"441402":"梅江区","441403":"梅县区","441422":"大埔县","441423":"丰顺县","441424":"五华县","441426":"平远县","441427":"蕉岭县","441481":"兴宁市"},"441500":{"441502":"城区","441521":"海丰县","441523":"陆河县","441581":"陆丰市"},"441600":{"441602":"源城区","441621":"紫金县","441622":"龙川县","441623":"连平县","441624":"和平县","441625":"东源县"},"441700":{"441702":"江城区","441704":"阳东区","441721":"阳西县","441781":"阳春市"},"441800":{"441802":"清城区","441803":"清新区","441821":"佛冈县","441823":"阳山县","441825":"连山壮族瑶族自治县","441826":"连南瑶族自治县","441881":"英德市","441882":"连州市"},"441900":{"441900003":"东城街道","441900004":"南城街道","441900005":"万江街道","441900006":"莞城街道","441900101":"石碣镇","441900102":"石龙镇","441900103":"茶山镇","441900104":"石排镇","441900105":"企石镇","441900106":"横沥镇","441900107":"桥头镇","441900108":"谢岗镇","441900109":"东坑镇","441900110":"常平镇","441900111":"寮步镇","441900112":"樟木头镇","441900113":"大朗镇","441900114":"黄江镇","441900115":"清溪镇","441900116":"塘厦镇","441900117":"凤岗镇","441900118":"大岭山镇","441900119":"长安镇","441900121":"虎门镇","441900122":"厚街镇","441900123":"沙田镇","441900124":"道滘镇","441900125":"洪梅镇","441900126":"麻涌镇","441900127":"望牛墩镇","441900128":"中堂镇","441900129":"高埗镇","441900401":"松山湖管委会","441900402":"虎门港管委会","441900403":"东莞生态园"},"442000":{"442000001":"石岐区街道","442000002":"东区街道","442000003":"火炬开发区街道","442000004":"西区街道","442000005":"南区街道","442000006":"五桂山街道","442000100":"小榄镇","442000101":"黄圃镇","442000102":"民众镇","442000103":"东凤镇","442000104":"东升镇","442000105":"古镇镇","442000106":"沙溪镇","442000107":"坦洲镇","442000108":"港口镇","442000109":"三角镇","442000110":"横栏镇","442000111":"南头镇","442000112":"阜沙镇","442000113":"南朗镇","442000114":"三乡镇","442000115":"板芙镇","442000116":"大涌镇","442000117":"神湾镇"},"445100":{"445102":"湘桥区","445103":"潮安区","445122":"饶平县"},"445200":{"445202":"榕城区","445203":"揭东区","445222":"揭西县","445224":"惠来县","445281":"普宁市"},"445300":{"445302":"云城区","445303":"云安区","445321":"新兴县","445322":"郁南县","445381":"罗定市"},"450000":{"450100":"南宁市","450200":"柳州市","450300":"桂林市","450400":"梧州市","450500":"北海市","450600":"防城港市","450700":"钦州市","450800":"贵港市","450900":"玉林市","451000":"百色市","451100":"贺州市","451200":"河池市","451300":"来宾市","451400":"崇左市"},"450100":{"450102":"兴宁区","450103":"青秀区","450105":"江南区","450107":"西乡塘区","450108":"良庆区","450109":"邕宁区","450110":"武鸣区","450123":"隆安县","450124":"马山县","450125":"上林县","450126":"宾阳县","450127":"横县"},"450200":{"450202":"城中区","450203":"鱼峰区","450204":"柳南区","450205":"柳北区","450206":"柳江区","450222":"柳城县","450223":"鹿寨县","450224":"融安县","450225":"融水苗族自治县","450226":"三江侗族自治县"},"450300":{"450302":"秀峰区","450303":"叠彩区","450304":"象山区","450305":"七星区","450311":"雁山区","450312":"临桂区","450321":"阳朔县","450323":"灵川县","450324":"全州县","450325":"兴安县","450326":"永福县","450327":"灌阳县","450328":"龙胜各族自治县","450329":"资源县","450330":"平乐县","450331":"荔浦县","450332":"恭城瑶族自治县"},"450400":{"450403":"万秀区","450405":"长洲区","450406":"龙圩区","450421":"苍梧县","450422":"藤县","450423":"蒙山县","450481":"岑溪市"},"450500":{"450502":"海城区","450503":"银海区","450512":"铁山港区","450521":"合浦县"},"450600":{"450602":"港口区","450603":"防城区","450621":"上思县","450681":"东兴市"},"450700":{"450702":"钦南区","450703":"钦北区","450721":"灵山县","450722":"浦北县"},"450800":{"450802":"港北区","450803":"港南区","450804":"覃塘区","450821":"平南县","450881":"桂平市"},"450900":{"450902":"玉州区","450903":"福绵区","450921":"容县","450922":"陆川县","450923":"博白县","450924":"兴业县","450981":"北流市"},"451000":{"451002":"右江区","451021":"田阳县","451022":"田东县","451023":"平果县","451024":"德保县","451026":"那坡县","451027":"凌云县","451028":"乐业县","451029":"田林县","451030":"西林县","451031":"隆林各族自治县","451081":"靖西市"},"451100":{"451102":"八步区","451103":"平桂区","451121":"昭平县","451122":"钟山县","451123":"富川瑶族自治县"},"451200":{"451202":"金城江区","451221":"南丹县","451222":"天峨县","451223":"凤山县","451224":"东兰县","451225":"罗城仫佬族自治县","451226":"环江毛南族自治县","451227":"巴马瑶族自治县","451228":"都安瑶族自治县","451229":"大化瑶族自治县","451281":"宜州市"},"451300":{"451302":"兴宾区","451321":"忻城县","451322":"象州县","451323":"武宣县","451324":"金秀瑶族自治县","451381":"合山市"},"451400":{"451402":"江州区","451421":"扶绥县","451422":"宁明县","451423":"龙州县","451424":"大新县","451425":"天等县","451481":"凭祥市"},"460000":{"460100":"海口市","460200":"三亚市","460300":"三沙市","460400":"儋州市","469001":"五指山市","469002":"琼海市","469005":"文昌市","469006":"万宁市","469007":"东方市","469021":"定安县","469022":"屯昌县","469023":"澄迈县","469024":"临高县","469025":"白沙黎族自治县","469026":"昌江黎族自治县","469027":"乐东黎族自治县","469028":"陵水黎族自治县","469029":"保亭黎族苗族自治县","469030":"琼中黎族苗族自治县"},"460100":{"460105":"秀英区","460106":"龙华区","460107":"琼山区","460108":"美兰区"},"460200":{"460202":"海棠区","460203":"吉阳区","460204":"天涯区","460205":"崖州区"},"460300":{"460321":"西沙群岛","460322":"南沙群岛","460323":"中沙群岛的岛礁及其海域"},"460400":{"4604001":"那大镇","4604004":"国营西培农场","4604005":"华南热作学院","46040011":"三都镇","460400101":"和庆镇","460400102":"南丰镇","460400103":"大成镇","460400104":"雅星镇","460400105":"兰洋镇","460400106":"光村镇","460400107":"木棠镇","460400108":"海头镇","460400109":"峨蔓镇","460400111":"王五镇","460400112":"白马井镇","460400113":"中和镇","460400114":"排浦镇","460400115":"东成镇","460400116":"新州镇","460400404":"国营西联农场","460400405":"国营蓝洋农场","460400407":"国营八一农场","460400499":"洋浦经济开发区"},"469001":{"4690011":"通什镇","4690012":"畅好乡","4690014":"畅好农场","469001101":"南圣镇","469001102":"毛阳镇","469001103":"番阳镇","469001201":"毛道乡","469001202":"水满乡"},"469002":{"4690021":"嘉积镇","4690024":"国营东太农场","4690025":"彬村山华侨农场","46900211":"大路镇","469002101":"万泉镇","469002102":"石壁镇","469002103":"中原镇","469002104":"博鳌镇","469002105":"阳江镇","469002106":"龙江镇","469002107":"潭门镇","469002108":"塔洋镇","469002109":"长坡镇","469002111":"会山镇","469002402":"国营东红农场","469002403":"国营东升农场"},"469005":{"4690051":"文城镇","4690054":"国营东路农场","46900511":"昌洒镇","469005101":"重兴镇","469005102":"蓬莱镇","469005103":"会文镇","469005104":"东路镇","469005105":"潭牛镇","469005106":"东阁镇","469005107":"文教镇","469005108":"东郊镇","469005109":"龙楼镇","469005111":"翁田镇","469005112":"抱罗镇","469005113":"冯坡镇","469005114":"锦山镇","469005115":"铺前镇","469005116":"公坡镇","469005401":"国营南阳农场","469005402":"国营罗豆农场"},"469006":{"4690061":"万城镇","4690064":"国营东兴农场","4690065":"兴隆华侨农场","46900611":"南桥镇","469006101":"龙滚镇","469006102":"和乐镇","469006103":"后安镇","469006104":"大茂镇","469006105":"东澳镇","469006106":"礼纪镇","469006107":"长丰镇","469006108":"山根镇","469006109":"北大镇","469006111":"三更罗镇","469006401":"国营东和农场","469006404":"国营新中农场","469006501":"地方国营六连林场"},"469007":{"4690071":"八所镇","4690072":"天安乡","4690074":"国营广坝农场","4690075":"东方华侨农场","469007101":"东河镇","469007102":"大田镇","469007103":"感城镇","469007104":"板桥镇","469007105":"三家镇","469007106":"四更镇","469007107":"新龙镇","469007201":"江边乡"},"469021":{"4690211":"定城镇","4690214":"国营中瑞农场","469021101":"新竹镇","469021102":"龙湖镇","469021103":"黄竹镇","469021104":"雷鸣镇","469021105":"龙门镇","469021106":"龙河镇","469021107":"岭口镇","469021108":"翰林镇","469021109":"富文镇","469021401":"国营南海农场","469021402":"国营金鸡岭农场"},"469022":{"4690221":"屯城镇","4690224":"国营中建农场","469022101":"新兴镇","469022102":"枫木镇","469022103":"乌坡镇","469022104":"南吕镇","469022105":"南坤镇","469022106":"坡心镇","469022107":"西昌镇","469022401":"国营中坤农场"},"469023":{"4690231":"金江镇","4690234":"国营红光农场","46902311":"大丰镇","469023101":"老城镇","469023102":"瑞溪镇","469023103":"永发镇","469023104":"加乐镇","469023105":"文儒镇","469023106":"中兴镇","469023107":"仁兴镇","469023108":"福山镇","469023109":"桥头镇","469023402":"国营西达农场","469023405":"国营金安农场"},"469024":{"4690241":"临城镇","4690244":"国营红华农场","469024101":"波莲镇","469024102":"东英镇","469024103":"博厚镇","469024104":"皇桐镇","469024105":"多文镇","469024106":"和舍镇","469024107":"南宝镇","469024108":"新盈镇","469024109":"调楼镇","469024401":"国营加来农场"},"469025":{"4690251":"牙叉镇","4690252":"细水乡","469025101":"七坊镇","469025102":"邦溪镇","469025103":"打安镇","469025201":"元门乡","469025202":"南开乡","469025203":"阜龙乡","469025204":"青松乡","469025205":"金波乡","469025206":"荣邦乡","469025401":"国营白沙农场","469025404":"国营龙江农场","469025408":"国营邦溪农场"},"469026":{"4690261":"石碌镇","4690262":"王下乡","4690265":"国营霸王岭林场","469026101":"叉河镇","469026102":"十月田镇","469026103":"乌烈镇","469026104":"昌化镇","469026105":"海尾镇","469026106":"七叉镇","469026401":"国营红林农场","469026501":"海南矿业联合有限公司"},"469027":{"4690271":"抱由镇","4690275":"国营尖峰岭林业公司","46902711":"莺歌海镇","469027101":"万冲镇","469027102":"大安镇","469027103":"志仲镇","469027104":"千家镇","469027105":"九所镇","469027106":"利国镇","469027107":"黄流镇","469027108":"佛罗镇","469027109":"尖峰镇","469027401":"国营山荣农场","469027402":"国营乐光农场","469027405":"国营保国农场","469027501":"国营莺歌海盐场"},"469028":{"4690281":"椰林镇","4690282":"提蒙乡","4690284":"国营岭门农场","4690285":"国营吊罗山林业公司","469028101":"光坡镇","469028102":"三才镇","469028103":"英州镇","469028104":"隆广镇","469028105":"文罗镇","469028106":"本号镇","469028107":"新村镇","469028108":"黎安镇","469028201":"群英乡","469028401":"国营南平农场"},"469029":{"4690291":"保城镇","4690292":"六弓乡","469029101":"什玲镇","469029102":"加茂镇","469029103":"响水镇","469029104":"新政镇","469029105":"三道镇","469029201":"南林乡","469029202":"毛感乡","469029401":"国营新星农场","469029402":"海南保亭热带作物研究所","469029403":"国营金江农场","469029405":"国营三道农场"},"469030":{"4690301":"营根镇","4690302":"吊罗山乡","4690305":"海南黎母山省级自然保护区管理站","469030101":"湾岭镇","469030102":"黎母山镇","469030103":"和平镇","469030104":"长征镇","469030105":"红毛镇","469030106":"中平镇","469030201":"上安乡","469030202":"什运乡","469030402":"国营阳江农场","469030403":"国营乌石农场","469030406":"国营加钗农场","469030407":"国营长征农场"},"500000":{"500100":"市辖区","500228":"梁平县","500229":"城口县","500230":"丰都县","500231":"垫江县","500232":"武隆县","500233":"忠县","500235":"云阳县","500236":"奉节县","500237":"巫山县","500238":"巫溪县","500240":"石柱土家族自治县","500241":"秀山土家族苗族自治县","500242":"酉阳土家族苗族自治县","500243":"彭水苗族土家族自治县"},"500100":{"500101":"万州区","500102":"涪陵区","500103":"渝中区","500104":"大渡口区","500105":"江北区","500106":"沙坪坝区","500107":"九龙坡区","500108":"南岸区","500109":"北碚区","500110":"綦江区","500111":"大足区","500112":"渝北区","500113":"巴南区","500114":"黔江区","500115":"长寿区","500116":"江津区","500117":"合川区","500118":"永川区","500119":"南川区","500120":"璧山区","500151":"铜梁区","500152":"潼南区","500153":"荣昌区","500154":"开州区"},"500228":{"5002282":"安胜乡","5002284":"梁平县农场","50022811":"聚奎镇","50022812":"合兴镇","500228001":"梁平县梁山街道","500228002":"梁平县双桂街道","500228101":"仁贤镇","500228102":"礼让镇","500228103":"云龙镇","500228104":"屏锦镇","500228106":"袁驿镇","500228107":"新盛镇","500228108":"福禄镇","500228109":"金带镇","500228111":"明达镇","500228112":"荫平镇","500228113":"和林镇","500228114":"回龙镇","500228115":"碧山镇","500228116":"虎城镇","500228117":"七星镇","500228118":"龙门镇","500228119":"文化镇","500228121":"石安镇","500228122":"柏家镇","500228123":"大观镇","500228124":"竹山镇","500228125":"蟠龙镇","500228126":"星桥镇","500228127":"曲水镇","500228201":"铁门乡","500228202":"龙胜乡","500228203":"复平乡","500228205":"紫照乡","500228401":"梁平县双桂工业园区"},"500229":{"50022911":"咸宜镇","50022921":"双河乡","50022922":"厚坪乡","500229001":"葛城街道","500229002":"复兴街道","500229102":"巴山镇","500229103":"坪坝镇","500229104":"庙坝镇","500229105":"明通镇","500229106":"修齐镇","500229107":"高观镇","500229108":"高燕镇","500229109":"东安镇","500229111":"高楠镇","500229201":"龙田乡","500229202":"北屏乡","500229205":"左岚乡","500229208":"沿河乡","500229211":"蓼子乡","500229212":"鸡鸣乡","500229214":"周溪乡","500229216":"明中乡","500229217":"治平乡","500229219":"岚天乡","500229221":"河鱼乡"},"500230":{"500230":"名山街道","50023011":"兴义镇","50023012":"兴龙镇","50023021":"三建乡","500230101":"虎威镇","500230102":"社坛镇","500230103":"三元镇","500230104":"许明寺镇","500230105":"董家镇","500230106":"树人镇","500230107":"十直镇","500230109":"高家镇","500230111":"双路镇","500230112":"江池镇","500230113":"龙河镇","500230114":"武平镇","500230115":"包鸾镇","500230116":"湛普镇","500230118":"南天湖镇","500230119":"保合镇","500230121":"仁沙镇","500230122":"龙孔镇","500230123":"暨龙镇","500230124":"双龙镇","500230125":"仙女湖镇","500230202":"青龙乡","500230206":"太平坝乡","500230207":"都督乡","500230209":"栗子乡"},"500231":{"50023111":"太平镇","50023112":"裴兴镇","500231001":"桂溪街道","500231002":"桂阳街道","500231101":"新民镇","500231102":"沙坪镇","500231103":"周嘉镇","500231104":"普顺镇","500231105":"永安镇","500231106":"高安镇","500231107":"高峰镇","500231108":"五洞镇","500231109":"澄溪镇","500231111":"鹤游镇","500231112":"坪山镇","500231113":"砚台镇","500231114":"曹回镇","500231115":"杠家镇","500231116":"包家镇","500231117":"白家镇","500231118":"永平镇","500231119":"三溪镇","500231121":"黄沙镇","500231122":"长龙镇","500231202":"沙河乡","500231204":"大石乡"},"500232":{"5002321":"巷口镇","5002322":"凤来乡","50023211":"土坎镇","50023221":"后坪苗族土家族乡","500232101":"火炉镇","500232102":"白马镇","500232103":"鸭江镇","500232104":"长坝镇","500232105":"江口镇","500232106":"平桥镇","500232107":"羊角镇","500232108":"仙女山镇","500232109":"桐梓镇","500232111":"和顺镇","500232112":"双河镇","500232202":"庙垭乡","500232203":"石桥苗族土家族乡","500232205":"黄莺乡","500232206":"沧沟乡","500232207":"文复苗族土家族乡","500232208":"土地乡","500232209":"白云乡","500232211":"浩口苗族仡佬族乡","500232212":"接龙乡","500232213":"赵家乡","500232214":"大洞河乡"},"500233":{"50023311":"官坝镇","50023312":"白石镇","50023321":"兴峰乡","500233001":"忠州街道","500233002":"白公街道","500233101":"新生镇","500233102":"任家镇","500233103":"乌杨镇","500233104":"洋渡镇","500233105":"东溪镇","500233106":"复兴镇","500233107":"石宝镇","500233108":"汝溪镇","500233109":"野鹤镇","500233111":"石黄镇","500233112":"马灌镇","500233113":"金鸡镇","500233114":"新立镇","500233115":"双桂镇","500233116":"拔山镇","500233117":"花桥镇","500233118":"永丰镇","500233119":"三汇镇","500233122":"黄金镇","500233201":"善广乡","500233203":"石子乡","500233204":"磨子土家族乡","500233206":"涂井乡","500233208":"金声乡"},"500235":{"50023513":"桑坪镇","50023514":"蔈草镇","500235001":"双江街道","500235002":"青龙街道","500235003":"人和街道","500235004":"盘龙街道","500235105":"龙角镇","500235107":"故陵镇","500235108":"红狮镇","500235115":"路阳镇","500235116":"农坝镇","500235118":"渠马镇","500235121":"黄石镇","500235122":"巴阳镇","500235123":"沙市镇","500235124":"鱼泉镇","500235125":"凤鸣镇","500235127":"宝坪镇","500235128":"南溪镇","500235129":"双土镇","500235131":"江口镇","500235132":"高阳镇","500235133":"平安镇","500235135":"云阳镇","500235136":"云安镇","500235137":"栖霞镇","500235138":"双龙镇","500235139":"泥溪镇","500235141":"养鹿镇","500235142":"水口镇","500235143":"堰坪镇","500235144":"龙洞镇","500235145":"后叶镇","500235146":"耀灵镇","500235147":"大阳镇","500235208":"外郎乡","500235215":"新津乡","500235216":"普安乡","500235218":"洞鹿乡","500235219":"石门乡","500235239":"上坝乡","500235242":"清水土家族自治乡"},"500236":{"50023612":"康乐镇","50023613":"新民镇","50023627":"康坪乡","500236001":"永安街道","500236002":"鱼复街道","500236003":"夔门街道","500236117":"白帝镇","500236118":"草堂镇","500236119":"汾河镇","500236121":"大树镇","500236122":"竹园镇","500236123":"公平镇","500236124":"朱衣镇","500236125":"甲高镇","500236126":"羊市镇","500236127":"吐祥镇","500236128":"兴隆镇","500236129":"青龙镇","500236131":"永乐镇","500236132":"安坪镇","500236133":"五马镇","500236134":"青莲镇","500236265":"岩湾乡","500236266":"平安乡","500236267":"红土乡","500236269":"石岗乡","500236272":"太和土家族乡","500236274":"鹤峰乡","500236275":"冯坪乡","500236276":"长安土家族乡","500236277":"龙桥土家族乡","500236278":"云雾土家族乡"},"500237":{"5002372":"红椿乡","50023711":"铜鼓镇","50023721":"建坪乡","500237001":"高唐街道","500237002":"龙门街道","500237101":"庙宇镇","500237102":"大昌镇","500237103":"福田镇","500237104":"龙溪镇","500237105":"双龙镇","500237106":"官阳镇","500237107":"骡坪镇","500237108":"抱龙镇","500237109":"官渡镇","500237111":"巫峡镇","500237207":"两坪乡","500237208":"曲尺乡","500237211":"大溪乡","500237214":"金坪乡","500237216":"平河乡","500237219":"当阳乡","500237222":"竹贤乡","500237225":"三溪乡","500237227":"培石乡","500237229":"笃坪乡","500237231":"邓家乡"},"500238":{"5002381":"城厢镇","5002384":"红池坝经济开发区","50023811":"峰灵镇","50023821":"长桂乡","50023824":"双阳乡","500238001":"宁河街道","500238002":"柏杨街道","500238101":"凤凰镇","500238102":"宁厂镇","500238103":"上磺镇","500238104":"古路镇","500238105":"文峰镇","500238106":"徐家镇","500238107":"白鹿镇","500238108":"尖山镇","500238109":"下堡镇","500238111":"塘坊镇","500238112":"朝阳镇","500238113":"田坝镇","500238114":"通城镇","500238115":"菱角镇","500238116":"蒲莲镇","500238117":"土城镇","500238204":"胜利乡","500238207":"大河乡","500238208":"天星乡","500238226":"鱼鳞乡","500238227":"乌龙乡","500238234":"中岗乡","500238237":"花台乡","500238239":"兰英乡","500238242":"中梁乡","500238243":"天元乡"},"500240":{"500240":"下路街道","50024011":"龙沙镇","50024021":"石家乡","500240101":"西沱镇","500240103":"悦崃镇","500240104":"临溪镇","500240105":"黄水镇","500240106":"马武镇","500240107":"沙子镇","500240108":"王场镇","500240109":"沿溪镇","500240111":"鱼池镇","500240112":"三河镇","500240113":"大歇镇","500240114":"桥头镇","500240115":"万朝镇","500240116":"冷水镇","500240117":"黄鹤镇","500240203":"黎场乡","500240204":"三星乡","500240205":"六塘乡","500240207":"三益乡","500240208":"王家乡","500240209":"河嘴乡","500240212":"枫木乡","500240213":"中益乡","500240214":"洗新乡","500240216":"龙潭乡","500240217":"新乐乡","500240218":"金铃乡","500240219":"金竹乡"},"500241":{"50024111":"雅江镇","500241001":"中和街道","500241002":"乌杨街道","500241003":"平凯街道","500241102":"清溪场镇","500241103":"隘口镇","500241104":"溶溪镇","500241105":"官庄镇","500241106":"龙池镇","500241107":"石堤镇","500241108":"峨溶镇","500241109":"洪安镇","500241111":"石耶镇","500241112":"梅江镇","500241113":"兰桥镇","500241114":"膏田镇","500241115":"溪口镇","500241116":"妙泉镇","500241117":"宋农镇","500241118":"里仁镇","500241119":"钟灵镇","500241201":"孝溪乡","500241207":"海洋乡","500241208":"大溪乡","500241211":"涌洞乡","500241214":"中平乡","500241215":"岑溪乡"},"500242":{"5002422":"涂市乡","50024211":"泔溪镇","50024221":"后坪乡","50024222":"清泉乡","500242001":"桃花源街道","500242002":"钟多街道","500242101":"龙潭镇","500242102":"麻旺镇","500242103":"酉酬镇","500242104":"大溪镇","500242105":"兴隆镇","500242106":"黑水镇","500242107":"丁市镇","500242108":"龚滩镇","500242109":"李溪镇","500242111":"酉水河镇","500242112":"苍岭镇","500242113":"小河镇","500242114":"板溪镇","500242202":"铜鼓乡","500242204":"可大乡","500242205":"偏柏乡","500242206":"五福乡","500242207":"木叶乡","500242208":"毛坝乡","500242209":"花田乡","500242211":"天馆乡","500242212":"宜居乡","500242213":"万木乡","500242214":"两罾乡","500242215":"板桥乡","500242216":"官清乡","500242217":"南腰界乡","500242218":"车田乡","500242219":"腴地乡","500242221":"庙溪乡","500242222":"浪坪乡","500242223":"双泉乡","500242224":"楠木乡"},"500243":{"50024311":"万足镇","50024321":"走马乡","500243001":"汉葭街道","500243002":"绍庆街道","500243003":"靛水街道","500243101":"保家镇","500243102":"郁山镇","500243103":"高谷镇","500243104":"桑柘镇","500243105":"鹿角镇","500243106":"黄家镇","500243107":"普子镇","500243108":"龙射镇","500243109":"连湖镇","500243111":"平安镇","500243112":"长生镇","500243113":"新田镇","500243114":"鞍子镇","500243115":"太原镇","500243116":"龙溪镇","500243117":"梅子垭镇","500243118":"大同镇","500243201":"岩东乡","500243202":"鹿鸣乡","500243204":"棣棠乡","500243206":"三义乡","500243207":"联合乡","500243208":"石柳乡","500243211":"芦塘乡","500243213":"乔梓乡","500243217":"诸佛乡","500243219":"桐楼乡","500243222":"善感乡","500243223":"双龙乡","500243224":"石盘乡","500243225":"大垭乡","500243226":"润溪乡","500243227":"朗溪乡","500243228":"龙塘乡"},"510000":{"510100":"成都市","510300":"自贡市","510400":"攀枝花市","510500":"泸州市","510600":"德阳市","510700":"绵阳市","510800":"广元市","510900":"遂宁市","511000":"内江市","511100":"乐山市","511300":"南充市","511400":"眉山市","511500":"宜宾市","511600":"广安市","511700":"达州市","511800":"雅安市","511900":"巴中市","512000":"资阳市","513200":"阿坝藏族羌族自治州","513300":"甘孜藏族自治州","513400":"凉山彝族自治州"},"510100":{"510104":"锦江区","510105":"青羊区","510106":"金牛区","510107":"武侯区","510108":"成华区","510112":"龙泉驿区","510113":"青白江区","510114":"新都区","510115":"温江区","510116":"双流区","510121":"金堂县","510124":"郫县","510129":"大邑县","510131":"蒲江县","510132":"新津县","510181":"都江堰市","510182":"彭州市","510183":"邛崃市","510184":"崇州市","510185":"简阳市"},"510300":{"510302":"自流井区","510303":"贡井区","510304":"大安区","510311":"沿滩区","510321":"荣县","510322":"富顺县"},"510400":{"510402":"东区","510403":"西区","510411":"仁和区","510421":"米易县","510422":"盐边县"},"510500":{"510502":"江阳区","510503":"纳溪区","510504":"龙马潭区","510521":"泸县","510522":"合江县","510524":"叙永县","510525":"古蔺县"},"510600":{"510603":"旌阳区","510623":"中江县","510626":"罗江县","510681":"广汉市","510682":"什邡市","510683":"绵竹市"},"510700":{"510703":"涪城区","510704":"游仙区","510705":"安州区","510722":"三台县","510723":"盐亭县","510725":"梓潼县","510726":"北川羌族自治县","510727":"平武县","510781":"江油市"},"510800":{"510802":"利州区","510811":"昭化区","510812":"朝天区","510821":"旺苍县","510822":"青川县","510823":"剑阁县","510824":"苍溪县"},"510900":{"510903":"船山区","510904":"安居区","510921":"蓬溪县","510922":"射洪县","510923":"大英县"},"511000":{"511002":"市中区","511011":"东兴区","511024":"威远县","511025":"资中县","511028":"隆昌县"},"511100":{"511102":"市中区","511111":"沙湾区","511112":"五通桥区","511113":"金口河区","511123":"犍为县","511124":"井研县","511126":"夹江县","511129":"沐川县","511132":"峨边彝族自治县","511133":"马边彝族自治县","511181":"峨眉山市"},"511300":{"511302":"顺庆区","511303":"高坪区","511304":"嘉陵区","511321":"南部县","511322":"营山县","511323":"蓬安县","511324":"仪陇县","511325":"西充县","511381":"阆中市"},"511400":{"511402":"东坡区","511403":"彭山区","511421":"仁寿县","511423":"洪雅县","511424":"丹棱县","511425":"青神县"},"511500":{"511502":"翠屏区","511503":"南溪区","511521":"宜宾县","511523":"江安县","511524":"长宁县","511525":"高县","511526":"珙县","511527":"筠连县","511528":"兴文县","511529":"屏山县"},"511600":{"511602":"广安区","511603":"前锋区","511621":"岳池县","511622":"武胜县","511623":"邻水县","511681":"华蓥市"},"511700":{"511702":"通川区","511703":"达川区","511722":"宣汉县","511723":"开江县","511724":"大竹县","511725":"渠县","511781":"万源市"},"511800":{"511802":"雨城区","511803":"名山区","511822":"荥经县","511823":"汉源县","511824":"石棉县","511825":"天全县","511826":"芦山县","511827":"宝兴县"},"511900":{"511902":"巴州区","511903":"恩阳区","511921":"通江县","511922":"南江县","511923":"平昌县"},"512000":{"512002":"雁江区","512021":"安岳县","512022":"乐至县"},"513200":{"513201":"马尔康市","513221":"汶川县","513222":"理县","513223":"茂县","513224":"松潘县","513225":"九寨沟县","513226":"金川县","513227":"小金县","513228":"黑水县","513230":"壤塘县","513231":"阿坝县","513232":"若尔盖县","513233":"红原县"},"513300":{"513301":"康定市","513322":"泸定县","513323":"丹巴县","513324":"九龙县","513325":"雅江县","513326":"道孚县","513327":"炉霍县","513328":"甘孜县","513329":"新龙县","513330":"德格县","513331":"白玉县","513332":"石渠县","513333":"色达县","513334":"理塘县","513335":"巴塘县","513336":"乡城县","513337":"稻城县","513338":"得荣县"},"513400":{"513401":"西昌市","513422":"木里藏族自治县","513423":"盐源县","513424":"德昌县","513425":"会理县","513426":"会东县","513427":"宁南县","513428":"普格县","513429":"布拖县","513430":"金阳县","513431":"昭觉县","513432":"喜德县","513433":"冕宁县","513434":"越西县","513435":"甘洛县","513436":"美姑县","513437":"雷波县"},"520000":{"520100":"贵阳市","520200":"六盘水市","520300":"遵义市","520400":"安顺市","520500":"毕节市","520600":"铜仁市","522300":"黔西南布依族苗族自治州","522600":"黔东南苗族侗族自治州","522700":"黔南布依族苗族自治州"},"520100":{"520102":"南明区","520103":"云岩区","520111":"花溪区","520112":"乌当区","520113":"白云区","520115":"观山湖区","520121":"开阳县","520122":"息烽县","520123":"修文县","520181":"清镇市"},"520200":{"520201":"钟山区","520203":"六枝特区","520221":"水城县","520222":"盘县"},"520300":{"520302":"红花岗区","520303":"汇川区","520304":"播州区","520322":"桐梓县","520323":"绥阳县","520324":"正安县","520325":"道真仡佬族苗族自治县","520326":"务川仡佬族苗族自治县","520327":"凤冈县","520328":"湄潭县","520329":"余庆县","520330":"习水县","520381":"赤水市","520382":"仁怀市"},"520400":{"520402":"西秀区","520403":"平坝区","520422":"普定县","520423":"镇宁布依族苗族自治县","520424":"关岭布依族苗族自治县","520425":"紫云苗族布依族自治县"},"520500":{"520502":"七星关区","520521":"大方县","520522":"黔西县","520523":"金沙县","520524":"织金县","520525":"纳雍县","520526":"威宁彝族回族苗族自治县","520527":"赫章县"},"520600":{"520602":"碧江区","520603":"万山区","520621":"江口县","520622":"玉屏侗族自治县","520623":"石阡县","520624":"思南县","520625":"印江土家族苗族自治县","520626":"德江县","520627":"沿河土家族自治县","520628":"松桃苗族自治县"},"522300":{"522301":"兴义市","522322":"兴仁县","522323":"普安县","522324":"晴隆县","522325":"贞丰县","522326":"望谟县","522327":"册亨县","522328":"安龙县"},"522600":{"522601":"凯里市","522622":"黄平县","522623":"施秉县","522624":"三穗县","522625":"镇远县","522626":"岑巩县","522627":"天柱县","522628":"锦屏县","522629":"剑河县","522630":"台江县","522631":"黎平县","522632":"榕江县","522633":"从江县","522634":"雷山县","522635":"麻江县","522636":"丹寨县"},"522700":{"522701":"都匀市","522702":"福泉市","522722":"荔波县","522723":"贵定县","522725":"瓮安县","522726":"独山县","522727":"平塘县","522728":"罗甸县","522729":"长顺县","522730":"龙里县","522731":"惠水县","522732":"三都水族自治县"},"530000":{"530100":"昆明市","530300":"曲靖市","530400":"玉溪市","530500":"保山市","530600":"昭通市","530700":"丽江市","530800":"普洱市","530900":"临沧市","532300":"楚雄彝族自治州","532500":"红河哈尼族彝族自治州","532600":"文山壮族苗族自治州","532800":"西双版纳傣族自治州","532900":"大理白族自治州","533100":"德宏傣族景颇族自治州","533300":"怒江傈僳族自治州","533400":"迪庆藏族自治州"},"530100":{"530102":"五华区","530103":"盘龙区","530111":"官渡区","530112":"西山区","530113":"东川区","530114":"呈贡区","530122":"晋宁县","530124":"富民县","530125":"宜良县","530126":"石林彝族自治县","530127":"嵩明县","530128":"禄劝彝族苗族自治县","530129":"寻甸回族彝族自治县","530181":"安宁市"},"530300":{"530302":"麒麟区","530303":"沾益区","530321":"马龙县","530322":"陆良县","530323":"师宗县","530324":"罗平县","530325":"富源县","530326":"会泽县","530381":"宣威市"},"530400":{"530402":"红塔区","530403":"江川区","530422":"澄江县","530423":"通海县","530424":"华宁县","530425":"易门县","530426":"峨山彝族自治县","530427":"新平彝族傣族自治县","530428":"元江哈尼族彝族傣族自治县"},"530500":{"530502":"隆阳区","530521":"施甸县","530523":"龙陵县","530524":"昌宁县","530581":"腾冲市"},"530600":{"530602":"昭阳区","530621":"鲁甸县","530622":"巧家县","530623":"盐津县","530624":"大关县","530625":"永善县","530626":"绥江县","530627":"镇雄县","530628":"彝良县","530629":"威信县","530630":"水富县"},"530700":{"530702":"古城区","530721":"玉龙纳西族自治县","530722":"永胜县","530723":"华坪县","530724":"宁蒗彝族自治县"},"530800":{"530802":"思茅区","530821":"宁洱哈尼族彝族自治县","530822":"墨江哈尼族自治县","530823":"景东彝族自治县","530824":"景谷傣族彝族自治县","530825":"镇沅彝族哈尼族拉祜族自治县","530826":"江城哈尼族彝族自治县","530827":"孟连傣族拉祜族佤族自治县","530828":"澜沧拉祜族自治县","530829":"西盟佤族自治县"},"530900":{"530902":"临翔区","530921":"凤庆县","530922":"云县","530923":"永德县","530924":"镇康县","530925":"双江拉祜族佤族布朗族傣族自治县","530926":"耿马傣族佤族自治县","530927":"沧源佤族自治县"},"532300":{"532301":"楚雄市","532322":"双柏县","532323":"牟定县","532324":"南华县","532325":"姚安县","532326":"大姚县","532327":"永仁县","532328":"元谋县","532329":"武定县","532331":"禄丰县"},"532500":{"532501":"个旧市","532502":"开远市","532503":"蒙自市","532504":"弥勒市","532523":"屏边苗族自治县","532524":"建水县","532525":"石屏县","532527":"泸西县","532528":"元阳县","532529":"红河县","532530":"金平苗族瑶族傣族自治县","532531":"绿春县","532532":"河口瑶族自治县"},"532600":{"532601":"文山市","532622":"砚山县","532623":"西畴县","532624":"麻栗坡县","532625":"马关县","532626":"丘北县","532627":"广南县","532628":"富宁县"},"532800":{"532801":"景洪市","532822":"勐海县","532823":"勐腊县"},"532900":{"532901":"大理市","532922":"漾濞彝族自治县","532923":"祥云县","532924":"宾川县","532925":"弥渡县","532926":"南涧彝族自治县","532927":"巍山彝族回族自治县","532928":"永平县","532929":"云龙县","532930":"洱源县","532931":"剑川县","532932":"鹤庆县"},"533100":{"533102":"瑞丽市","533103":"芒市","533122":"梁河县","533123":"盈江县","533124":"陇川县"},"533300":{"533301":"泸水市","533323":"福贡县","533324":"贡山独龙族怒族自治县","533325":"兰坪白族普米族自治县"},"533400":{"533401":"香格里拉市","533422":"德钦县","533423":"维西傈僳族自治县"},"540000":{"540100":"拉萨市","540200":"日喀则市","540300":"昌都市","540400":"林芝市","540500":"山南市","542400":"那曲地区","542500":"阿里地区"},"540100":{"540102":"城关区","540103":"堆龙德庆区","540121":"林周县","540122":"当雄县","540123":"尼木县","540124":"曲水县","540126":"达孜县","540127":"墨竹工卡县"},"540200":{"540202":"桑珠孜区","540221":"南木林县","540222":"江孜县","540223":"定日县","540224":"萨迦县","540225":"拉孜县","540226":"昂仁县","540227":"谢通门县","540228":"白朗县","540229":"仁布县","540230":"康马县","540231":"定结县","540232":"仲巴县","540233":"亚东县","540234":"吉隆县","540235":"聂拉木县","540236":"萨嘎县","540237":"岗巴县"},"540300":{"540302":"卡若区","540321":"江达县","540322":"贡觉县","540323":"类乌齐县","540324":"丁青县","540325":"察雅县","540326":"八宿县","540327":"左贡县","540328":"芒康县","540329":"洛隆县","540330":"边坝县"},"540400":{"540402":"巴宜区","540421":"工布江达县","540422":"米林县","540423":"墨脱县","540424":"波密县","540425":"察隅县","540426":"朗县"},"540500":{"540502":"乃东区","540521":"扎囊县","540522":"贡嘎县","540523":"桑日县","540524":"琼结县","540525":"曲松县","540526":"措美县","540527":"洛扎县","540528":"加查县","540529":"隆子县","540530":"错那县","540531":"浪卡子县"},"542400":{"542421":"那曲县","542422":"嘉黎县","542423":"比如县","542424":"聂荣县","542425":"安多县","542426":"申扎县","542427":"索县","542428":"班戈县","542429":"巴青县","542430":"尼玛县","542431":"双湖县"},"542500":{"542521":"普兰县","542522":"札达县","542523":"噶尔县","542524":"日土县","542525":"革吉县","542526":"改则县","542527":"措勤县"},"610000":{"610100":"西安市","610200":"铜川市","610300":"宝鸡市","610400":"咸阳市","610500":"渭南市","610600":"延安市","610700":"汉中市","610800":"榆林市","610900":"安康市","611000":"商洛市"},"610100":{"610102":"新城区","610103":"碑林区","610104":"莲湖区","610111":"灞桥区","610112":"未央区","610113":"雁塔区","610114":"阎良区","610115":"临潼区","610116":"长安区","610117":"高陵区","610122":"蓝田县","610124":"周至县","610125":"户县"},"610200":{"610202":"王益区","610203":"印台区","610204":"耀州区","610222":"宜君县"},"610300":{"610302":"渭滨区","610303":"金台区","610304":"陈仓区","610322":"凤翔县","610323":"岐山县","610324":"扶风县","610326":"眉县","610327":"陇县","610328":"千阳县","610329":"麟游县","610330":"凤县","610331":"太白县"},"610400":{"610402":"秦都区","610403":"杨陵区","610404":"渭城区","610422":"三原县","610423":"泾阳县","610424":"乾县","610425":"礼泉县","610426":"永寿县","610427":"彬县","610428":"长武县","610429":"旬邑县","610430":"淳化县","610431":"武功县","610481":"兴平市"},"610500":{"610502":"临渭区","610503":"华州区","610522":"潼关县","610523":"大荔县","610524":"合阳县","610525":"澄城县","610526":"蒲城县","610527":"白水县","610528":"富平县","610581":"韩城市","610582":"华阴市"},"610600":{"610602":"宝塔区","610603":"安塞区","610621":"延长县","610622":"延川县","610623":"子长县","610625":"志丹县","610626":"吴起县","610627":"甘泉县","610628":"富县","610629":"洛川县","610630":"宜川县","610631":"黄龙县","610632":"黄陵县"},"610700":{"610702":"汉台区","610721":"南郑县","610722":"城固县","610723":"洋县","610724":"西乡县","610725":"勉县","610726":"宁强县","610727":"略阳县","610728":"镇巴县","610729":"留坝县","610730":"佛坪县"},"610800":{"610802":"榆阳区","610803":"横山区","610821":"神木县","610822":"府谷县","610824":"靖边县","610825":"定边县","610826":"绥德县","610827":"米脂县","610828":"佳县","610829":"吴堡县","610830":"清涧县","610831":"子洲县"},"610900":{"610902":"汉滨区","610921":"汉阴县","610922":"石泉县","610923":"宁陕县","610924":"紫阳县","610925":"岚皋县","610926":"平利县","610927":"镇坪县","610928":"旬阳县","610929":"白河县"},"611000":{"611002":"商州区","611021":"洛南县","611022":"丹凤县","611023":"商南县","611024":"山阳县","611025":"镇安县","611026":"柞水县"},"620000":{"620100":"兰州市","620200":"嘉峪关市","620300":"金昌市","620400":"白银市","620500":"天水市","620600":"武威市","620700":"张掖市","620800":"平凉市","620900":"酒泉市","621000":"庆阳市","621100":"定西市","621200":"陇南市","622900":"临夏回族自治州","623000":"甘南藏族自治州"},"620100":{"620102":"城关区","620103":"七里河区","620104":"西固区","620105":"安宁区","620111":"红古区","620121":"永登县","620122":"皋兰县","620123":"榆中县"},"620200":{},"620300":{"620302":"金川区","620321":"永昌县"},"620400":{"620402":"白银区","620403":"平川区","620421":"靖远县","620422":"会宁县","620423":"景泰县"},"620500":{"620502":"秦州区","620503":"麦积区","620521":"清水县","620522":"秦安县","620523":"甘谷县","620524":"武山县","620525":"张家川回族自治县"},"620600":{"620602":"凉州区","620621":"民勤县","620622":"古浪县","620623":"天祝藏族自治县"},"620700":{"620702":"甘州区","620721":"肃南裕固族自治县","620722":"民乐县","620723":"临泽县","620724":"高台县","620725":"山丹县"},"620800":{"620802":"崆峒区","620821":"泾川县","620822":"灵台县","620823":"崇信县","620824":"华亭县","620825":"庄浪县","620826":"静宁县"},"620900":{"620902":"肃州区","620921":"金塔县","620922":"瓜州县","620923":"肃北蒙古族自治县","620924":"阿克塞哈萨克族自治县","620981":"玉门市","620982":"敦煌市"},"621000":{"621002":"西峰区","621021":"庆城县","621022":"环县","621023":"华池县","621024":"合水县","621025":"正宁县","621026":"宁县","621027":"镇原县"},"621100":{"621102":"安定区","621121":"通渭县","621122":"陇西县","621123":"渭源县","621124":"临洮县","621125":"漳县","621126":"岷县"},"621200":{"621202":"武都区","621221":"成县","621222":"文县","621223":"宕昌县","621224":"康县","621225":"西和县","621226":"礼县","621227":"徽县","621228":"两当县"},"622900":{"622901":"临夏市","622921":"临夏县","622922":"康乐县","622923":"永靖县","622924":"广河县","622925":"和政县","622926":"东乡族自治县","622927":"积石山保安族东乡族撒拉族自治县"},"623000":{"623001":"合作市","623021":"临潭县","623022":"卓尼县","623023":"舟曲县","623024":"迭部县","623025":"玛曲县","623026":"碌曲县","623027":"夏河县"},"630000":{"630100":"西宁市","630200":"海东市","632200":"海北藏族自治州","632300":"黄南藏族自治州","632500":"海南藏族自治州","632600":"果洛藏族自治州","632700":"玉树藏族自治州","632800":"海西蒙古族藏族自治州"},"630100":{"630102":"城东区","630103":"城中区","630104":"城西区","630105":"城北区","630121":"大通回族土族自治县","630122":"湟中县","630123":"湟源县"},"630200":{"630202":"乐都区","630203":"平安区","630222":"民和回族土族自治县","630223":"互助土族自治县","630224":"化隆回族自治县","630225":"循化撒拉族自治县"},"632200":{"632221":"门源回族自治县","632222":"祁连县","632223":"海晏县","632224":"刚察县"},"632300":{"632321":"同仁县","632322":"尖扎县","632323":"泽库县","632324":"河南蒙古族自治县"},"632500":{"632521":"共和县","632522":"同德县","632523":"贵德县","632524":"兴海县","632525":"贵南县"},"632600":{"632621":"玛沁县","632622":"班玛县","632623":"甘德县","632624":"达日县","632625":"久治县","632626":"玛多县"},"632700":{"632701":"玉树市","632722":"杂多县","632723":"称多县","632724":"治多县","632725":"囊谦县","632726":"曲麻莱县"},"632800":{"632801":"格尔木市","632802":"德令哈市","632821":"乌兰县","632822":"都兰县","632823":"天峻县"},"640000":{"640100":"银川市","640200":"石嘴山市","640300":"吴忠市","640400":"固原市","640500":"中卫市"},"640100":{"640104":"兴庆区","640105":"西夏区","640106":"金凤区","640121":"永宁县","640122":"贺兰县","640181":"灵武市"},"640200":{"640202":"大武口区","640205":"惠农区","640221":"平罗县"},"640300":{"640302":"利通区","640303":"红寺堡区","640323":"盐池县","640324":"同心县","640381":"青铜峡市"},"640400":{"640402":"原州区","640422":"西吉县","640423":"隆德县","640424":"泾源县","640425":"彭阳县"},"640500":{"640502":"沙坡头区","640521":"中宁县","640522":"海原县"},"650000":{"650100":"乌鲁木齐市","650200":"克拉玛依市","650400":"吐鲁番市","650500":"哈密市","652300":"昌吉回族自治州","652700":"博尔塔拉蒙古自治州","652800":"巴音郭楞蒙古自治州","652900":"阿克苏地区","653000":"克孜勒苏柯尔克孜自治州","653100":"喀什地区","653200":"和田地区","654000":"伊犁哈萨克自治州","654200":"塔城地区","654300":"阿勒泰地区","659001":"石河子市","659002":"阿拉尔市","659003":"图木舒克市","659004":"五家渠市","659006":"铁门关市"},"650100":{"650102":"天山区","650103":"沙依巴克区","650104":"新市区","650105":"水磨沟区","650106":"头屯河区","650107":"达坂城区","650109":"米东区","650121":"乌鲁木齐县"},"650200":{"650202":"独山子区","650203":"克拉玛依区","650204":"白碱滩区","650205":"乌尔禾区"},"650400":{"650402":"高昌区","650421":"鄯善县","650422":"托克逊县"},"650500":{"650502":"伊州区","650521":"巴里坤哈萨克自治县","650522":"伊吾县"},"652300":{"652301":"昌吉市","652302":"阜康市","652323":"呼图壁县","652324":"玛纳斯县","652325":"奇台县","652327":"吉木萨尔县","652328":"木垒哈萨克自治县"},"652700":{"652701":"博乐市","652702":"阿拉山口市","652722":"精河县","652723":"温泉县"},"652800":{"652801":"库尔勒市","652822":"轮台县","652823":"尉犁县","652824":"若羌县","652825":"且末县","652826":"焉耆回族自治县","652827":"和静县","652828":"和硕县","652829":"博湖县"},"652900":{"652901":"阿克苏市","652922":"温宿县","652923":"库车县","652924":"沙雅县","652925":"新和县","652926":"拜城县","652927":"乌什县","652928":"阿瓦提县","652929":"柯坪县"},"653000":{"653001":"阿图什市","653022":"阿克陶县","653023":"阿合奇县","653024":"乌恰县"},"653100":{"653101":"喀什市","653121":"疏附县","653122":"疏勒县","653123":"英吉沙县","653124":"泽普县","653125":"莎车县","653126":"叶城县","653127":"麦盖提县","653128":"岳普湖县","653129":"伽师县","653130":"巴楚县","653131":"塔什库尔干塔吉克自治县"},"653200":{"653201":"和田市","653221":"和田县","653222":"墨玉县","653223":"皮山县","653224":"洛浦县","653225":"策勒县","653226":"于田县","653227":"民丰县"},"654000":{"654002":"伊宁市","654003":"奎屯市","654004":"霍尔果斯市","654021":"伊宁县","654022":"察布查尔锡伯自治县","654023":"霍城县","654024":"巩留县","654025":"新源县","654026":"昭苏县","654027":"特克斯县","654028":"尼勒克县"},"654200":{"654201":"塔城市","654202":"乌苏市","654221":"额敏县","654223":"沙湾县","654224":"托里县","654225":"裕民县","654226":"和布克赛尔蒙古自治县"},"654300":{"654301":"阿勒泰市","654321":"布尔津县","654322":"富蕴县","654323":"福海县","654324":"哈巴河县","654325":"青河县","654326":"吉木乃县"},"659001":{"6590011":"北泉镇","6590015":"兵团一五二团","659001001":"新城街道","659001002":"向阳街道","659001003":"红山街道","659001004":"老街街道","659001005":"东城街道","659001101":"石河子镇"},"659002":{"6590022":"托喀依乡","6590025":"兵团七团","65900252":"兵团三团","659002001":"金银川路街道","659002002":"幸福路街道","659002003":"青松路街道","659002004":"南口街道","659002402":"工业园区","659002501":"兵团八团","659002503":"兵团十团","659002504":"兵团十一团","659002505":"兵团十二团","659002506":"兵团十三团","659002507":"兵团十四团","659002509":"兵团十六团","659002511":"兵团第一师水利水电工程处","659002512":"兵团第一师塔里木灌区水利管理处","659002513":"阿拉尔农场","659002514":"兵团第一师幸福农场","659002515":"中心监狱","659002516":"兵团一团","659002517":"兵团农一师沙井子水利管理处","659002518":"西工业园区管理委员会","659002519":"兵团二团"},"659003":{"65900351":"兵团五十团","659003001":"齐干却勒街道","659003002":"前海街道","659003003":"永安坝街道","659003504":"兵团四十四团","659003509":"兵团四十九团","659003511":"兵团五十一团","659003513":"兵团五十三团","659003514":"兵团图木舒克市喀拉拜勒镇"},"659004":{"6590045":"兵团一零一团","659004001":"军垦路街道","659004002":"青湖路街道","659004003":"人民路街道","659004501":"兵团一零二团","659004502":"兵团一零三团"}, + "659006":{"6590061":"博古其镇","659006101":"双丰镇"}, + "710000":{"710100":"台湾省"}, + "710100":{"710101":"金门","710102":"连江","710103":"苗栗","710104":"南投","710105":"澎湖","710106":"屏东","710107":"台东","710108":"台中","710109":"台南","710110":"台北","710111":"桃园","710112":"云林","710113":"新北","710114":"彰化","710115":"嘉义","710116":"新竹","710117":"花莲","710118":"宜兰","710119":"高雄","710120":"基隆"}, + "910000":{"810000":"香港特别行政区","820000":"澳门特别行政区"}, + "810000":{"810101":"中西区","810102":"东区","810103":"九龙城区","810104":"观塘区","810105":"深水埗区","810106":"湾仔区","810107":"黄大仙区","810108":"油尖旺区","810109":"离岛区","810110":"葵青区","810111":"北区","810112":"西贡区","810113":"沙田区","810114":"屯门区","810115":"大埔区","810116":"荃湾区","810117":"元朗区","810118":"香港","810119":"九龙","810120":"新界"}, + "820000":{"820101":"离岛","820102":"澳门半岛","820103":"凼仔","820104":"路凼城","820105":"路环"} +} \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/facelib/arcsoft-sdk-face-4.1.1.0.jar b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/facelib/arcsoft-sdk-face-4.1.1.0.jar new file mode 100644 index 0000000..c49fe45 Binary files /dev/null and b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/facelib/arcsoft-sdk-face-4.1.1.0.jar differ diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/facelib/libarcsoft_face.dll b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/facelib/libarcsoft_face.dll new file mode 100644 index 0000000..d0a4c97 Binary files /dev/null and b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/facelib/libarcsoft_face.dll differ diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/facelib/libarcsoft_face_engine.dll b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/facelib/libarcsoft_face_engine.dll new file mode 100644 index 0000000..db65b00 Binary files /dev/null and b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/facelib/libarcsoft_face_engine.dll differ diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/facelib/libarcsoft_face_engine_jni.dll b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/facelib/libarcsoft_face_engine_jni.dll new file mode 100644 index 0000000..0266b32 Binary files /dev/null and b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/facelib/libarcsoft_face_engine_jni.dll differ diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/pom.xml b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/pom.xml new file mode 100644 index 0000000..a6fad21 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/pom.xml @@ -0,0 +1,14 @@ + + + + jeecg-system-api + org.jeecgframework.boot + 3.4.0 + + 4.0.0 + + jeecg-system-local-api + + \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/common/system/api/ISysBaseAPI.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/common/system/api/ISysBaseAPI.java new file mode 100644 index 0000000..c366b08 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/common/system/api/ISysBaseAPI.java @@ -0,0 +1,354 @@ +package org.jeecg.common.system.api; + +import com.alibaba.fastjson.JSONObject; +import org.jeecg.common.api.CommonAPI; +import org.jeecg.common.api.dto.OnlineAuthDTO; +import org.jeecg.common.api.dto.message.*; +import org.jeecg.common.system.vo.*; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @Description 底层共通业务API,提供其他独立模块调用 + * @Author scott + * @Date 2019-4-20 + * @Version V1.0 + */ +public interface ISysBaseAPI extends CommonAPI { + + + /** + * 1发送系统消息 + * @param message 使用构造器赋值参数 如果不设置category(消息类型)则默认为2 发送系统消息 + */ +// void sendSysAnnouncement(MessageDTO message); +// +// /** +// * 2发送消息 附带业务参数 +// * @param message 使用构造器赋值参数 +// */ +// void sendBusAnnouncement(BusMessageDTO message); +// +// /** +// * 3通过模板发送消息 +// * @param message 使用构造器赋值参数 +// */ +// void sendTemplateAnnouncement(TemplateMessageDTO message); +// +// /** +// * 4通过模板发送消息 附带业务参数 +// * @param message 使用构造器赋值参数 +// */ +// void sendBusTemplateAnnouncement(BusTemplateMessageDTO message); +// +// /** +// * 5通过消息中心模板,生成推送内容 +// * @param templateDTO 使用构造器赋值参数 +// * @return +// */ +// String parseTemplateByCode(TemplateDTO templateDTO); + + /** + * 6根据用户id查询用户信息 + * @param id + * @return + */ + LoginUser getUserById(String id); + + /** + * 7通过用户账号查询角色集合 + * @param username + * @return + */ +// List getRolesByUsername(String username); +// +// /** +// * 8通过用户账号查询部门集合 +// * @param username +// * @return 部门 id +// */ +// List getDepartIdsByUsername(String username); +// +// /** +// * 9通过用户账号查询部门 name +// * @param username +// * @return 部门 name +// */ +// List getDepartNamesByUsername(String username); +// +// +// +// /** 11查询所有的父级字典,按照create_time排序 +// * @return List 字典集合 +// */ +// public List queryAllDict(); +// +// /** +// * 12查询所有分类字典 +// * @return +// */ +// public List queryAllSysCategory(); +// +// +// /** +// * 14查询所有部门 作为字典信息 id -->value,departName -->text +// * @return +// */ +// public List queryAllDepartBackDictModel(); +// +// /** +// * 15根据业务类型及业务id修改消息已读 +// * @param busType +// * @param busId +// */ +// public void updateSysAnnounReadFlag(String busType, String busId); +// +// /** +// * 16查询表字典 支持过滤数据 +// * @param table +// * @param text +// * @param code +// * @param filterSql +// * @return +// */ +// public List queryFilterTableDictInfo(String table, String text, String code, String filterSql); + + /** + * 17查询指定table的 text code 获取字典,包含text和value + * @param table + * @param text + * @param code + * @param keyArray + * @return + */ +// @Deprecated +// public List queryTableDictByKeys(String table, String text, String code, String[] keyArray); + + /** + * 18查询所有用户 返回ComboModel + * @return + */ +// public List queryAllUserBackCombo(); +// +// /** +// * 19分页查询用户 返回JSONObject +// * @param userIds 多个用户id +// * @param pageNo 当前页数 +// * @param pageSize 每页显示条数 +// * @return +// */ +// public JSONObject queryAllUser(String userIds, Integer pageNo, Integer pageSize); +// +// /** +// * 20获取所有角色 +// * @return +// */ +// public List queryAllRole(); +// +// /** +// * 21获取所有角色 带参 +// * @param roleIds 默认选中角色 +// * @return +// */ +// public List queryAllRole(String[] roleIds ); +// +// /** +// * 22通过用户账号查询角色Id集合 +// * @param username +// * @return +// */ +// public List getRoleIdsByUsername(String username); +// +// /** +// * 23通过部门编号查询部门id +// * @param orgCode +// * @return +// */ +// public String getDepartIdsByOrgCode(String orgCode); +// +// /** +// * 24查询所有部门 +// * @return +// */ +// public List getAllSysDepart(); +// +// /** +// * 25查找父级部门 +// * @param departId +// * @return +// */ +// DictModel getParentDepartId(String departId); +// +// /** +// * 26根据部门Id获取部门负责人 +// * @param deptId +// * @return +// */ +// public List getDeptHeadByDepId(String deptId); +// +// /** +// * 27给指定用户发消息 +// * @param userIds +// * @param cmd +// */ +// public void sendWebSocketMsg(String[] userIds, String cmd); +// +// /** +// * 28根据id获取所有参与用户 +// * @param userIds 多个用户id +// * @return +// */ +// public List queryAllUserByIds(String[] userIds); +// +// /** +// * 29将会议签到信息推动到预览 +// * userIds +// * @return +// * @param userId +// */ +// void meetingSignWebsocket(String userId); +// +// /** +// * 30根据name获取所有参与用户 +// * @param userNames 多个用户账户 +// * @return +// */ +// List queryUserByNames(String[] userNames); +// +// +// /** +// * 31获取用户的角色集合 +// * @param username +// * @return +// */ +// Set getUserRoleSet(String username); +// +// /** +// * 32获取用户的权限集合 +// * @param username +// * @return +// */ +// Set getUserPermissionSet(String username); +// +// /** +// * 33判断是否有online访问的权限 +// * @param onlineAuthDTO +// * @return +// */ +// boolean hasOnlineAuth(OnlineAuthDTO onlineAuthDTO); +// +// /** +// * 34通过部门id获取部门全部信息 +// * @param id 部门id +// * @return SysDepartModel对象 +// */ +// SysDepartModel selectAllById(String id); +// +// /** +// * 35根据用户id查询用户所属公司下所有用户ids +// * @param userId +// * @return +// */ +// List queryDeptUsersByUserId(String userId); +// +// /** +// * 36根据多个用户账号(逗号分隔),查询返回多个用户信息 +// * @param usernames +// * @return +// */ +// List queryUsersByUsernames(String usernames); +// +// /** +// * 37根据多个用户ID(逗号分隔),查询返回多个用户信息 +// * @param ids +// * @return +// */ +// List queryUsersByIds(String ids); +// +// /** +// * 38根据多个部门编码(逗号分隔),查询返回多个部门信息 +// * @param orgCodes +// * @return +// */ +// List queryDepartsByOrgcodes(String orgCodes); +// +// /** +// * 39根据多个部门id(逗号分隔),查询返回多个部门信息 +// * @param ids +// * @return +// */ +// List queryDepartsByIds(String ids); +// +// /** +// * 40发送邮件消息 +// * @param email +// * @param title +// * @param content +// */ +// void sendEmailMsg(String email,String title,String content); +// /** +// * 41 获取公司下级部门和公司下所有用户信息 +// * @param orgCode +// * @return List +// */ +// List getDeptUserByOrgCode(String orgCode); +// +// /** +// * 查询分类字典翻译 +// * @param ids 多个分类字典id +// * @return List +// */ +// List loadCategoryDictItem(String ids); +// +// /** +// * 根据字典code加载字典text +// * +// * @param dictCode 顺序:tableName,text,code +// * @param keys 要查询的key +// * @return +// */ +// List loadDictItem(String dictCode, String keys); +// +// /** +// * 根据字典code查询字典项 +// * +// * @param dictCode 顺序:tableName,text,code +// * @param dictCode 要查询的key +// * @return +// */ +// List getDictItems(String dictCode); +// +// /** +// * 根据多个字典code查询多个字典项 +// * @param dictCodeList +// * @return key = dictCode ; value=对应的字典项 +// */ +// Map> getManyDictItems(List dictCodeList); +// +// /** +// * 【JSearchSelectTag下拉搜索组件专用接口】 +// * 大数据量的字典表 走异步加载 即前端输入内容过滤数据 +// * +// * @param dictCode 字典code格式:table,text,code +// * @param keyword 过滤关键字 +// * @param pageSize 分页条数 +// * @return +// */ +// List loadDictItemByKeyword(String dictCode, String keyword, Integer pageSize); +// +// /** +// * 发送模板消息 +// * @param message +// */ +// void sendTemplateMessage(MessageDTO message); +// +// /** +// * 根据模板编码获取模板内容 +// * @param templateCode +// * @return +// */ +// String getTemplateContent(String templateCode); + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/emp/entity/CqBaseEmployee.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/emp/entity/CqBaseEmployee.java new file mode 100644 index 0000000..bf28351 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/emp/entity/CqBaseEmployee.java @@ -0,0 +1,207 @@ +package org.jeecg.emp.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecg.common.aspect.annotation.Dict; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; + +/** + * @Description: 员工信息表 + * @Author: jeecg-boot + * @Date: 2021-12-31 + * @Version: V1.0 + */ +@Data +@TableName("cq_base_employee") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Tag(name = "cq_base_employee对象", description = "员工信息表") +public class CqBaseEmployee implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + //此id由sysUser传过来,因此不能设置id自增 +// @TableId(type = IdType.ASSIGN_ID) + @Schema(title = "员工id") + private String id; + /** + * 工号 + */ + @Excel(name = "工号", width = 15) + @Schema(title = "工号") + private String empSysno; + /** + * 身份证号 + */ + @Excel(name = "身份证号", width = 15) + @Schema(title = "身份证号") + private String empIdcard; + /** + * 生日 + */ + @Excel(name = "生日", width = 15, format = "yyyy-MM-dd") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @Schema(title = "生日") + private java.util.Date empBirthday; + + + /** + * 人员类型 + */ +// @Excel(name = "人员类型", width = 15, dicCode = "p_type") + @Dict(dicCode = "p_type") + @Schema(title = "人员类型") + private String personType; + /** + * 员工职位 + */ + @Excel(name = "员工职位", width = 15, dicCode = "emp_position") + @Dict(dicCode = "emp_position") + @Schema(title = "员工职位") + private String empPosition; + /** + * 员工职务 + */ + @Excel(name = "员工职务", width = 15, dicCode = "emp_job") + @Dict(dicCode = "emp_job") + @Schema(title = "员工职务") + private String empJob; + /** + * 员工子组 + */ +// @Excel(name = "员工子组", width = 15, dicCode = "emp_group") + @Dict(dicCode = "emp_group") + @Schema(title = "员工子组") + private String empGroup; + /** + * 行政级别 + */ + @Excel(name = "行政级别", width = 15, dicCode = "emp_level") + @Dict(dicCode = "emp_level") + @Schema(title = "行政级别") + private String empLevel; + /** + * 员工状态 + */ + @Excel(name = "员工状态", width = 15, dicCode = "emp_status") + @Dict(dicCode = "emp_status") + @Schema(title = "员工状态") + private String empStatus; + /** + * 政治面貌 + */ + @Excel(name = "政治面貌", width = 15, dicCode = "emp_political") + @Dict(dicCode = "emp_political") + @Schema(title = "政治面貌") + private String empPolitical; + /** + * 员工民族 + */ + @Excel(name = "员工民族", width = 15, dicCode = "emp_nation") + @Dict(dicCode = "emp_nation") + @Schema(title = "员工民族") + private String empNation; + /** + * 员工学位 + */ + @Excel(name = "员工学位", width = 15, dicCode = "emp_degree") + @Dict(dicCode = "emp_degree") + @Schema(title = "员工学位") + private String empDegree; + /** + * 籍贯 + */ +// @Excel(name = "籍贯", width = 15) + @Schema(title = "籍贯") + private String empNativeplace; + /** + * 健康状态 + */ +// @Excel(name = "健康状态", width = 15, dicCode = "emp_jk_state") + @Dict(dicCode = "emp_jk_state") + @Schema(title = "健康状态") + private String empJkState; + /** + * 员工工种 + */ +// @Excel(name = "员工工种", width = 15, dicCode = "emp_worktype") + @Dict(dicCode = "emp_worktype") + @Schema(title = "员工工种") + private String empWorktype; + /** + * 合同类型 + */ +// @Excel(name = "合同类型", width = 15, dicCode = "emp_type") + @Dict(dicCode = "emp_type") + @Schema(title = "合同类型") + private String empType; + /** + * 工作地点 + */ +// @Excel(name = "工作地点", width = 15) + @Schema(title = "工作地点") + private String workSpace; + /** + * 居住地点 + */ +// @Excel(name = "居住地点", width = 15) + @Schema(title = "居住地点") + private String liveSpace; + /** + * 备注 + */ +// @Excel(name = "备注", width = 15) + @Schema(title = "备注") + private String empRemark; + + /** + * 性别(1:男 2:女) + */ + @Excel(name = "性别", width = 15,dicCode="sex") + @Dict(dicCode = "sex") + private Integer sex; + + /**身高(m)*/ + @Excel(name = "身高(m)", width = 15) + @Schema(title = "身高(m)") + private Double height; + + /**体重(kg)*/ + @Excel(name = "体重(kg)", width = 15) + @Schema(title = "体重(kg)") + private Double weight; + + /**工作强度 + * 1重度劳动 + * 2中度劳动 + * 3轻度劳动 + * */ + @Excel(name = "工作强度", width = 15, dicCode = "work_strength") + @Dict(dicCode = "work_strength") + @Schema(title = "工作强度") + private String workStrength; + + + @Schema(title = "同步标记(1:已同步,0:未同步)") + private Integer isSync; + + @Schema(title = "删除状态(0-正常,1-已删除)") +// @TableLogic + private Integer delFlag; + /** + * 添加来源 0系统新增 1四合一导入 + */ + private Integer addSource; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/emp/vo/CqBaseEmpVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/emp/vo/CqBaseEmpVo.java new file mode 100644 index 0000000..00390ed --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/emp/vo/CqBaseEmpVo.java @@ -0,0 +1,107 @@ +package org.jeecg.emp.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.jeecg.emp.entity.CqBaseEmployee; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +@Data +public class CqBaseEmpVo extends CqBaseEmployee implements Serializable { + + + /*头像*/ + private String avatar; + /*员工姓名*/ + @Excel(name = "员工姓名", width = 15) + private String realname; + /*员工账号*/ + @Excel(name = "员工账号", width = 15) + private String username; + /*密码*/ + private String password; + /*状态(1-正常,2-冻结)*/ + private Integer status; + /*部门*/ +// @Excel(name="部门",width = 15,dictTable ="sys_depart",dicText = "depart_name",dicCode = "id") +// @Dict(dictTable ="sys_depart",dicText = "depart_name",dicCode = "id") + private String departId; + private String secondDepartId; + @Excel(name="二级部门",width = 15) + private String secondDepartName; + private String thirdDepartId; + @Excel(name="三级部门",width = 15) + private String thirdDepartName; + /*角色*/ + private String selectedroles; + /*部门*/ + private String selecteddeparts; + + @Excel(name = "手机号码", width = 15) + private String phone; + + private String selections; + + + /** + * 当前登录人部门code + */ + private String orgCode; + + /** + * 二三级部门id用 , 分割 如果没有, 说明只有二级部门 三级部门为空 + */ + @Schema(title = "二三级部门id") + private String secondAndThirdDepartIds; + + /** + * 工号 + */ + private String workNo; + + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + @Schema(title = "创建日期") + private Date createTime; + + /** + * 用户类型(0:正式员工 1:非正式员工,2:家庭用户) + */ + private Integer userType; + + /**绑定主用户ID*/ + @Excel(name = "绑定主用户ID", width = 15) + @Schema(title = "绑定主用户ID") + private String mainUserId; + + /**与主账户关系(亲子,夫妻,兄弟姐妹,父母)*/ + @Excel(name = "与主账户关系(亲子,夫妻,兄弟姐妹,父母)", width = 15) + @Schema(title = "与主账户关系(亲子,夫妻,兄弟姐妹,父母)") + private String mainUserRelation; + + /**家庭用户集合*/ + List subUserList; + + String remark; + + @Schema(title ="卡号") + private String cardCode; + + + @Schema(title ="卡号状态(1:正常,2:挂失)") + private Integer cardCodeStatus; + + @Schema(title="部门集合") + private List list; + + //部门集合名称 + private String departNames; + + //直接部门ID + private String departIds; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/emp/vo/DepartVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/emp/vo/DepartVo.java new file mode 100644 index 0000000..10a31ee --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/emp/vo/DepartVo.java @@ -0,0 +1,16 @@ +package org.jeecg.emp.vo; + +import lombok.Data; + +/** + * @author lianlonggang + * @date 2021/12/21 18:06 + * @throws + * @since + */ +@Data +public class DepartVo { + private String id; + private String departName; + private String orgCode; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/emp/vo/EmpInfoVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/emp/vo/EmpInfoVo.java new file mode 100644 index 0000000..f420cd0 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/emp/vo/EmpInfoVo.java @@ -0,0 +1,40 @@ +package org.jeecg.emp.vo; + +import lombok.Data; + +import java.util.Date; + +/** + * @author lianlonggang + * @date 2021/12/21 18:06 + * @throws + * @since + */ +@Data +public class EmpInfoVo { + private String avatar; + private String userId; + private String secondDepartId; + private String secondDepartName; + private String thirdDepartId; + private String thirdDepartName; + private String realname; + private String username; + private String empIdcard; + private String mobilePhone; + private String token; + + private String workNo; + private Date createTime; + + private String password; + private String salt; + + private String level; + + // 控制大屏端是否能查看食堂历史数据(0不行1可以) + private String canteenId; + private String departIds; + private String sex; + private String age; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/emp/vo/UserInfoVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/emp/vo/UserInfoVo.java new file mode 100644 index 0000000..84ed5e5 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/emp/vo/UserInfoVo.java @@ -0,0 +1,18 @@ +package org.jeecg.emp.vo; + +import lombok.Data; + +/** + * @Description: 用户信息 + * @Author: jeecg-boot + * @Date: 2022-07-18 + * @Version: V1.0 + */ +@Data +public class UserInfoVo { + private String id; + private String username; + private String avatar; + private String phone; + private String realname; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/equipment/entity/StEquipment.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/equipment/entity/StEquipment.java new file mode 100644 index 0000000..c98de08 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/equipment/entity/StEquipment.java @@ -0,0 +1,120 @@ +package org.jeecg.equipment.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecgframework.poi.excel.annotation.Excel; + +import java.io.Serializable; +import java.util.Date; + +/** + * @Description: 设备管理表 + * @Author: jeecg-boot + * @Date: 2022-08-30 + * @Version: V1.0 + */ +@Data +@TableName("st_equipment") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Tag(name="st_equipment对象", description="设备管理表") +public class StEquipment implements Serializable { + private static final long serialVersionUID = 1L; + + /**主键*/ + @TableId(type = IdType.ASSIGN_ID) + @Schema(title = "主键") + private String id; + /**创建人*/ + @Schema(title = "创建人") + private String createBy; + /**创建日期*/ + @Schema(title = "创建日期") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + /**更新人*/ + @Schema(title = "更新人") + private String updateBy; + /**更新日期*/ + @Schema(title = "更新日期") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + /**所属部门*/ + @Schema(title = "所属部门") + private String sysOrgCode; + /**设备名称编码*/ + @Excel(name = "设备名称编码", width = 15) + @Schema(title = "设备名称编码") + private String equipmentName; + /**设备编号*/ + @Excel(name = "设备编号", width = 15) + @Schema(title = "设备编号") + private String equipmentCode; + /**状态*/ + @Excel(name = "状态", width = 15) + @Schema(title = "状态") + private Integer status; + /**食堂id*/ + @Excel(name = "食堂id", width = 15) + @Schema(title = "食堂id") + private String canteenId; + /**"食堂名称"*/ + @Excel(name = "食堂名称", width = 15) + @Schema(title = "食堂名称") + @TableField(exist = false) + private String canteenName; + /**client服务内网ip*/ + @Excel(name = "client服务内网ip", width = 15) + @Schema(title = "client服务内网ip") + private String clientServerIp; + /**zhst外网服务ip*/ + @Excel(name = "zhst外网服务ip", width = 15) + @Schema(title = "zhst外网服务ip") + private String zhstServerIp; + /**app包下载路径*/ + @Excel(name = "app包下载路径", width = 15) + @Schema(title = "app包下载路径") + private String appPackageUrl; + /**设备编码*/ + @Excel(name = "设备编码", width = 15) + @Schema(title = "设备编码") + private String appPackageLocalUrl; + /**同步标记(1:已同步,0:未同步)*/ + @Excel(name = "同步标记(1:已同步,0:未同步)", width = 15) + @Schema(title = "同步标记(1:已同步,0:未同步)") + private Integer isSync; + + /**虹软app_id*/ + @Excel(name = "虹软app_id", width = 15) + @Schema(title = "虹软app_id") + private String arcsoftAppId; + + /**虹软sdk_key*/ + @Excel(name = "虹软sdk_key", width = 15) + @Schema(title = "虹软sdk_key") + private String arcsoftSdkKey; + + /**虹软active_key*/ + @Excel(name = "虹软active_key", width = 15) + @Schema(title = "虹软active_key") + private String arcsoftActiveKey; + + @Schema(title = "删除状态(0-正常,1-已删除)") + private Integer delFlag; + + /**所属餐道*/ + @Schema(title = "所属餐道") + private Integer owningTrack; + + /**所属餐道餐道中的顺序*/ + @Schema(title = "所属餐道餐道中的顺序") + private Integer owningTrackOrder; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/equipment/entity/StEquipmentThirdparty.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/equipment/entity/StEquipmentThirdparty.java new file mode 100644 index 0000000..1f4cdca --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/equipment/entity/StEquipmentThirdparty.java @@ -0,0 +1,92 @@ +package org.jeecg.equipment.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecgframework.poi.excel.annotation.Excel; + +import java.io.Serializable; +import java.util.Date; + +/** + * @Description: 第三方设备白名单(体重秤) + * @Author: jeecg-boot + * @Date: 2022-08-30 + * @Version: V1.0 + */ +@Data +@TableName("st_equipment_thirdparty") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Tag(name="第三方设备白名单 对象", description="第三方设备白名单") +public class StEquipmentThirdparty implements Serializable { + private static final long serialVersionUID = 1L; + + /**主键*/ + @TableId(type = IdType.ASSIGN_ID) + @Schema(title = "主键") + private String id; + /**创建人*/ + @Schema(title = "创建人") + private String createBy; + /**创建日期*/ + @Schema(title = "创建日期") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + /**更新人*/ + @Schema(title = "更新人") + private String updateBy; + /**更新日期*/ + @Schema(title = "更新日期") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + /**所属部门*/ + @Schema(title = "所属部门") + private String sysOrgCode; + /**设备名称编码*/ + @Excel(name = "设备名称编码", width = 15) + @Schema(title = "设备名称编码") + private String equipmentName; + /**设备编号*/ + @Excel(name = "设备编号", width = 15) + @Schema(title = "设备编号") + private String equipmentCode; + /**设备权限(0 正常,1:禁用)*/ + @Excel(name = "设备权限(0 正常,1:禁用)", width = 15) + @Schema(title = "设备权限(0 正常,1:禁用)") + private Integer status; + /**组织id*/ + @Excel(name = "组织id", width = 15) + @Schema(title = "组织id") + private String orgId; + /**设备消息通讯MQ地址*/ + @Excel(name = "设备消息通讯MQ地址", width = 15) + @Schema(title = "设备消息通讯MQ地址") + private String mqServerIp; + /**设备消息通讯MQ端口*/ + @Excel(name = "设备消息通讯MQ端口", width = 15) + @Schema(title = "设备消息通讯MQ端口") + private String mqServerPort; + /**业务服务请求地址*/ + @Excel(name = "业务服务请求地址", width = 15) + @Schema(title = "业务服务请求地址") + private String serviceRequestUrl; + /**设备条形码*/ + @Excel(name = "设备条形码", width = 15) + @Schema(title = "设备条形码") + private String equipmentBarCode; + /**同步标记(1:已同步,0:未同步)*/ + @Excel(name = "同步标记(1:已同步,0:未同步)", width = 15) + @Schema(title = "同步标记(1:已同步,0:未同步)") + private Integer isSync; + + @Schema(title = "删除状态(0-正常,1-已删除)") + private Integer delFlag; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/equipment/entity/StWeighSacleOrgCode.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/equipment/entity/StWeighSacleOrgCode.java new file mode 100644 index 0000000..25fc00c --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/equipment/entity/StWeighSacleOrgCode.java @@ -0,0 +1,59 @@ +package org.jeecg.equipment.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecg.common.aspect.annotation.Dict; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** + * @Description: 体重秤设备二级部门关系表 + * @Author: jeecg-boot + */ +@Data +@TableName("st_weigh_sacle_org_code") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +public class StWeighSacleOrgCode implements Serializable { + private static final long serialVersionUID = 1L; + + /**主键*/ + @TableId(type = IdType.ASSIGN_ID) + private String id; + /**创建人*/ + private String createBy; + /**创建日期*/ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + private Date createTime; + /**更新人*/ + private String updateBy; + /**更新日期*/ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + private Date updateTime; + /**所属部门*/ + private String sysOrgCode; + /**禁用状态(1:禁止,0:正常)*/ +// @Excel(name = "禁用状态(1:禁止,0:正常)", width = 15) + @Dict(dicCode = "use_status") + private Integer useStatus; + /**设备编号*/ + @Excel(name = "设备编号", width = 15) + private String equipmentCode; + /**获取人脸库的二级组织code(多个用逗号分割)*/ + private String orgCode; + /**同步标记(1:已同步,0:未同步)*/ + private Integer isSync; + /**删除标记(0:未删除,1:已删除)*/ + @Excel(name = "删除标记(0:未删除,1:已删除)", width = 15) + private Integer delFlag; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/OrderInfo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/OrderInfo.java new file mode 100644 index 0000000..79cd393 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/OrderInfo.java @@ -0,0 +1,108 @@ +package org.jeecg.payment; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class OrderInfo implements Serializable { + + private static final long serialVersionUID = 1L; + + + @Schema(title ="订单id") + public int orderId; + + @Schema(title ="订单类型") + public int orderType;//1、桌台 2、号牌 + + @Schema(title ="订单号") + public String orderNo; + + @Schema(title ="序列号") + private String serialNo; + + @Schema(title ="桌台号") + public String deskNo; + + @Schema(title ="桌台名称") + public String deskName; + + @Schema(title ="订单原价") + public Double originalPrice; + + //后台计算实付金额 + public Double customerPrice; + + @Schema(title ="订单优惠") + public Double preferentialPrice; + + @Schema(title ="会员id") + public Integer memberId; + + @Schema(title ="微信标记") + public String openId; + + @Schema(title ="就餐人数") + public int number; + + @Schema(title ="就餐方式") + public int dineWay;//1打包 2堂食 3自助餐 + + @Schema(title ="备注") + public String remark; + + @Schema(title ="付款方式0 微信 1会员 2混合 3支付宝") //对应订单 1是支付宝 2微信 3现金 4会员 5混合 + public int payWay; + +// @Schema(title ="菜品") +// public List orderFoodList; + + @Schema(title ="就餐时间") + private String dineDate; + + //状态 + @Schema(title ="状态") + private int status; + //状态中文 + @Schema(title ="状态中文") + private String statusName; + @Schema(title ="订单所属机构") + + private int eaId; + @Schema(title ="订单所属机构名称") + private String eaName; + + @Schema(title ="原始订单串") + private String orderText; + + @Schema(title ="创建时间") + private Date createTime; + /** + * 预定用餐时间 + */ + @Schema(title ="预定用餐时间") + private Date reserveDate; + + @Schema(title ="预定用餐人") + private String reserveUser; + + @Schema(title ="预定用电话") + private String reservePhone; + + @Schema(title ="性别") + private Integer sex; + + @Schema(title ="倒计时时间") + private int millisecond; + + //餐盒价格 + private BigDecimal boxPrice; + + //下单时间 + private String selectTime; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/SwClientUserInfoShopDetailsVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/SwClientUserInfoShopDetailsVO.java new file mode 100644 index 0000000..9a28063 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/SwClientUserInfoShopDetailsVO.java @@ -0,0 +1,86 @@ +package org.jeecg.payment; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.jeecg.common.util.BigDecimalSerialize; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 会员查看详情 + * + * @author Ls. + * @date 2021/6/17. + */ +@Data +public class SwClientUserInfoShopDetailsVO implements Serializable { + + @Schema(title ="手机号") + private String phone; + + @Schema(title ="身份证号码") + private String idCard; + + @Schema(title ="姓名") + private String name; + + @Schema(title ="性别") + private Integer sex; + + @Schema(title ="出生日期") + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd") + private Date birthDate; + + @TableField(value = "email") + @Schema(title ="邮箱") + private String email; + + @Schema(title ="省市区") + private String provinces; + @Schema(title ="详细地址") + private String address; + + @Schema(title ="发票抬头") + private String invoiceTitle; + + @Schema(title ="税号") + private String taxCode; + + @Schema(title ="标签") + private String labelName; + + @Schema(title ="备注") + private String remark; + + + @JsonSerialize(using = BigDecimalSerialize.class) + @Schema(title ="充值余额") + private BigDecimal topUpBalance; + + @JsonSerialize(using = BigDecimalSerialize.class) + @Schema(title ="累计充值") + private BigDecimal topUpTotal; + + @JsonSerialize(using = BigDecimalSerialize.class) + @Schema(title ="赠送余额") + private BigDecimal rewardBalance; + + @Schema(title ="积分余额") + private Integer integralBalance; + + @Schema(title ="消费次数") + private Integer consumptionCount; + + @JsonSerialize(using = BigDecimalSerialize.class) + @Schema(title ="累计消费合计") + private BigDecimal consumptionTotal; + + @Schema(title ="卡余额") + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal cardBalance; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StAmountSpentEveryMemberDTO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StAmountSpentEveryMemberDTO.java new file mode 100644 index 0000000..05eec01 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StAmountSpentEveryMemberDTO.java @@ -0,0 +1,40 @@ +package org.jeecg.payment.dto; + +import lombok.Data; + +import java.util.List; + +@Data +public class StAmountSpentEveryMemberDTO { + + /** + * 食堂id + */ + private String canteenId; + /** + * 二/三级部门id + */ + private String secondThirdDepartIds; + /** + * 开始时间 + */ + private String startDate; + /** + * 结束时间 + */ + private String endDate; + /** + * 时间区间 + */ + private List dates; + /** + * 姓名 + */ + private String name; + /** + * 姓名 + */ + private String payName; + private Integer pageNo; + private Integer pageSize; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StBbzxOrderInfoDTO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StBbzxOrderInfoDTO.java new file mode 100644 index 0000000..c7f3dab --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StBbzxOrderInfoDTO.java @@ -0,0 +1,54 @@ +/** + * use jianan code generator created + */ +package org.jeecg.payment.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.jeecg.payment.entity.StBbzxOrderInfo; + + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * 报表中心-店内订单数据传输对象实体类 + * + * @author yucl + * @since 2021-05-31 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class StBbzxOrderInfoDTO extends StBbzxOrderInfo { + private static final long serialVersionUID = 1L; + + @Schema(title ="下单时间1 结账时间2 营业时间3 ") + private int timeType; + + @Schema(title ="开始时间0 结束时间1") + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + private List timeValues; + + @Schema(title ="开始时间", hidden = true) + @JsonIgnore + private String timeStart; + + @Schema(title ="开始时间", hidden = true) + @JsonIgnore + private String timeEnd; + + @Schema(title ="订单原价底0 订单原价高1") + private BigDecimal[] orderPrices; + + @Schema(title ="当前页") + private Integer pageNum; + + @Schema(title ="数量/页") + private Integer pageSize; + + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberBackTopUpDto.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberBackTopUpDto.java new file mode 100644 index 0000000..f50871e --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberBackTopUpDto.java @@ -0,0 +1,27 @@ +package org.jeecg.payment.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 会员卡撤销dto + * @author Ls. + * @date 2021/6/22. + */ +@Data +public class StClientMemberBackTopUpDto { + //主键id + private String id; + //原充值余额 + private BigDecimal oldTopUpBalance; + //原赠送余额 + private BigDecimal oldRewardBalance; + + //需要扣减的充值金额 + private BigDecimal topUpSubtractBalance; + //需要扣减的赠送金额 + private BigDecimal rewardSubtractBalance; + //需要扣减的积分 + private Integer integralSubtractBalance; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberBalanceDTO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberBalanceDTO.java new file mode 100644 index 0000000..e2bc2e3 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberBalanceDTO.java @@ -0,0 +1,39 @@ +package org.jeecg.payment.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; +import java.util.List; + +/** + * @author Ls. + * @date 2021/6/21. + */ +@Data +public class StClientMemberBalanceDTO { + @Schema(title ="手机/卡号") + private String param; + + @Schema(title ="卡类型:全部不传 1后台 2收银") + private Integer memberFrom; + + @Schema(title ="卡状态:全部不传 0禁用1启用") + private Integer status; + + @Schema(title ="开卡时间") + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + private List createTime; + + @Schema(title ="当前页") + private Integer pageNum; + @Schema(title ="数量/页") + private Integer pageSize; + + private Integer eaId; + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberChangeBalanceDTO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberChangeBalanceDTO.java new file mode 100644 index 0000000..e892274 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberChangeBalanceDTO.java @@ -0,0 +1,43 @@ +package org.jeecg.payment.dto; + + + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.math.BigDecimal; + +/** + * 修改余额 + */ +@Data +@Tag(name= "StClientMemberBackOutTopUpDTO对象", description = "修改余额") +public class StClientMemberChangeBalanceDTO { + + @NotBlank(message = "id不可为空") + @Schema(title ="会员余额表主键id") + private String id; + + @NotBlank(message = "调正方式不可为空") + @Schema(title ="调正方式:1补录扣减2") + private Integer status; + + @Schema(title ="充值余额") + private BigDecimal topUpBalance; + + @Schema(title ="赠送余额") + private BigDecimal rewardBalance; + + @Schema(title ="积分") + private Integer integralBalance; + + @Schema(title ="备注") + private String remark; + + + @Schema(title ="食堂id") + private String canteenId; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberSpendMapperDTO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberSpendMapperDTO.java new file mode 100644 index 0000000..dc467d5 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberSpendMapperDTO.java @@ -0,0 +1,26 @@ +package org.jeecg.payment.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author Ls. + * @date 2021/7/13. + */ +@Data +public class StClientMemberSpendMapperDTO { + @Schema(title ="会员余额表主键id") + private String id; + + @Schema(title ="充值余额") + private BigDecimal topUpBalance; + + @Schema(title ="赠送余额") + private BigDecimal rewardBalance; + + @Schema(title ="累计消费合计") + private BigDecimal consumptionTotal; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberTopUpDTO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberTopUpDTO.java new file mode 100644 index 0000000..e9e418e --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberTopUpDTO.java @@ -0,0 +1,58 @@ +package org.jeecg.payment.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.math.BigDecimal; + +/** + * 充值 + * + * @author Ls. + * @date 2021/6/21. + */ +@Data +public class StClientMemberTopUpDTO { + + @NotBlank(message = "id不可为空") + @Schema(title ="会员余额表主键id") + private String id; + + @NotBlank(message = "充值方式不可为空") + @Schema(title ="充值方式:1现金2微信3支付宝,4:前台码,5导入") + private Integer paymentMethod; + + @Schema(title ="充值金额") + private BigDecimal topUpBalance; + + @Schema(title ="赠送余额") + private BigDecimal rewardBalance; + + @Schema(title ="赠送积分") + private Integer integralBalance; + + @Schema(title ="提成人") + private String rewardUserNo; + + @Schema(title ="已开发票:false未开 true已开") + private boolean isInvoice; + + @Schema(title ="计入可开发票金额:false不计入 true计入") + private boolean isInvoiceTotal; + + @Schema(title ="累计发票金额") + private BigDecimal invoiceBalance; + + @Schema(title ="创建人") + private int userId; + + @Schema(title ="食堂ID") + private String canteenId; + + @Schema(title ="导入充值excel的身份证号") + private String cardNo; + @Schema(title ="导入充值excel的缴费单号") + private String serialNo; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberTransactionRecordDTO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberTransactionRecordDTO.java new file mode 100644 index 0000000..b7c24c3 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientMemberTransactionRecordDTO.java @@ -0,0 +1,85 @@ +package org.jeecg.payment.dto; + + + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.List; + +/** + */ +@Data +@Tag(name= "StClientMemberTransactionRecordDTO对象", description = "后台撤销充值列表") +public class StClientMemberTransactionRecordDTO { + @Schema(title ="1结账时间 2交易时间") + private Integer payTimeStatus; + @DateTimeFormat(pattern="yyyy-MM-dd") + @Schema(title ="就餐时间") + private String mainTimeDay; + + private String startTime; + + private String endTime; + /** + * 时间区间 + */ + private List dates; + + @Schema(title ="操作类型:101消费102充值") + private Integer status; + + @Schema(title ="手机号") + private String phone; + + @Schema(title ="会员号") + private String cardNo; + + @Schema(title ="会员姓名") + private String userName; + + @Schema(title ="操作端:0后台1前端") + private Integer updateStatus; + + private String canteenId; + + + @Schema(title ="提成人") + private String rewardUserNo; + + @Schema(title ="当前页") + private Integer pageNo; + @Schema(title ="数量/页") + private Integer pageSize; + + @Schema(title ="工号") + private String workNo; + + /** + * 钱包状态(1-禁用,0-正常) + */ + @Schema(title ="钱包状态(1-禁用,0-正常)") + private Integer isStatus; + + + @Schema(title ="卡号") + private String cardCode; + + + @Schema(title ="卡号状态(1:正常,2:挂失)") + private Integer cardCodeStatus; + + @Schema(title ="扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费,4:自定义金额)") + private Integer businessModel; + + @Schema(title ="结账方式:0卡余额1现金2微信3支付宝4前台码,5充值明细导入") + private Integer paymentMethod; + + @Schema(title="部门集合") + private List list; + + //直接部门ID + private String departId; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientUserInfoShopPageDTO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientUserInfoShopPageDTO.java new file mode 100644 index 0000000..de7b70a --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/dto/StClientUserInfoShopPageDTO.java @@ -0,0 +1,53 @@ +package org.jeecg.payment.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; +import java.util.List; + +/** + * @author Ls. + * @date 2021/6/17. + */ +@Data +public class StClientUserInfoShopPageDTO { + + @Schema(title ="手机号/卡号/姓名") + private String param; + + @Schema(title ="会员来源:0全部1后台2收银") + private Integer memberFrom; + + @Schema(title ="入会时间") + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + private List createTime; + + @Schema(title ="消费时间") + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + private List consumptionTime; + + @Schema(title ="当前页") + private Integer pageNum; + @Schema(title ="数量/页") + private Integer pageSize; + + private Integer eaId; + +// @Schema(title ="入会开始时间") + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + private Date startCreateTime; + +// @Schema(title ="入会结束时间") + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + private Date endCreateTime; + +// @Schema(title ="消费开始时间") + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + private Date startConsumptionTime; + +// @Schema(title ="消费结束时间") + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + private Date endConsumptionTime; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StBbzxOrderInfo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StBbzxOrderInfo.java new file mode 100644 index 0000000..8c0a608 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StBbzxOrderInfo.java @@ -0,0 +1,280 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.jeecg.common.system.base.entity.JeecgEntity; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 报表中心-店内订单实体类 + */ +@Data +@TableName("st_bbzx_order_info") +@EqualsAndHashCode(callSuper = true) +@Tag(name= "SwBbzxOrderInfo对象", description = "报表中心-店内订单") +public class StBbzxOrderInfo extends JeecgEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(title ="订单类型1:桌台点餐 2预点餐 3.自助餐") + private int orderType; + + /** + * 订单号 + */ + @Schema(title ="订单号") + private String orderNo; + + @Schema(title ="流水号") + private String serialNo; + + @Schema(title ="餐盘编码") + private String plateNumber; + + @Schema(title ="餐次名称") + private String dinnerType; + /** + * 经营模式、扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费) + */ + @Schema(title ="经营模式、扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费)") + private Integer businessModel; + + @Schema(title ="就餐日期") + private String eatDay; + + /** + 订单来源(1平板端 、2收银端、3自助点餐机、4小程序、5自助餐) + */ + @Schema(title ="订单来源(1平板端 、2收银端、3自助点餐机、4小程序、5自助餐)") + private Integer orderFrom; + + @Schema(title ="订单来源标记") + private String orderFromSign; + + private String unionId; + + /** + * 用餐方式 + */ + @Schema(title ="用餐方式") + private Integer diningWay; + /** + * 营业时间 + */ + @Schema(title ="营业时间") + private Date businessDate; + /** + * 下单时间 + */ + @Schema(title ="下单时间") + private Date orderDate; + /** + * 结账时间 + */ + @Schema(title ="结账时间") + private Date payOrderDate; + /** + * 撤单时间 + */ + @Schema(title ="撤单时间") + private Date withdrawalsDate; + + @Schema(title ="撤单原因") + private String withdrawalsReason; + + /** + * 桌台id + */ + @Schema(title ="桌台id") + private Integer deskId; + + @Schema(title ="桌台号") + private String deskNo; + + @Schema(title ="桌台名称") + private String deskName; + + @Schema(title ="桌台备注") + private String deskRemark; + + /** + * 用餐人数 + */ + @Schema(title ="用餐人数") + private Integer userNum; + /** + * 用餐原价 + */ + @Schema(title ="订单原价(一次扣费模式:扣费规则里固定金额+补助金额)") + private BigDecimal orderPrice; + /** + * 订单优惠 + */ + @Schema(title ="订单优惠(一次扣费模式:扣费规则里补助金额)") + private BigDecimal preferentialPrice; + /** + * 顾客应付 + */ + @Schema(title ="顾客应付(一次扣费模式:扣费规则里固定金额,按量扣费模式:最终顾客应付)") + private BigDecimal customerPrice; + /** + * 顾客应付 + */ + @Schema(title ="后端顾客应付(按量扣费模式:后端计算金额)") + private BigDecimal customerBackendPrice; + /** + * 顾客应付 + */ + @Schema(title ="前端顾客应付(按量扣费模式:前端计算金额)") + private BigDecimal customerHeadendPrice; + /** + * 支付合计 + */ + @Schema(title ="支付合计") + private BigDecimal payTotal; + + + @Schema(title ="折扣率") + private BigDecimal orderDiscount; + + @Schema(title ="折扣原因") + private String orderDiscountReason; + + @Schema(title ="订单减免") + private BigDecimal orderDerate; + + @Schema(title ="remark") + private String remark; + + @Schema(title ="自动抹零") + private BigDecimal autoChangeZero; + + @Schema(title ="手动抹零") + private BigDecimal changeZero; + + + /** + * 订单收入 + */ + @Schema(title ="订单收入") + private BigDecimal income; + /** + * 结账方式 + * /对应订单 1是支付宝 2微信 3现金 4会员 5混合 + */ + @Schema(title ="结账方式") + private Integer payType; + /** + * 订单状态 + * OrderStatus.ORDER_STATUS_WAIT_ORDER (1, "ORDER_STATUS_WAIT_ORDER ", "待下单"), + * OrderStatus.ORDER_STATUS_WAIT_RECEIVING_ORDERS(2, "ORDER_STATUS_WAIT_RECEIVING_ORDERS", "待接单"), + * OrderStatus.ORDER_STATUS_WAIT_MAKE(3, "ORDER_STATUS_WAIT_MAKE", "待制作"), + * OrderStatus.ORDER_STATUS_WAIT_CHECK_OUT(4, "ORDER_STATUS_WAIT_CHECK_OUT", "待结账"), + * OrderStatus.ORDER_STATUS_CHECK_OUT_ING(0, "ORDER_STATUS_WAIT_CHECK_OUT", "结账中"),//收银台需要的细化状态 + * OrderStatus.ORDER_STATUS_HAS_CHECK_OUT(5, "ORDER_STATUS_HAS_CHECK_OUT", "已完成"), + * OrderStatus.ORDER_STATUS_CANCEL_ORDER(6, "ORDER_STATUS_CANCEL_ORDER", "已撤单"), + * OrderStatus.ORDER_STATUS_FJZ_ORDER(7, "ORDER_STATUS_FJZ_ORDER", "反结账"), + * OrderStatus.ORDER_STATUS_CANCEL_ED(8, "ORDER_STATUS_CANCEL_ED", "后台定时任务关闭"); + */ + @Schema(title ="订单状态") + private Integer status; + /** + * 菜品价格 + */ + @Schema(title ="菜品价格") + private BigDecimal foodPrice; + /** + * 服务费 + */ + @Schema(title ="服务费") + private BigDecimal servicePrice; + /** + * 服务费优惠 + */ + @Schema(title ="服务费优惠") + private BigDecimal serviceDiscountsPrice; + /** + * 服务费收入 + */ + @Schema(title ="服务费收入") + private BigDecimal serviceIncome; + /** + * 预定用餐时间 + */ + @Schema(title ="预定用餐时间") + private Date reserveDate; + + @Schema(title ="预定用餐人") + private String reserveUser; + + @Schema(title ="预定用电话") + private String reservePhone; + /** + * 支付会员id + */ + @Schema(title ="支付会员id") + private String payMemberId; + + /** + * 会员id + */ + @Schema(title ="会员id") + private String memberId; + + @Schema(title ="会员名称") + private String memberName; + + @Schema(title ="预定用电话") + private String memberPhone; + + /** + * 提成人id + */ + @Schema(title ="提成人id") + private Integer commissionId; + + @Schema(title ="提成人名称") + private String commissionName; + + /** + * 整单备注 + */ + @Schema(title ="整单备注") + private String orderRemark; + + @Schema(title ="原始备注") + private String orderText; + + @Schema(title ="桌台区域名称") + private String areaName; + + @TableField(exist = false) + @Schema(title ="机构名称") + private String eaName; + + /** + * 食堂id + */ + @Schema(title ="食堂id") + private String canteenId; + + @Schema(title ="同步标记(1:已同步,0:未同步)") + private Integer isSync; + + @Schema(title ="删除标记(1:删除,0:未删除)") + private Integer isDel; + /** + * 闸机记录的支付唯一id(用于判断防止重复支付的标记) + */ + private String payId; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StBbzxOrderInfoPay.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StBbzxOrderInfoPay.java new file mode 100644 index 0000000..4a2aac9 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StBbzxOrderInfoPay.java @@ -0,0 +1,89 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.jeecg.common.system.base.entity.JeecgEntity; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 支付信息实体类 + * + */ +@Data +@TableName("st_bbzx_order_info_pay") +@EqualsAndHashCode(callSuper = true) +@Tag(name= "SwBbzxOrderInfoPay对象", description = "支付信息") +public class StBbzxOrderInfoPay extends JeecgEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + private String orderId; + + private String orderNo; + /** + * 支付方式 + * public static final String PAYMENT_ALI_PAY = "1"; 支付宝 + * public static final String PAYMENT_WX_PAY = "2"; 微信 + * public static final String PAYMENT_CASH_PAY = "3"; 现金 + * public static final String PAYMENT_VIP_PAY = "4"; 余额 + */ + @Schema(title ="支付方式") + private Integer payType; + /** + * 支付价格 + */ + @Schema(title ="支付价格") + private BigDecimal payPrice; + /** + * 优惠价格 + */ + @Schema(title ="优惠价格") + private BigDecimal payDiscount; + /** + * 支付收入 + */ + @Schema(title ="支付收入") + private BigDecimal payIncome; + /** + * 状态1==支付成功/other==失败 + */ + @Schema(title ="状态") + private Integer status; + /** + * 操作人 + */ + @Schema(title ="操作人") + private String userId; + /** + * 备注 + */ + @Schema(title ="备注") + private String remark; + + @Schema(title ="支付编码") + private String payCode; + + /** + * 是否删除(1-已删除,0-正常) + */ +// @JsonIgnore + @Schema(title ="是否已删除(1-true,0-false)") + private Integer isDel; + + /** + * 食堂id + */ + @Schema(title ="食堂id") + private String canteenId; + + @Schema(title ="同步标记(1:已同步,0:未同步)") + private Integer isSync; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StClientMemberBalance.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StClientMemberBalance.java new file mode 100644 index 0000000..87b9dcd --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StClientMemberBalance.java @@ -0,0 +1,102 @@ +package org.jeecg.payment.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import org.jeecg.common.system.base.entity.JeecgEntity; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 用户钱包表 + */ + +@Tag(name="user-entity-SwClientMemberBalance") +@Data +@TableName(value = "st_client_member_balance") +public class StClientMemberBalance extends JeecgEntity implements Serializable { + + @Schema(title ="会员id") + private String memberId; + + @Schema(title ="充值余额(元)") + private BigDecimal topUpBalance; + + @Schema(title ="累计充值") + private BigDecimal topUpTotal; + + @Schema(title ="赠送余额") + private BigDecimal rewardBalance; + + @Schema(title ="积分余额") + private Integer integralBalance; + + @Schema(title ="消费次数") + private Integer consumptionCount; + + @Schema(title ="累计消费合计") + private BigDecimal consumptionTotal; + + @Schema(title ="最后消费时间") + private Date lastConsumptionTime; + + @Schema(title ="首充时间") + private Date firstTopUpTime; + + @Schema(title ="可开发票金额") + private BigDecimal invoiceTotal; + + @Schema(title ="用户类型:内部用户=0,外部用户=1") + private Integer userInnerType; + + /** + * 是否删除(1-已删除,0-正常) + */ +// @TableLogic( value = "0", delval = "1") +// @JsonIgnore + @Schema(title ="是否已删除(1-true,0-false)") + private Integer isDel; + + @Schema(title ="同步标记(1:已同步,0:未同步)") + private Integer isSync; + + /** + * 钱包状态(1-禁用,0-正常) + */ + @Schema(title ="钱包状态(1-禁用,0-正常)") + private Integer isStatus; + + /** + * 食堂ID + */ + @Schema(title ="食堂ID") +// @JsonIgnore + private String canteenId; + + private static final long serialVersionUID = 1L; + + public static final String COL_ID = "id"; + + public static final String COL_MEMBER_ID = "member_id"; + + public static final String COL_TOP_UP_BALANCE = "top_up_balance"; + + public static final String COL_REWARD_BALANCE = "reward_balance"; + + public static final String COL_INTEGRAL_BALANCE = "integral_balance"; + + public static final String COL_CONSUMPTION_COUNT = "consumption_count"; + + public static final String COL_CONSUMPTION_TOTAL = "consumption_total"; + + public static final String COL_LAST_CONSUMPTION_TIME = "last_consumption_time"; + + public static final String COL_FIRST_TOP_UP_TIME = "first_top_up_time"; + + public static final String COL_IS_DEL = "is_del"; + + public static final String COL_EA_ID = "canteen_id"; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StClientMemberLevelDeductRule.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StClientMemberLevelDeductRule.java new file mode 100644 index 0000000..08a2d2d --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StClientMemberLevelDeductRule.java @@ -0,0 +1,64 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import org.jeecg.common.system.base.entity.JeecgEntity; + +import java.io.Serializable; + +/** + * 扣费规则实体类 + */ +@Data +@TableName("st_client_member_level_deduct_rule") +@Tag(name= "StClientMemberLevelDeductRule对象", description = "StClientMemberLevelDeductRule对象") +public class StClientMemberLevelDeductRule extends JeecgEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + + @TableField(value = "rule_name") + @Schema(title = "规则名称") + private String ruleName; + + /** + * 扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费) + */ + @TableField(value = "deduction_type") + @Schema(title ="扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费)") + private Integer deductionType; + + + /** + * 扣费规则(fixedDeduction:固定扣费,rewardDeduction:赠送扣费,ratioDeduction:比例扣费) + * + */ + @TableField(value = "deduction_rule") + @Schema(title = "扣费规则(fixedDeduction:固定扣费,rewardDeduction:赠送扣费,ratioDeduction:比例扣费)") + private String deductionRule; + + /** + * 食堂ID + */ + @Schema(title ="食堂ID") + private String canteenId; + + /** + * 启用=1,禁用=0 + */ + @TableField(value = "is_enable") + @Schema(title ="启用=1,禁用=0") + private Integer isEnable; + + @Schema(title ="同步标记(1:已同步,0:未同步)") + private Integer isSync; + + @Schema(title ="删除状态(0-正常,1-已删除)") + private Integer delFlag; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StClientMemberLevelDemotionTime.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StClientMemberLevelDemotionTime.java new file mode 100644 index 0000000..2560e64 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StClientMemberLevelDemotionTime.java @@ -0,0 +1,123 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import org.jeecg.common.system.base.entity.JeecgEntity; + +import java.io.Serializable; + +/** + * 储值规则实体类 + */ +@Data +@TableName("st_client_member_level_demotion_time") +@Tag(name= "StClientMemberLevelDemotionTime对象", description = "StClientMemberLevelDemotionTime对象") +public class StClientMemberLevelDemotionTime extends JeecgEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + + @TableField(value = "rule_name") + @Schema(title = "规则名称") + private String ruleName; + /** + * 小时 + */ + @Schema(title ="小时") + @TableField("d_hour") + private Integer dHour; + /** + * 分钟 + */ + @Schema(title ="分钟") + @TableField("d_minute") + private Integer dMinute; + + /** + * 扣款顺序(1储值和赠送余额等比例扣减,2先储值余额,后赠送余额,3先赠送余额,后储值余额) + */ + @TableField(value = "payment_order") + @Schema(title ="扣款顺序(1储值和赠送余额等比例扣减,2先储值余额,后赠送余额,3先赠送余额,后储值余额") + private Integer paymentOrder; + + /** + * 储值规则1固定2比例 + */ + @TableField(value = "stored_rule_flag") + @Schema(title ="储值规则1固定2比例") + private Integer storedRuleFlag; + + /** + * 启用=1,禁用=0 + */ + @TableField(value = "is_enable") + @Schema(title ="启用=1,禁用=0") + private Integer isEnable; + + /** + * 赠送金额有效期:1长期有效,0当月有效 + */ + @TableField(value = "reward_amount_reset") + @Schema(title ="赠送金额有效期:1长期有效,0当月有效") + private Integer rewardAmountReset; + + + /** + * 储值规则(rechargePresenter:充值金额,rewardAmount:赠送金额,rewardIntegral:赠送积分,rewardStatus:赠送方式(1固定2比例) + * ) + */ + @TableField(value = "stored_rule") + @Schema(title = "储值规则1固定2比例(rechargePresenter:充值金额,rewardAmount:赠送金额,rewardIntegral:赠送积分)") + private String storedRule; + + /** + * 使用规则(maxUse:当日使用上限比例(单位:%),expTime:限制次日失效时间) + */ + @TableField(value = "use_rule") + @Schema(title ="使用规则(maxUse:当日使用上限比例(单位:%),expTime:限制次日失效时间)") + private String useRule; + + /** + * 积分抵现规则(integralCount:使用积分量,deductionAmount:抵现*元) + */ + @TableField(value = "integral_rule") + @Schema(title = "积分抵现规则(integralCount:使用积分量,deductionAmount:抵现*元)") + private String integralRule; + + /** + * 单笔订单积分抵扣上限 + */ + @Schema(title = "单笔订单积分抵扣上限:iPattern:(积分抵扣上限1/抵扣上限比例为应收2) iUpperOrScale:积分抵扣个数/抵扣比例(%)") + private String singleMaxIntegralRule; + + /** + * 积分清零规则 + */ + @Schema(title = "积分清零规则:iStatus:1开启/0关闭,iMonth:关闭时间(年),iDay:关闭时间(月)") + private String integralResetRule; + + /** + * 积分清零提示短信 + */ + @Schema(title = "积分清零提示短信 mStatus:1开启/0关闭,resetBeforeDay:清零前多少天提示") + private String integralResetRemindMessage; + + /** + * 食堂ID + */ + @Schema(title ="食堂ID") + private String canteenId; + + + @Schema(title ="同步标记(1:已同步,0:未同步)") + private Integer isSync; + + @Schema(title ="删除状态(0-正常,1-已删除)") + private Integer delFlag; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StClientMemberTransactionRecord.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StClientMemberTransactionRecord.java new file mode 100644 index 0000000..d6c5caf --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/entity/StClientMemberTransactionRecord.java @@ -0,0 +1,174 @@ +package org.jeecg.payment.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.jeecg.common.system.base.entity.JeecgEntity; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 交易记录表 + */ + +@Data +@TableName(value = "st_client_member_transaction_record") +public class StClientMemberTransactionRecord extends JeecgEntity implements Serializable { + + /** + * 会员id + */ + @TableField(value = "member_id") + @Schema(title ="会员id") + private String memberId; + + /** + * 支付会员id + */ + @Schema(title ="支付会员id") + private String payMemberId; + + /** + * 交易流水号 + */ + @TableField(value = "serial_no") + @Schema(title ="交易流水号") + private String serialNo; + + /** + * 订单号 + */ + @TableField(value = "order_no") + @Schema(title ="订单号") + private String orderNo; + + /** + * 操作类型:1消费2充值 + */ + @TableField(value = "status") + @Schema(title ="操作类型:MemberTransactionTypeEnum枚举类") + private Integer status; + + /** + * 交易金额 + */ + @TableField(value = "transaction_amount") + @Schema(title ="交易金额(元)") + private BigDecimal transactionAmount; + + /** + * 结账方式:0卡余额1自定义记账-前台码2微信3支付宝4前台码 + */ + @TableField(value = "payment_method") + @Schema(title ="结账/退回方式:0卡余额1现金2微信3支付宝4前台码,MemberEnum.PAYMENT_METHOD_BY_ALI") + private Integer paymentMethod; + + /** + * 储值余额变动 + */ + @TableField(value = "stored_balance") + @Schema(title ="储值余额变动")//余额退款时表示:充值余额 + private BigDecimal storedBalance; + + /** + * 奖励余额变动 + */ + @TableField(value = "reward_balance") + @Schema(title ="奖励余额变动")//余额退款时表示:赠送余额 + private BigDecimal rewardBalance; + + /** + * 积分余额变动 + */ + @TableField(value = "integral_balance") + @Schema(title ="积分变动") + private Integer integralBalance; + + @Schema(title ="可用余额:充值余额+奖励")//余额退款时表示:累计充值总额=充值+赠送 + private BigDecimal availableBalance; + + /** + * 结账时间 + */ + @TableField(value = "pay_time") + @Schema(title ="结账时间") + private Date payTime; + + /** + * 操作端:0后台1前端 + */ + @TableField(value = "update_status") + @Schema(title ="操作端:0后台1前端") + private Integer updateStatus; + + @Schema(title ="备注") + private String remark; + + @Schema(title ="提成人") + private String rewardUserNo; + + @Schema(title ="已开发票:false未开 true已开") + private Integer isInvoice; + + + @Schema(title ="微信充值商户订单号(退款用)") + private String payOrderNo; + + /** + * 是否删除(1-已删除,0-正常) + */ +// @TableLogic( value = "0", delval = "1") +// @JsonIgnore + @Schema(title ="是否已删除(1-true,0-false)") + private Integer isDel; + + /** + * 食堂id + */ + @Schema(title ="食堂id") + private String canteenId; + + @Schema(title ="同步标记(1:已同步,0:未同步)") + private Integer isSync; + + private static final long serialVersionUID = 1L; + + public static final String COL_ID = "id"; + + public static final String COL_MEMBER_ID = "member_id"; + + public static final String COL_SERIAL_NO = "serial_no"; + + public static final String COL_ORDER_NO = "order_no"; + + public static final String COL_STATUS = "status"; + + public static final String COL_TRANSACTION_AMOUNT = "transaction_amount"; + + public static final String COL_PAYMENT_METHOD = "payment_method"; + + public static final String COL_STORED_BALANCE = "stored_balance"; + + public static final String COL_REWARD_BALANCE = "reward_balance"; + + public static final String COL_INTEGRAL_BALANCE = "integral_balance"; + + public static final String COL_PAY_TIME = "pay_time"; + + public static final String COL_UPDATE_STATUS = "update_status"; + + public static final String COL_CREATE_USER_NO = "create_by"; + + public static final String COL_CREATE_TIME = "create_time"; + + public static final String COL_UPDATE_USER_NO = "update_by"; + + public static final String COL_UPDATE_TIME = "update_time"; + + public static final String COL_IS_DEL = "is_del"; + + public static final String COL_EA_ID = "canteen_id"; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/DeductionTypeEnum.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/DeductionTypeEnum.java new file mode 100644 index 0000000..08c5865 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/DeductionTypeEnum.java @@ -0,0 +1,42 @@ +package org.jeecg.payment.enums; + +/** + * 扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费) + */ +public enum DeductionTypeEnum { + FIXED_WITHHOLDING_FEE(1, "固定扣费"), + FIXED_AND_SUBSIDY_DEDUCTION_FEE(2, "固定+补贴扣费"), + DEDUCT_FEES_BY_VOLUME(3, "按量扣费"), + ; + + private Integer code; + private String name; + + + DeductionTypeEnum(Integer code, String name) { + this.name = name; + this.code = code; + } + + public String getName() { + return name; + } + + + public Integer getCode() { + return code; + } + + public static String getName(Integer code) { + DeductionTypeEnum[] enums = values(); + int length = enums.length; + + for (int i = 0; i < length; ++i) { + DeductionTypeEnum item = enums[i]; + if (item.code.equals(code)) { + return item.name; + } + } + return ""; + } +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/MemberEnum.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/MemberEnum.java new file mode 100644 index 0000000..92e74c7 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/MemberEnum.java @@ -0,0 +1,49 @@ +package org.jeecg.payment.enums; + +/** + * 会员卡相关 + */ +public enum MemberEnum { + PAYMENT_METHOD_BY_CARD(0, "结账方式_卡余额","卡余额"), + PAYMENT_METHOD_BY_CASH(1, "结账方式_现金","现金"), + PAYMENT_METHOD_BY_WX(2, "结账方式_微信","微信"), + PAYMENT_METHOD_BY_ALI(3, "结账方式_支付宝","支付宝"), + PAYMENT_METHOD_BY_COUNTER(4, "结账方式_前台码","前台码"), + ; + + private Integer code; + private String name; + private String xcxName; + + + MemberEnum(Integer code, String name, String xcxName) { + this.name = name; + this.code = code; + this.xcxName = xcxName; + } + + public String getName() { + return name; + } + + public String getXcxName() { + return xcxName; + } + + public Integer getCode() { + return code; + } + + public static String getXcxName(Integer code) { + MemberEnum[] enums = values(); + int length = enums.length; + + for (int i = 0; i < length; ++i) { + MemberEnum item = enums[i]; + if (item.code.equals(code)) { + return item.xcxName; + } + } + return null; + } +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/MemberLevelEnum.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/MemberLevelEnum.java new file mode 100644 index 0000000..7da8581 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/MemberLevelEnum.java @@ -0,0 +1,46 @@ +package org.jeecg.payment.enums; + +/** + */ +public enum MemberLevelEnum { + COMMON_LEVEL_MEMBER(-1, "普通会员卡"), + + PAY_SEQUENCE_BY_RATIO(1, "储值和赠送余额等比例扣减"), + PAY_SEQUENCE_BY_TOP_UP(2, "先储值余额,后赠送余额"), + PAY_SEQUENCE_BY_REWARD(3, "先赠送余额,后储值余额"), + ; + private String name; + private Integer code; + + + MemberLevelEnum(Integer code, String name) { + this.name = name; + this.code = code; + } + + public String getName() { + return name; + } + + public Integer getCode() { + return code; + } + + /** + * 根据统计编号获取对应枚举实例 + * + * @param code + * @return + */ + public static MemberLevelEnum getEnumCode(Integer code) { + if (code == null) { + return PAY_SEQUENCE_BY_RATIO;//默认等比例扣减 + } + for (MemberLevelEnum result : MemberLevelEnum.values()) { + if (result.code.equals(code)) { + return result; + } + } + return PAY_SEQUENCE_BY_RATIO;//默认等比例扣减 + } +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/MemberTransactionTypeEnum.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/MemberTransactionTypeEnum.java new file mode 100644 index 0000000..f75b026 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/MemberTransactionTypeEnum.java @@ -0,0 +1,76 @@ +package org.jeecg.payment.enums; + + +import org.jeecg.payment.vo.StMemberOperationTypeVO; + +import java.util.ArrayList; +import java.util.List; + +/** + * 会员卡交易记录 + */ +public enum MemberTransactionTypeEnum { + EXPENDITURE("消费", 101), + PAY("充值", 102), + BACK_PAY("撤销充值", 103), + BALANCE_ADD("余额手动补录", 201), + BALANCE_SUBTRACT("余额手动扣减", 202), + INTEGRAL_ADD("积分手动补录", 203), + INTEGRAL_SUBTRACT("积分手动扣减", 204), + INTEGRAL_RESET("积分清零", 205), + B_TO_C_REFUND_301("余额退款完成", 301), + B_TO_C_REFUND_302("余额退款中", 302), + ZS_BALANCE_RESET("赠送余额清零", 303) + ; + + private String name; + private Integer code; + + MemberTransactionTypeEnum(String name, Integer code) { + this.name = name; + this.code = code; + } + + public static String getName(Integer code) { + MemberTransactionTypeEnum[] enums = values(); + int length = enums.length; + + for (int i = 0; i < length; ++i) { + MemberTransactionTypeEnum item = enums[i]; + if (item.code.equals(code)) { + return item.name; + } + } + return null; + } + + public static List getList() { + List list = new ArrayList<>(); + MemberTransactionTypeEnum[] enums = values(); + int length = enums.length; + for (int i = 0; i < length; ++i) { + StMemberOperationTypeVO model = new StMemberOperationTypeVO(); + MemberTransactionTypeEnum item = enums[i]; + model.setCode(item.code.toString()); + model.setName(item.name); + list.add(model); + } + return list; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/OrderStatus.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/OrderStatus.java new file mode 100644 index 0000000..72caba2 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/OrderStatus.java @@ -0,0 +1,76 @@ +package org.jeecg.payment.enums; + +/** + * 订单状态枚举 + */ +public enum OrderStatus { + ORDER_STATUS_WAIT_ORDER (1, "ORDER_STATUS_WAIT_ORDER ", "待下单"), + ORDER_STATUS_WAIT_RECEIVING_ORDERS(2, "ORDER_STATUS_WAIT_RECEIVING_ORDERS", "待接单"), + ORDER_STATUS_WAIT_MAKE(3, "ORDER_STATUS_WAIT_MAKE", "待制作"), + ORDER_STATUS_WAIT_CHECK_OUT(4, "ORDER_STATUS_WAIT_CHECK_OUT", "待结账"), + ORDER_STATUS_CHECK_OUT_ING(0, "ORDER_STATUS_WAIT_CHECK_OUT", "结账中"),//收银台需要的细化状态 + ORDER_STATUS_HAS_CHECK_OUT(5, "ORDER_STATUS_HAS_CHECK_OUT", "已完成"), + ORDER_STATUS_CANCEL_ORDER(6, "ORDER_STATUS_CANCEL_ORDER", "已撤单"), + ORDER_STATUS_FJZ_ORDER(7, "ORDER_STATUS_FJZ_ORDER", "反结账"), + ORDER_STATUS_CANCEL_ED(8, "ORDER_STATUS_CANCEL_ED", "后台定时任务关闭"); + private int code; + private String type; + private String desc; + + OrderStatus(int code, String type, String desc) { + this.code = code; + this.type = type; + this.desc = desc; + } + + public static String getDescByCode(int code) { + for (OrderStatus type : OrderStatus.values()) { + if (type.getCode() == code) { + return type.getDesc(); + } + } + return null; + } + + public static String getTypeByCode(int code) { + for (OrderStatus type : OrderStatus.values()) { + if (type.getCode() == code) { + return type.getType(); + } + } + return null; + } + + public static Integer getCode(String desc) { + for (OrderStatus type : OrderStatus.values()) { + if (type.getDesc().equalsIgnoreCase(desc)) { + return type.getCode(); + } + } + return null; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/UserTypeEnum.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/UserTypeEnum.java new file mode 100644 index 0000000..9d1cec5 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/enums/UserTypeEnum.java @@ -0,0 +1,36 @@ +package org.jeecg.payment.enums; + + + // 定义用户类型枚举类 +public enum UserTypeEnum { + OFFICIAL(0, "正式员工"), + UNOFFICIAL(1, "非正式员工"), + FAMILY(2, "家庭用户"), + UNKNOWN_SUBSCRIBER(-1, "未知用户"); + private int code; + private String desc; + + UserTypeEnum(int code, String desc) { + this.code = code; + this.desc = desc; + } + + public int getCode() { + return code; + } + + public String getDesc() { + return desc; + } + + // 根据代码获取对应的枚举实例 + public static UserTypeEnum getByCode(int code) { + for (UserTypeEnum type : UserTypeEnum.values()) { + if (type.getCode() == code) { + return type; + } + } + return UNKNOWN_SUBSCRIBER; + } + } + diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/InOutRecordInfoVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/InOutRecordInfoVo.java new file mode 100644 index 0000000..aec8779 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/InOutRecordInfoVo.java @@ -0,0 +1,28 @@ +package org.jeecg.payment.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.jeecg.common.util.BigDecimalSerialize; + +import java.math.BigDecimal; + +@Data +public class InOutRecordInfoVo { + + private String status; + + @Schema(title ="交易金额") + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal transactionAmount; + + private String payTime; + + private String orderNo; + + private String paymentMethod; + + private String remark; + + private String eaName; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/OrderShow.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/OrderShow.java new file mode 100644 index 0000000..e9f480b --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/OrderShow.java @@ -0,0 +1,60 @@ +package org.jeecg.payment.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +@Data +public class OrderShow { + + @Schema(title ="订单id") + public int orderId; + + + @Schema(title ="订单编号") + public String orderNo; + + @Schema(title ="订单备注") + public String remark; + + @Schema(title ="订单备注") + public String orderName; + + @Schema(title ="下单时间") + public Date createTime; + + + @Schema(title ="状态") + public String status; + + @JsonIgnore + @Schema(title ="eaId") + public Integer eaId; + + @Schema(title ="机构名称") + private String eaName; + + @Schema(title ="订单价格") + private BigDecimal originalPrice; + + /** + * 订单来源 + * 1、平板端 + * 2、收银端 + * 3、自助点餐机 + * 4、微信小程序 + * 5、支付宝小程序 + */ + @Schema(title ="订单来源") + private Integer orderFrom; + + @Schema(title ="订单类型 1:扫码点餐 2预点餐 3.自助餐") + private Integer orderType; + //订单菜品详情 + private List eatFoodInfoVoList; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBackOutPayVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBackOutPayVO.java new file mode 100644 index 0000000..f9dd8b5 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBackOutPayVO.java @@ -0,0 +1,74 @@ +package org.jeecg.payment.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import org.jeecg.common.util.BigDecimalSerialize; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @author Ls. + * @date 2021/6/22. + */ +@Data +@Tag(name= "StClientMemberBackOutVO对象", description = "撤销充值列表") +public class StClientMemberBackOutPayVO { + @Schema(title ="ID") + private String id; + + @Schema(title ="交易流水号") + private String serialNo; + + @Schema(title ="订单号") + private String orderNo; + + @Schema(title ="支付订单号") + private String payOrderNo; + + + @Schema(title ="操作类型:MemberTransactionTypeEnum枚举类") + private Integer status; + + @Schema(title ="操作类型名称") + private String statusName; + + @Schema(title ="结账/退回方式:0卡余额1现金2微信3支付宝4:前台码5充值明细导入") + private Integer paymentMethod; + + @Schema(title ="结账方式:名称") + private String paymentMethodName; + + @Schema(title ="储值余额变动") + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal storedBalance; + + @Schema(title ="赠送余额变动") + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal rewardBalance; + + @Schema(title ="操作门店") + private String eaName; + + @Schema(title ="操作端:0后台1前端") + private Integer updateStatus; + + @Schema(title ="操作人") + private String createOne; + + @Schema(title ="备注:撤销原因") + private String remark; + + @Schema(title ="操作状态:0不可撤销1可撤销") + private Integer operationStatus; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @Schema(title ="操作时间") + private Date createTime; + + + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBackOutTopUpDTO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBackOutTopUpDTO.java new file mode 100644 index 0000000..4f44602 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBackOutTopUpDTO.java @@ -0,0 +1,24 @@ +package org.jeecg.payment.vo; + + + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + 充值列表 + */ +@Data +@Tag(name= "StClientMemberBackOutTopUpDTO对象", description = "撤销充值") +public class StClientMemberBackOutTopUpDTO { + @NotBlank(message = "请输入id") + @Schema(title ="会员余额列表主键id") + private String id; + + @Schema(title ="食堂id") + private String canteenId; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBackOutUserVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBackOutUserVO.java new file mode 100644 index 0000000..086f924 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBackOutUserVO.java @@ -0,0 +1,37 @@ +package org.jeecg.payment.vo; + + + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + +/** + * 充值 + */ +@Data +@Tag(name= "StClientMemberBackOutUserVO对象", description = "撤销充值") +public class StClientMemberBackOutUserVO { + @Schema(title ="用户名称") + private String userName; + + @Schema(title ="手机号") + private String phone; + + @Schema(title ="卡号") + private String cardCode; + + @Schema(title ="工号") + private String workNo; + + @Schema(title ="所属部门") + private String deptName; + +// @Schema(title ="所属门店") + private String eaName; + + @Schema(title ="等级") + private String cardLevelName; + + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBackOutVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBackOutVO.java new file mode 100644 index 0000000..d338fe8 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBackOutVO.java @@ -0,0 +1,22 @@ +package org.jeecg.payment.vo; + + + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + +import java.util.List; + +/** + */ +@Data +@Tag(name= "StClientMemberBackOutVO对象", description = "后台撤销充值列表") +public class StClientMemberBackOutVO { + + @Schema(title ="充值列表") + private List payVO; + + @Schema(title ="用户信息") + private StClientMemberBackOutUserVO userVO; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBackTopUpQueryDto.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBackTopUpQueryDto.java new file mode 100644 index 0000000..8dc8cf1 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBackTopUpQueryDto.java @@ -0,0 +1,44 @@ +package org.jeecg.payment.vo; + + + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.math.BigDecimal; + +/** +充值 + */ +@Data +@Tag(name= "StClientMemberBackOutTopUpDTO对象", description = "撤销充值") +public class StClientMemberBackTopUpQueryDto { + + //线上线下公共参数 + @NotBlank(message = "请输入id") + @Schema(title ="流水表主键id") + private String id; + + @NotBlank(message = "撤销原因不能为空") + @Schema(title ="撤销原因") + private String remark; + + @NotBlank(message = "退回方式不能为空") + @Schema(title ="退回方式:0卡余额1现金2微信3支付宝4:前台码") + private Integer paymentMethod; + + @Schema(title ="食堂id") + private String canteenId; + + //线上 + /** + * 支付宝,微信,银联返回的交易号 + */ + @Schema(title ="商户订单号+订单随机数(order_no + order_tag)") + private String payOrderNo; + + @Schema(title ="标价金额(单位为元)") + private BigDecimal totalFee; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBalanceListVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBalanceListVO.java new file mode 100644 index 0000000..30f46fa --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBalanceListVO.java @@ -0,0 +1,85 @@ +package org.jeecg.payment.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import org.jeecg.common.util.BigDecimalSerialize; + +import java.math.BigDecimal; +import java.util.Date; + +/** +后台充值列表 + */ +@Data +@Tag(name= "StClientMemberBalanceListVO对象", description = "后台充值列表") +public class StClientMemberBalanceListVO { + @Schema(title ="主键id") + private String id; + + @Schema(title ="用户ID") + private String userId; + + @Schema(title ="用户名称") + private String userName; + + @Schema(title ="部门名称") + private String deptName; + + @Schema(title ="工号") + private String workNo; + + @Schema(title ="手机号") + private String phone; + + @Schema(title ="总余额(元)充值金额+补贴赠送金额") + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal topUpBalanceTotal; + + @Schema(title ="累计充值(元)") + private BigDecimal topUpTotal; + + @Schema(title ="补贴金额") + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal rewardBalance; + + @Schema(title ="充值余额(元)") + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal topUpBalance; + + @Schema(title ="积分余额") + private Integer integralBalance; + + @Schema(title ="消费次数") + private Integer consumptionCount; + + @Schema(title ="累计消费(元)") + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal consumptionTotal; + + + /** + * 钱包状态(1-禁用,0-正常) + */ + @Schema(title ="钱包状态(1-禁用,0-正常)") + private Integer isStatus; + + @Schema(title ="充值次数") + private Integer topUpCount; + + + @Schema(title ="最后一次消费时间") + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + private Date lastConsumptionTime; + + @Schema(title ="首充时间") + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + private Date firstTopUpTime; + + + + + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBalanceVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBalanceVO.java new file mode 100644 index 0000000..cce78b8 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberBalanceVO.java @@ -0,0 +1,52 @@ +package org.jeecg.payment.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.jeecg.common.util.BigDecimalSerialize; + +import java.math.BigDecimal; + +/** + * @author Ls. + * @date 2021/6/21. + */ +@Data +public class StClientMemberBalanceVO { + @Schema(title ="主键id") + private Integer id; + + @Schema(title ="卡号") + private String cardCode; + + @Schema(title ="手机号") + private String phone; + + @Schema(title ="卡类型:1后台2收银") + private String memberFrom; + + @Schema(title ="卡等级名称") + private String cardName; + + @Schema(title ="卡等级ID") + private Integer memberLevelId; + + @Schema(title ="储值余额变动") + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal topUpBalance; + + @Schema(title ="积分余额变动") + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal integralBalance; + + @Schema(title ="赠送余额变动") + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal rewardBalance; + + @Schema(title="状态:0禁用1启用") + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal status; + + @Schema(title="店铺名称") + private String eaName; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberLevelDeductRuleVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberLevelDeductRuleVo.java new file mode 100644 index 0000000..1b1f1e6 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberLevelDeductRuleVo.java @@ -0,0 +1,69 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.vo; + + + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import org.jeecg.common.system.base.entity.JeecgEntity; + +import java.io.Serializable; + +/** + * 扣费规则 + */ +@Data +@Tag(name= "StClientMemberLevelDeductRule对象", description = "StClientMemberLevelDeductRule对象") +public class StClientMemberLevelDeductRuleVo extends JeecgEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + + @Schema(title = "规则名称") + private String ruleName; + + + /** + * 启用=1,禁用=0 + */ + @Schema(title ="启用=1,禁用=0") + private Integer isEnable; + + /** + * 扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费) + */ + @Schema(title ="扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费)") + private Integer deductionType; + + /** + * 用户类型(1:全部用户,2:指定用户) + */ + @Schema(title ="用户类型(1:全部用户,0:指定用户)") + private Integer isExternalDine; + + + @Schema(title ="规则") + private StMemberDeductRuleVO stMemberDeductRuleVO; + + /** + * 扣费规则(fixedDeduction:固定扣费,rewardDeduction:赠送扣费,ratioDeduction:比例扣费) + * + */ + @Schema(title = "扣费规则(fixedDeduction:固定扣费,rewardDeduction:赠送扣费,ratioDeduction:比例扣费)") + private String deductionRule; + + /** + * 食堂ID + */ + @Schema(title ="食堂ID") + private String canteenId; + + @Schema(title = "是否使用支持外部人员就餐(0:不支持,1:支持)") + private Integer isExternalDineFlag; + + @Schema(title = "是否支持透支消费(0:不支持,1:支持)") + private Integer isOverdraftDine; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberLevelDemotionTimeVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberLevelDemotionTimeVO.java new file mode 100644 index 0000000..9e03140 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberLevelDemotionTimeVO.java @@ -0,0 +1,58 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.vo; + + + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.jeecg.payment.entity.StClientMemberLevelDemotionTime; + +import java.util.List; + +/** + * 视图实体类 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Tag(name= "StClientMemberLevelDemotionTimeVO对象", description = "StClientMemberLevelDemotionTimeVO对象") +public class StClientMemberLevelDemotionTimeVO extends StClientMemberLevelDemotionTime { + private static final long serialVersionUID = 1L; + + @Schema(title ="新增/修改参数") + private String hourMinuteStr; + + @Schema(title ="当前页") + private Integer pageNo; + @Schema(title ="数量/页") + private Integer pageSize; + + /** + * 启用=1,禁用=0 + */ + @Schema(title ="启用=1,禁用=0") + private Integer isEnable; + + /** + * 等级规则1 + */ + //储值规则 + @Schema(title ="储值规则") + private List storedVOS; + //积分抵现规则 + @Schema(title ="积分抵现规则") + private StMemberLevelIntegralVO integralVO; + //单笔积分抵扣上限规则 + @Schema(title ="单笔积分抵扣上限规则") + private StMemberLevelSingleVO singleVO; + //积分清零规则 + @Schema(title ="积分清零规则") + private StMemberLevelResetVO resetVO; + //积分清零短信提示规则 + @Schema(title ="积分清零短信提示规则") + private StMemberLevelResetMessageVO resetMessageVO; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberSpendDTO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberSpendDTO.java new file mode 100644 index 0000000..18d51e3 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberSpendDTO.java @@ -0,0 +1,37 @@ +package org.jeecg.payment.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author Ls. + * @date 2021/7/13. + */ +@Data +public class StClientMemberSpendDTO { + @Schema(title = "必填:用户id") + private String id; + + @Schema(title = "必填:订单号") + private String orderNo; + + @Schema(title = "消费金额") + private BigDecimal spendMoney; + + @Schema(title = "备注") + private String remark; + + @Schema(title = "已开发票:false未开 true已开") + private Boolean invoice; + + + @Schema(title = "食堂ID") + private String canteenId; + + + //扣费钱包信息 + private StMiniMineBalanceVo miniMineBalanceVo; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberSpendDataVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberSpendDataVO.java new file mode 100644 index 0000000..8ba14a4 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberSpendDataVO.java @@ -0,0 +1,21 @@ +package org.jeecg.payment.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +/** + */ +@Data +public class StClientMemberSpendDataVO { + + @Schema(title ="本次消费金额(固定/固定+补贴模式的 固定金额,按量扣费模式的 实际扣费金额)") + private BigDecimal spendMoney; + + @Schema(title ="补贴金额(固定+补贴模式下)") + private BigDecimal rewardDeduction; + + @Schema(title ="原订单费用总计(一次扣费模式=固定/固定+补贴,按量扣费模式=实际金额)") + private BigDecimal totalDeduction; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberSpendVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberSpendVO.java new file mode 100644 index 0000000..ba18f8d --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberSpendVO.java @@ -0,0 +1,47 @@ +package org.jeecg.payment.vo; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +/** + */ +@Data +public class StClientMemberSpendVO { + @Schema(title ="消费状态") + private Boolean success; + @Schema(title ="返回信息") + private String msg; + @Schema(title ="应补差额(食堂开启透支模式,差价金额)") + private BigDecimal priceSpread; + + @Schema(title ="本次消费金额(固定/固定+补贴模式的 固定金额,按量扣费模式的 实际扣费金额)") + private BigDecimal spendMoney; + + @Schema(title ="补贴金额(固定+补贴模式下)") + private BigDecimal preferentialPrice; + + @Schema(title ="本次消费后余额") + private BigDecimal balance; + + + @Schema(name = "当前用户姓名") + private String realname; + + + @Schema(title ="当前用户工号") + private String workNo; + + @Schema(name = "支付用户姓名") + private String payRealname; + + + @Schema(title ="支付用户工号") + private String payWorkNo; + + + @Schema(title ="补偿订单id") + private String orderId; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberTransactionRecordFeignVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberTransactionRecordFeignVO.java new file mode 100644 index 0000000..61b28a1 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberTransactionRecordFeignVO.java @@ -0,0 +1,147 @@ +package org.jeecg.payment.vo; + +import com.baomidou.mybatisplus.annotation.TableField; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.jeecg.common.system.base.entity.JeecgEntity; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 交易记录表 + */ + +@Data + +public class StClientMemberTransactionRecordFeignVO extends JeecgEntity { + + /** + * 会员id + */ + @TableField(value = "member_id") + @Schema(title ="会员id") + private String memberId; + + /** + * 交易流水号 + */ + @TableField(value = "serial_no") + @Schema(title ="交易流水号") + private String serialNo; + + /** + * 订单号 + */ + @TableField(value = "order_no") + @Schema(title ="订单号") + private String orderNo; + + /** + * 操作类型:1消费2充值 + */ + @TableField(value = "status") + @Schema(title ="操作类型:MemberTransactionTypeEnum枚举类") + private Integer status; + + /** + * 交易金额 + */ + @TableField(value = "transaction_amount") + @Schema(title ="交易金额") + private BigDecimal transactionAmount; + + /** + * 结账方式:0卡余额1自定义记账-前台码2微信3支付宝4前台码 + */ +// @TableField(value = "payment_method") +// @Schema(title ="结账/退回方式:0卡余额1现金2微信3支付宝4前台码") +// private Integer paymentMethod; + + /** + * 储值余额变动 + */ +// @TableField(value = "stored_balance") +// @Schema(title ="储值余额变动") +// private BigDecimal storedBalance; + + /** + * 奖励余额变动 + */ +// @TableField(value = "reward_balance") + @Schema(title ="奖励余额变动") + private BigDecimal rewardBalance; + + /** + * 积分余额变动 + */ +// @TableField(value = "integral_balance") +// @Schema(title ="积分变动") +// private Integer integralBalance; + + @Schema(title ="可用余额:充值余额+奖励") + private BigDecimal availableBalance; + + /** + * 结账时间 + */ +// @TableField(value = "pay_time") + @Schema(title ="结账时间") + private Date payTime; + + /** + * 操作端:0后台1前端 + */ +// @TableField(value = "update_status") +// @Schema(title ="操作端:0后台1前端") +// private Integer updateStatus; + +// @Schema(title ="备注") +// private String remark; + +// @Schema(title ="提成人") +// private String rewardUserNo; + +// @Schema(title ="已开发票:false未开 true已开") +// private boolean isInvoice; + + + + private static final long serialVersionUID = 1L; + + public static final String COL_ID = "id"; + + public static final String COL_MEMBER_ID = "member_id"; + + public static final String COL_SERIAL_NO = "serial_no"; + + public static final String COL_ORDER_NO = "order_no"; + + public static final String COL_STATUS = "status"; + + public static final String COL_TRANSACTION_AMOUNT = "transaction_amount"; + + public static final String COL_PAYMENT_METHOD = "payment_method"; + + public static final String COL_STORED_BALANCE = "stored_balance"; + + public static final String COL_REWARD_BALANCE = "reward_balance"; + + public static final String COL_INTEGRAL_BALANCE = "integral_balance"; + + public static final String COL_PAY_TIME = "pay_time"; + + public static final String COL_UPDATE_STATUS = "update_status"; + + public static final String COL_CREATE_USER_NO = "create_user_no"; + + public static final String COL_CREATE_TIME = "create_time"; + + public static final String COL_UPDATE_USER_NO = "update_user_no"; + + public static final String COL_UPDATE_TIME = "update_time"; + + public static final String COL_IS_DEL = "is_del"; + + public static final String COL_EA_ID = "canteen_id"; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberTransactionRecordVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberTransactionRecordVO.java new file mode 100644 index 0000000..64f5fa7 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StClientMemberTransactionRecordVO.java @@ -0,0 +1,112 @@ +package org.jeecg.payment.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import org.jeecg.common.util.BigDecimalSerialize; + +import java.math.BigDecimal; +import java.util.Date; + +/** +流水明细 + */ +@Tag(name= "StClientMemberTransactionRecordVO对象", description = "流水明细") +@Data +public class StClientMemberTransactionRecordVO { + @Schema(title ="id") + private String id; + + @Schema(title ="交易流水号") + private String serialNo; + + @Schema(title ="订单号") + private String orderNo; + + @Schema(title ="操作类型") + private Integer status; + + @Schema(title ="操作类型名称") + private String statusName; + + @Schema(title ="交易金额")//余额退款时表示:实际退款金额 + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal transactionAmount; + + @Schema(title ="结账方式:0卡余额1现金2微信3支付宝4前台码,5充值明细导入") + private Integer paymentMethod; + + @Schema(title ="结账方式:名称") + private String paymentMethodName; + + @Schema(title ="储值余额")//余额退款时表示:充值余额 + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal storedBalance; + + @Schema(title ="奖励余额")//余额退款时表示:赠送余额 + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal rewardBalance; + + @Schema(title ="可用余额:充值余额+奖励")//余额退款时表示:累计充值总额=充值+赠送 + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal availableBalance; + + @Schema(title ="消费金额")//余额退款时表示:实际消费金额 + @JsonSerialize(using = BigDecimalSerialize.class) + private BigDecimal monetary; + + @Schema(title ="积分余额") + private Integer integralBalance; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @Schema(title ="结账时间") + private Date payTime; + + @Schema(title ="操作端:0后台1前端") + private Integer updateStatus; + + @Schema(title ="操作端名称:0后台1前端") + private String updateStatusName; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @Schema(title ="交易时间") + private Date createTime; + + @Schema(title = "支付会员姓名") + private String payUserName; + + @Schema(title ="会员姓名") + private String userName; + + @Schema(title ="会员电话") + private String phone; + + @Schema(title ="工号") + private String workNo; + + @Schema(title ="操作人") + private String createOne; + + @Schema(title ="提成人") + private String rewardUserNo; + + @Schema(title ="卡号") + private String cardCode; + + @Schema(title ="扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费,4:自定义金额)") + private Integer businessModel; + + @Schema(title ="扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费,4:自定义金额)") + private String businessModelName; + + @Schema(title ="食堂名称") + private String restName; + + @Schema(title ="餐次名称") + private String dinnerType; + + @Schema(title ="部门名称") + private String deptName; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StEatFoodInfoVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StEatFoodInfoVo.java new file mode 100644 index 0000000..7f276f9 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StEatFoodInfoVo.java @@ -0,0 +1,34 @@ +package org.jeecg.payment.vo; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +public class StEatFoodInfoVo { + + @Schema(title ="菜品id") + private String foodId; + @Schema(title ="菜品名称") + private String foodName; + @Schema(title ="规格名称") + private String specName; + @Schema(title ="食用数量") + private String num; + @Schema(title ="类型 0普通 1赠菜 2退菜 3加菜") + private String type; + @Schema(title ="菜品实际支付价格") + private String price; + @Schema(title ="菜品图片") + private String relativeUrl; + //订单id + private Integer orderId; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberDeductRuleVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberDeductRuleVO.java new file mode 100644 index 0000000..bc80c32 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberDeductRuleVO.java @@ -0,0 +1,36 @@ +package org.jeecg.payment.vo; + + + + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + + +/** + * 扣费类规则 + */ +@Data +@Tag(name= "StMemberDeductRuleVO对象",description = "扣费类规则") +public class StMemberDeductRuleVO { + private static final long serialVersionUID = 1L; + + /** + * 早餐 + */ + @Schema(title ="早餐") + private StMemberDeductUserTypeRuleVO breakfast; + + /** + * 午餐 + */ + @Schema(title ="午餐") + private StMemberDeductUserTypeRuleVO lunch; + + /** + * 晚餐 + */ + @Schema(title ="晚餐") + private StMemberDeductUserTypeRuleVO dinner; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberDeductTypeOfAuditorRuleVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberDeductTypeOfAuditorRuleVO.java new file mode 100644 index 0000000..302ab23 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberDeductTypeOfAuditorRuleVO.java @@ -0,0 +1,47 @@ +package org.jeecg.payment.vo; + + + + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + + +/** + * 扣除类规则 + */ +@Data +@Tag(name= "StMemberDeductTypeOfAuditorRuleVO对象",description = "扣除类规则") +public class StMemberDeductTypeOfAuditorRuleVO { + private static final long serialVersionUID = 1L; + + /** + * 总扣除(元) + */ + @Schema(title ="总扣除(元)") + private Float totalDeduction; + + /** + * 固定扣除(元) + */ + @Schema(title ="固定扣除(元)") + private Float fixedDeduction; + + /** + * 补贴扣减(元) + */ + @Schema(title ="补贴扣减(元)") + private Float rewardDeduction; + + /** + * 比例扣减(%) + */ + @Schema(title ="比例扣减(%)") + private Float ratioDeduction; + + /** + * 1:补贴扣减,2:比例扣减 + */ + private int type; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberDeductUserTypeRuleVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberDeductUserTypeRuleVO.java new file mode 100644 index 0000000..c719edc --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberDeductUserTypeRuleVO.java @@ -0,0 +1,31 @@ +package org.jeecg.payment.vo; + + + + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + + +/** + * 扣费人员类型 + */ +@Data +@Tag(name= "StMemberDeductBreakfastRuleVO对象",description = "扣费人员类型") +public class StMemberDeductUserTypeRuleVO { + private static final long serialVersionUID = 1L; + + /** + * 内部人员 + */ + @Schema(title ="内部人员") + private StMemberDeductTypeOfAuditorRuleVO inner; + + /** + * 外部人员 + */ + @Schema(title ="外部人员") + private StMemberDeductTypeOfAuditorRuleVO external; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberLevelIntegralVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberLevelIntegralVO.java new file mode 100644 index 0000000..030ba7e --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberLevelIntegralVO.java @@ -0,0 +1,36 @@ +package org.jeecg.payment.vo; + + + + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + +/** + * 积分抵现规则类 + */ +@Data +@Tag(name= "StMemberLevelRuleIntegralVO对象",description = "积分抵现规则") +public class StMemberLevelIntegralVO { + private static final long serialVersionUID = 1L; + + + /** + * 是否打开 + */ + @Schema(title ="是否开启:1是/0否") + private Integer status; + + /** + * 抵现积分 + */ + @Schema(title ="抵现积分") + private Integer integralCount; + + /** + * 抵现金额 + */ + @Schema(title ="抵现金额") + private Float deductionAmount; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberLevelResetMessageVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberLevelResetMessageVO.java new file mode 100644 index 0000000..6633a1a --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberLevelResetMessageVO.java @@ -0,0 +1,31 @@ +package org.jeecg.payment.vo; + + + + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + +/** + * 积分清零短信提示 + */ +@Data +@Tag(name= "StMemberLevelRuleResetMessageVO对象",description = "积分清零短信提示规则") +public class StMemberLevelResetMessageVO { + + private static final long serialVersionUID = 1L; + + + /** + * 是否打开 + */ + @Schema(title ="是否开启:1是/0否") + private Integer status; + + /** + * 清零前多少天提示 + */ + @Schema(title ="清零前多少天提示") + private Integer resetBeforeDay; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberLevelResetVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberLevelResetVO.java new file mode 100644 index 0000000..74ba8aa --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberLevelResetVO.java @@ -0,0 +1,37 @@ +package org.jeecg.payment.vo; + + + + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + + +/** + * 积分清零类 + */ +@Data +@Tag(name= "StMemberLevelRuleResetVO对象",description = "积分清零规则") +public class StMemberLevelResetVO { + private static final long serialVersionUID = 1L; + + + /** + * 是否打开 + */ + @Schema(title ="是否开启:1是/0否") + private Integer status; + + /** + * 关闭月份 + */ + @Schema(title ="关闭月份") + private Integer iMonth; + + /** + * 关闭天 + */ + @Schema(title ="关闭天") + private Integer iDay; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberLevelSingleVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberLevelSingleVO.java new file mode 100644 index 0000000..a709449 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberLevelSingleVO.java @@ -0,0 +1,37 @@ +package org.jeecg.payment.vo; + + + + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + +/** + * 单笔订单积分抵现上限规则类 + */ +@Data +@Tag(name= "StMemberLevelRuleSingleVO对象",description = "单笔订单积分抵现上限规则") +public class StMemberLevelSingleVO { + + private static final long serialVersionUID = 1L; + + + /** + * 是否打开 + */ + @Schema(title ="是否开启:1是/0否") + private Integer status; + + /** + * 积分抵扣 or 按比例抵扣 + */ + @Schema(title ="积分抵扣上限1/抵扣上限比例为应收2") + private Integer iPattern; + + /** + * 积分抵扣个数/抵扣比例(%) + */ + @Schema(title ="积分抵扣个数/抵扣比例(%)") + private Float iUpperOrScale; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberLevelStoredVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberLevelStoredVO.java new file mode 100644 index 0000000..97e271b --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberLevelStoredVO.java @@ -0,0 +1,38 @@ +package org.jeecg.payment.vo; + + + + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + + +/** + * 储值规则类 + */ +@Data +@Tag(name= "StMemberLevelRuleStoredVO对象", description = "储值规则") +public class StMemberLevelStoredVO { + private static final long serialVersionUID = 1L; + + /** + * 充值金额 + */ + @Schema(title ="充值金额") + private Float rechargePresenter; + + /** + * 赠送金额 + */ + @Schema(title ="赠送金额/比例") + private Float rewardAmount; + + /** + * 赠送积分 + */ + @Schema(title ="赠送积分") + private Integer rewardIntegral; + + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberOperationTypeVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberOperationTypeVO.java new file mode 100644 index 0000000..b191a2d --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMemberOperationTypeVO.java @@ -0,0 +1,11 @@ +package org.jeecg.payment.vo; + +import lombok.Data; + +/** + */ +@Data +public class StMemberOperationTypeVO { + private String code; + private String name; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMiniMineBalanceVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMiniMineBalanceVo.java new file mode 100644 index 0000000..42576f6 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMiniMineBalanceVo.java @@ -0,0 +1,82 @@ +package org.jeecg.payment.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + + +@Data +public class StMiniMineBalanceVo { + + //=================主用户===== + //钱包主键ID + private String id; + //余额 + private String balance; + //积分 + private Integer integralBalance; + //用户类型:内部用户(正式员工)=0,外部用户(非正式员工+家庭用户)=1 + private Integer userInnerType; + //会员等级id + private String levelId; + //余额退款状态:0=无退款审核中状态,1:=退款审核中 + private String refundStatus; + //储值规则 + private List storedVOS; + /** + * 用户状态 1正常 2作废 + */ + private Integer userStatus; + //支付用户会员id + private String memberId; + + //本次消费金额 + private BigDecimal amountThis; + + @Schema(name = "支付用户姓名") + private String payRealname; + + + @Schema(title ="支付用户工号") + private String payWorkNo; + + @Schema(title ="二次提醒") + private Boolean secondReminder; + + //支付主用户删除状态 + private Integer payDelFlag; + + /** + * 卡号 + */ + private String cardCode; + + /** + * 卡号状态(1:正常 , 2:挂失) + */ + private Integer cardCodeStatus; + + //================当前用户================= + + @Schema(title = "当前用户ID") + private String userId; + + //用户状态 用户状态 1正常 2作废 + private Integer status; + + //用户删除状态 + private Integer delFlag; + + @Schema(name = "当前用户姓名") + private String realname; + + + @Schema(title ="当前用户工号") + private String workNo; + + //用户类型(0:正式员工, 1:非正式员工,2:家庭用户) + private Integer userType; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMinisIntegralBalanceVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMinisIntegralBalanceVo.java new file mode 100644 index 0000000..766a6d6 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StMinisIntegralBalanceVo.java @@ -0,0 +1,13 @@ +package org.jeecg.payment.vo; + +import lombok.Data; + + +@Data +public class StMinisIntegralBalanceVo { + + //余额 + private String balance; + //积分 + private String integral; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StRecordListInfo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StRecordListInfo.java new file mode 100644 index 0000000..838fbd9 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StRecordListInfo.java @@ -0,0 +1,31 @@ +package org.jeecg.payment.vo; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class StRecordListInfo { + + @Schema(title ="状态名称") + private String name; + + private Integer status; + + @Schema(title ="订单号") + private String orderNo; + + @Schema(title ="交易金额") + private BigDecimal transactionAmount; + + @Schema(title ="赠送金额") + private BigDecimal rewardBalance; + + @Schema(title ="可用余额") + private BigDecimal availableBalance; + + @Schema(title ="结账时间") + private String payTime; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StRecordListVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StRecordListVo.java new file mode 100644 index 0000000..73b6227 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StRecordListVo.java @@ -0,0 +1,16 @@ +package org.jeecg.payment.vo; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +public class StRecordListVo { + + @Schema(title ="年月") + private String date; + @Schema(title ="记录列表") + private List recordListInfoList; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StRfidVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StRfidVo.java new file mode 100644 index 0000000..733b7a5 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StRfidVo.java @@ -0,0 +1,37 @@ +package org.jeecg.payment.vo; + +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import java.math.BigDecimal; + +/** + * @author lianlonggang + * @date 2022/4/19 11:48 + * @throws + * @since + */ +@Data +public class StRfidVo { +// @NotEmpty(message = "用户ID不能为空") + private String userId; + @NotEmpty(message = "设备编码不能为空") + private String deviceCode; + @NotEmpty(message = "食堂ID不能为空") + private String restId; + /** + * 卡号 + */ + private String cardCode; + /** + * 闸机记录的支付唯一id(用于判断防止重复支付的标记) + */ + private String payId; + + //===离线扣费场景参数 + //消费时间(时间戳) + private String dateTime; + + //本次扣费金额(后端根据规则计算) + private BigDecimal spendMoney; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StRfidoffLineVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StRfidoffLineVo.java new file mode 100644 index 0000000..ffb1764 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StRfidoffLineVo.java @@ -0,0 +1,18 @@ +package org.jeecg.payment.vo; + +import lombok.Data; + +import java.util.List; + +/** + * @author lianlonggang + * @date 2022/4/19 11:48 + * @throws + * @since + */ +@Data +public class StRfidoffLineVo { + private String deviceCode; + private String restId; + List voList; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StatisticsTheAmountSpentVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StatisticsTheAmountSpentVo.java new file mode 100644 index 0000000..4e3f262 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/StatisticsTheAmountSpentVo.java @@ -0,0 +1,66 @@ +package org.jeecg.payment.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author he + */ +@Data +public class StatisticsTheAmountSpentVo { + /** 食堂id */ + private String canteenId; + /** 食堂名称 */ + private String restName; + /** 消费人次 || 消费次数 */ + private BigDecimal totalCount; + /** 消费人数 || 消费天数*/ + private BigDecimal totalNum; + /** 消费总额 (用户实付金额)*/ + private BigDecimal customerPrice; + /** 纠错次数 */ + private BigDecimal correctionCount; + /** 纠错合计 */ + private BigDecimal correctionValue; + /** 早餐 消费金额 */ + private BigDecimal breakfastAmount; + /** 早餐 消费次数 */ + private BigDecimal breakfastPerson; + /** 午餐 消费金额 */ + private BigDecimal lunchAmount; + /** 午餐 消费次数 */ + private BigDecimal lunchPerson; + /** 晚餐 消费金额 */ + private BigDecimal dinnerAmount; + /** 晚餐 消费次数 */ + private BigDecimal dinnerPerson; + /** 其他 消费金额 */ + private BigDecimal otherAmount; + /** 其他 消费次数 */ + private BigDecimal otherPerson; + /** 结算金额(订单金额) */ + private BigDecimal orderPrice; + /** 日期 */ + private String eatDay; + /** + * 姓名 + */ + private String realname; + /** + * 支付用户姓名 + */ + private String payRealname; + /** + * 部门 + */ + private String departName; + /** + * 用户id + */ + private String memberId; + /** + * 工号 + */ + private String workNo; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/SwBbzxOrderInfoVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/SwBbzxOrderInfoVO.java new file mode 100644 index 0000000..c4f16c5 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/payment/vo/SwBbzxOrderInfoVO.java @@ -0,0 +1,58 @@ +package org.jeecg.payment.vo;///** +// * use shuwei code generator created +// */ +//package org.jeecg.modules.payment.vo; +// +// +// +//import lombok.Data; +//import lombok.EqualsAndHashCode; +//import org.jeecg.payment.entity.SwBbzxOrderInfo; +// +//import java.util.List; +// +///** +// * 报表中心-店内订单视图实体类 +// */ +//@Data +//@EqualsAndHashCode(callSuper = true) +//@Tag(name= "SwBbzxOrderInfoVO对象", description = "报表中心-店内订单") +//public class SwBbzxOrderInfoVO extends SwBbzxOrderInfo { +// private static final long serialVersionUID = 1L; +// +// @Schema(title ="门店名称") +// private String eaName; +// +// @Schema(title ="会员名称") +// private String memberName; +// +// /** +// 订单来源(1平板端 、2收银端、3自助点餐机、4小程序、5自助餐) +// */ +// @Schema(title ="订单来源(1平板端 、2收银端、3自助点餐机、4小程序、5自助餐)") +// private String orderFromName; +// +// @Schema(title ="下单人") +// private String placeOrderUser; +// +// //预订信息 +// private SwBbzxReadyInfoVO readyInfoVO; +// +// //食物对象 +// private List swBbzxOrderInfoFoodList; +// +// //支付对象 +// private List swBbzxOrderInfoPayList; +// +// //日志对象 +// private List swBbzxOrderInfoLogList; +// +// //折扣对象 +// private List swBbzxOrderInfoFoodDiscounts; +// +// //反结账对象 +// private List swBbzxOrderInfoAntis; +// +//// private List boxInfoList; +// +//} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/entity/SlBaseDinnerTime.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/entity/SlBaseDinnerTime.java new file mode 100644 index 0000000..f2b2fa7 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/entity/SlBaseDinnerTime.java @@ -0,0 +1,80 @@ +package org.jeecg.restaurant.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecg.common.aspect.annotation.Dict; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; + +/** + * @Description: 食堂餐次定义 + * @Author: jeecg-boot + * @Date: 2022-03-17 + * @Version: V1.0 + */ +@Data +@TableName("st_base_dinner_time") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Tag(name="st_base_dinner_time对象", description="食堂餐次定义") +public class SlBaseDinnerTime implements Serializable { + private static final long serialVersionUID = 1L; + + /**主键*/ + @TableId(type = IdType.AUTO) + @Schema(title = "主键") + private java.lang.Integer id; + /**创建人*/ + @Schema(title = "创建人") + private java.lang.String createBy; + /**创建日期*/ + @Schema(title = "创建日期") + @Excel(name = "创建日期", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private java.util.Date createTime; + /**更新人*/ + @Schema(title = "更新人") + private java.lang.String updateBy; + /**更新日期*/ + @Schema(title = "更新日期") + @Excel(name = "创建日期", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private java.util.Date updateTime; + /**所属部门*/ + @Schema(title = "所属部门") + private java.lang.String sysOrgCode; + /**食堂id*/ + @Excel(name = "食堂id", width = 15, dictTable = "st_base_restaurant", dicText = "rest_name", dicCode = "id") + @Dict(dictTable = "st_base_restaurant", dicText = "rest_name", dicCode = "id") + @Schema(title = "食堂id") + private java.lang.String restId; + /**餐次定义*/ + @Excel(name = "餐次定义", width = 15) + @Schema(title = "餐次定义") + private java.lang.String dinnerType; + /**起始小时(含)*/ + @Excel(name = "起始小时(含)", width = 15) + @Schema(title = "起始小时(含)") + private java.lang.String startTime; + /**结束小时(不含)*/ + @Excel(name = "结束小时(不含)", width = 15) + @Schema(title = "结束小时(不含)") + private java.lang.String endTime; + + @Schema(title = "同步标记(1:已同步,0:未同步)") + private Integer isSync; + + @Schema(title = "删除状态(0-正常,1-已删除)") + private Integer delFlag; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/entity/SlBaseRestaurant.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/entity/SlBaseRestaurant.java new file mode 100644 index 0000000..a07dda4 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/entity/SlBaseRestaurant.java @@ -0,0 +1,128 @@ +package org.jeecg.restaurant.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * @Description: 食堂基本信息 + * @Author: jeecg-boot + * @Date: 2022-03-16 + * @Version: V1.0 + */ +@Data +@TableName("st_base_restaurant") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Tag(name="st_base_restaurant对象", description="食堂基本信息") +public class SlBaseRestaurant implements Serializable { + private static final long serialVersionUID = 1L; + + /**主键*/ + @TableId(type = IdType.ASSIGN_ID) + @Schema(title = "主键") + private String id; + /**创建人*/ + @Schema(title = "创建人") + private String createBy; + /**创建日期*/ + @Schema(title = "创建日期") + @Excel(name = "创建日期", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + /**更新人*/ + @Schema(title = "更新人") + private String updateBy; + /**更新日期*/ + @Schema(title = "更新日期") + @Excel(name = "创建日期", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + /**所属部门*/ + @Schema(title = "所属部门") + private String sysOrgCode; + /**食堂名称*/ + @Excel(name = "食堂名称", width = 15) + @Schema(title = "食堂名称") + private String restName; + /**食堂编号*/ + @Excel(name = "食堂编号", width = 15) + @Schema(title = "食堂编号") + private String restNo; + /**食堂所属单位*/ + @Excel(name = "食堂所属单位", width = 15) + @Schema(title = "食堂所属单位") + private String restDepart; + /**食堂地址*/ + @Excel(name = "食堂地址", width = 15) + @Schema(title = "食堂地址") + private String restAddr; + /**食堂联系人*/ + @Excel(name = "食堂联系人", width = 15) + @Schema(title = "食堂联系人") + private String restContecter; + /**食堂联系电话*/ + @Excel(name = "食堂联系电话", width = 15) + @Schema(title = "食堂联系电话") + private String restPhone; + /**备注信息*/ + @Excel(name = "备注信息", width = 15) + @Schema(title = "备注信息") + private String restRemark; + + @Schema(title = "同步标记(1:已同步,0:未同步)") + private Integer isSync; + + @Schema(title = "删除状态(0-正常,1-已删除)") + private Integer delFlag; + + @Schema(title = "是否使用三减终端(0:不使用,1:使用)") + private Boolean isThreeMinus; + + @Schema(title = "是否使用支持外部人员就餐(0:不支持,1:支持)") + private Integer isExternalDine; + + @Schema(title = "是否支持透支消费(0:不支持,1:支持)") + private Integer isOverdraftDine; + + @Schema(title = "是否开启使用前端结算金额(0:不启用,1:启用)") + private Integer isHeadendSettlement; + + @Schema(title = "前端结算价格浮动元(当开启使用前端结算金额isHeadendSettlement=1时:后端价格和前端价格上下相差不超过此金额,优先采用前端价格,否则采取后台价格)") + private BigDecimal headendSettlementFloat; + + /** + * 食堂地址 纬度 + */ + @Schema(title = "食堂地址 纬度") + private String addressLatitude; + /** + * 食堂地址 经度 + */ + @Schema(title = "食堂地址 经度") + private String addressLongitude; + /** + *健康管理员 + */ + @Schema(title = "健康管理员") + private String healthAdminName; + /** + *健康管理员电话 + */ + @Schema(title = "健康管理员电话") + private String healthPhone; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/RestaurantBusinessEatTimeVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/RestaurantBusinessEatTimeVo.java new file mode 100644 index 0000000..eb3fc01 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/RestaurantBusinessEatTimeVo.java @@ -0,0 +1,21 @@ +package org.jeecg.restaurant.vo; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * 当日经营-各餐统计数据 + */ +@Data +public class RestaurantBusinessEatTimeVo { + + @Schema(title = "餐次名称") + private String eatTimeName; + + @Schema(title = "餐次各项数据集合") + private List businessSumVoList; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/RestaurantBusinessSumVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/RestaurantBusinessSumVo.java new file mode 100644 index 0000000..2d52740 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/RestaurantBusinessSumVo.java @@ -0,0 +1,22 @@ +package org.jeecg.restaurant.vo; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 当日经营-数据汇总 + */ +@Data +public class RestaurantBusinessSumVo { + + @Schema(title = "数据汇总指标") + private String valueName; + + @Schema(title = "数据汇总指标单位") + private String valueUnit; + + @Schema(title = "数据汇总指值") + private Double value; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/SIBaseRestaurantSumTotalVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/SIBaseRestaurantSumTotalVo.java new file mode 100644 index 0000000..7a3c1da --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/SIBaseRestaurantSumTotalVo.java @@ -0,0 +1,16 @@ +package org.jeecg.restaurant.vo; + +import lombok.Data; + +import java.util.List; + +@Data +public class SIBaseRestaurantSumTotalVo { + + //总人数 + private String totalNum; + //总制作量 + private String initialTotal; + //单餐 + private List list; + } diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/SlBaseDinnerTimeVO.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/SlBaseDinnerTimeVO.java new file mode 100644 index 0000000..301c255 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/SlBaseDinnerTimeVO.java @@ -0,0 +1,22 @@ +package org.jeecg.restaurant.vo; + +import lombok.Data; +import org.jeecg.restaurant.entity.SlBaseDinnerTime; + +import java.io.Serializable; +import java.util.List; + +/** + * @Description: 食堂餐次定义 + * @Author: jeecg-boot + * @Date: 2022-03-17 + * @Version: V1.0 + */ +@Data +public class SlBaseDinnerTimeVO implements Serializable { + + private List dineTimes; + + private boolean dayShow; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/SlBaseRestaurantBusinessVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/SlBaseRestaurantBusinessVo.java new file mode 100644 index 0000000..c971cb7 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/SlBaseRestaurantBusinessVo.java @@ -0,0 +1,29 @@ +package org.jeecg.restaurant.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +@Data +public class SlBaseRestaurantBusinessVo { + + @Schema(title = "食堂id") + private String restaurantId; + + /**开始日期*/ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + @Schema(title = "开始日期") + private Date startTime; + + /**结束日期*/ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + @Schema(title = "结束日期") + private Date endTime; + + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/SlBaseRestaurantVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/SlBaseRestaurantVo.java new file mode 100644 index 0000000..9bd7186 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/SlBaseRestaurantVo.java @@ -0,0 +1,30 @@ +package org.jeecg.restaurant.vo; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.jeecg.restaurant.entity.SlBaseDinnerTime; +import org.jeecg.restaurant.entity.SlBaseRestaurant; + +import java.util.List; + +@Data +public class SlBaseRestaurantVo extends SlBaseRestaurant { + + @Schema(title = "登陆账号") + private String username; + + @Schema(title = "密码") + private String password; + + @Schema(title = "角色组") + private String roleId; + + @Schema(title = "权限名称") + private String restDepartName; + + @Schema(title = "餐次定义数组对象") + private List slBaseDinnerTimeList; + + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/StRestaurantFeedbackAddVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/StRestaurantFeedbackAddVo.java new file mode 100644 index 0000000..85ca2e9 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/StRestaurantFeedbackAddVo.java @@ -0,0 +1,45 @@ +package org.jeecg.restaurant.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecgframework.poi.excel.annotation.Excel; + +import java.io.Serializable; +import java.util.List; + +/** + * @Description: 食堂反馈表 + * @Author: jeecg-boot + * @Date: 2022-07-21 + * @Version: V1.0 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Tag(name="st_restaurant_feedback对象", description="食堂反馈表") +public class StRestaurantFeedbackAddVo implements Serializable { + private static final long serialVersionUID = 1L; + + /**食堂id*/ + @Excel(name = "食堂id", width = 15) + @Schema(title = "食堂id") + private String restaurantId; + /**反馈内容*/ + @Excel(name = "反馈内容", width = 15) + @Schema(title = "反馈内容") + private String feedbackContent; + /**用户id*/ + @Excel(name = "用户id", width = 15) + @Schema(title = "用户id") + private String userId; + /**反馈类型*/ + @Excel(name = "反馈类型", width = 15) + @Schema(title = "反馈类型") + private String feedbackType; + @Schema(title = "反馈图片集合") + private List imgList; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/StRestaurantFeedbackReplyVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/StRestaurantFeedbackReplyVo.java new file mode 100644 index 0000000..723dc0f --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/StRestaurantFeedbackReplyVo.java @@ -0,0 +1,82 @@ +package org.jeecg.restaurant.vo; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecgframework.poi.excel.annotation.Excel; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * @Description: 食堂反馈回复记录表VO + * @Author: jeecg-boot + * @Date: 2022-07-21 + * @Version: V1.0 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Tag(name="st_restaurant_feedback_reply对象VO", description="食堂反馈回复记录表VO") +public class StRestaurantFeedbackReplyVo implements Serializable { + private static final long serialVersionUID = 1L; + + /**主键*/ + @TableId(type = IdType.ASSIGN_ID) + @Schema(title = "主键") + private String id; + /**创建人*/ + @Schema(title = "创建人") + private String createBy; + /**创建日期*/ + @Schema(title = "创建日期") + private Date createTime; + /**更新人*/ + @Schema(title = "更新人") + private String updateBy; + /**更新日期*/ + @Schema(title = "更新日期") + private Date updateTime; + /**所属部门*/ + @Schema(title = "所属部门") + private String sysOrgCode; + /**反馈id*/ + @Excel(name = "反馈id", width = 15) + @Schema(title = "反馈id") + private String feedbackId; + /**回复内容*/ + @Excel(name = "回复内容", width = 15) + @Schema(title = "回复内容") + private String replyContent; + /**回复图片*/ + @Excel(name = "回复图片", width = 15) + @Schema(title = "回复图片") + private String replyImg; + /**用户id*/ + @Excel(name = "用户id", width = 15) + @Schema(title = "用户id") + private String userId; + /**用户名称*/ + @Excel(name = "用户名称", width = 15) + @Schema(title = "用户名称") + private String userName; + /**用户类型(0:用户,1:管理员)*/ + @Excel(name = "用户类型(0:用户,1:管理员)", width = 15) + @Schema(title = "用户类型(0:用户,1:管理员)") + private Integer userType; + /**用户名*/ + @Excel(name = "头像", width = 15) + @Schema(title = "头像") + private String avatar; + + @Schema(title = "反馈图片") + private List imgList; + + @Schema(title = "反馈缩略图片") + private List imgListSmall; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/StRestaurantFeedbackVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/StRestaurantFeedbackVo.java new file mode 100644 index 0000000..5fd6bb3 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/StRestaurantFeedbackVo.java @@ -0,0 +1,102 @@ +package org.jeecg.restaurant.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * @Description: 食堂反馈表 + * @Author: jeecg-boot + * @Date: 2022-07-21 + * @Version: V1.0 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Tag(name="st_restaurant_feedback对象", description="食堂反馈表") +public class StRestaurantFeedbackVo implements Serializable { + private static final long serialVersionUID = 1L; + + /**主键*/ + @Schema(title = "主键") + private String id; + /**创建人*/ + @Schema(title = "创建人") + private String createBy; + /**创建日期*/ + @Schema(title = "创建日期") + private Date createTime; + /**更新人*/ + @Schema(title = "更新人") + private String updateBy; + /**更新日期*/ + @Schema(title = "更新日期") + private Date updateTime; + /**所属部门*/ + @Schema(title = "所属部门") + private String sysOrgCode; + /**食堂id*/ + @Excel(name = "食堂id", width = 15) + @Schema(title = "食堂id") + private String restaurantId; + /**食堂id*/ + @Excel(name = "食堂名称", width = 15) + @Schema(title = "食堂名称") + private String restaurantName; + /**反馈内容*/ + @Excel(name = "反馈内容", width = 15) + @Schema(title = "反馈内容") + private String feedbackContent; + @Schema(title = "反馈内容-缩略文字") + private String feedbackContentBreviary; + /**用户id*/ + @Excel(name = "用户id", width = 15) + @Schema(title = "用户id") + private String userId; + + /**用户名称*/ + @Excel(name = "用户名称", width = 15) + @Schema(title = "用户名称") + private String userName; + /**反馈类型*/ + @Excel(name = "反馈类型", width = 15) + @Schema(title = "反馈类型") + private String feedbackType; + + /**反馈类型名称*/ + @Excel(name = "反馈类型名称", width = 15) + @Schema(title = "反馈类型名称") + private String feedbackTypeName; + + /**回复时间*/ + @Schema(title = "回复时间") + private Date createTimeReply; + + /**开始日期*/ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + @Schema(title = "开始日期") + private Date startTime; + + /**结束日期*/ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + @Schema(title = "结束日期") + private Date endTime; + + @Schema(title = "反馈图片集合") + private List imgList; + + /**是否回复:1 已回复 0:未回复*/ + @Schema(title = "是否回复:1 已回复 0:未回复") + private Integer isReply; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/StUserFoodInfoVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/StUserFoodInfoVo.java new file mode 100644 index 0000000..dd37437 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/StUserFoodInfoVo.java @@ -0,0 +1,26 @@ +package org.jeecg.restaurant.vo; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + + +@Data +public class StUserFoodInfoVo { + + @Schema(title = "制作量") + private String initial; + + @Schema(title = "餐品余量") + private String surplus; + + @Schema(title = "收残量") + private String remnantTotal; + + @Schema(title = "餐次名称") + private String dinnerType; + + @Schema(title = "就餐人数") + private String num; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/StatisticsBusinessVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/StatisticsBusinessVo.java new file mode 100644 index 0000000..4413f7d --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/restaurant/vo/StatisticsBusinessVo.java @@ -0,0 +1,27 @@ +package org.jeecg.restaurant.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author he + */ +@Data +public class StatisticsBusinessVo { + /** 食堂id */ + private String canteenId; + /** 日期 */ + private String payTime; + /** 食堂名称 */ + private String restName; + /** 消费次数 */ + private BigDecimal totalCount; + /** 消费合计*/ + private BigDecimal customerPrice; + /** 充值合计 */ + private BigDecimal rechargeAmount; + /** 退款合计*/ + private BigDecimal totalRefund; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/statistics/vo/QueueViewMBeanVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/statistics/vo/QueueViewMBeanVo.java new file mode 100644 index 0000000..74df5ab --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/statistics/vo/QueueViewMBeanVo.java @@ -0,0 +1,23 @@ +package org.jeecg.statistics.vo; + +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + +@Data +@Tag(name="ActiveMq队列信息", description="ActiveMq队列信息") +public class QueueViewMBeanVo { + + + //System.out.println("队列的名称:" + queueBean.getName()); + private String name; + //System.out.println("队列中剩余的消息数:" + queueBean.getQueueSize()); + private long queueSize; + //System.out.println("消费者数:" + queueBean.getConsumerCount()); + private long consumerCount; + //System.out.println("消息入队的数量:" + queueBean.getEnqueueCount()); + private long enqueueCount; + //System.out.println("消息出队的数量:" + queueBean.getDequeueCount()); + private long dequeueCount; + + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/statistics/vo/StActiveMqQueueViewVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/statistics/vo/StActiveMqQueueViewVo.java new file mode 100644 index 0000000..26b7eb0 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/statistics/vo/StActiveMqQueueViewVo.java @@ -0,0 +1,30 @@ +package org.jeecg.statistics.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + +import java.util.List; + +@Data +@Tag(name="ActiveMq队列信息", description="ActiveMq队列信息") +public class StActiveMqQueueViewVo { + + @Schema(title = "queue统计") + List queueViewMBeanList; + + @Schema(title = "topic统计") + List topicViewMBeanList; + + + @Schema(title = "离线持久话题订阅者统计") + List subscriptionViewMBeanOffLineList; + + + @Schema(title = "活跃持久话题订阅者统计") + List subscriptionViewMBeanActiveList; + + + @Schema(title = "活跃的非持久主题订阅者统计") + List subscriptionViewMBeanActiveNonpersistentList; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/statistics/vo/StEatFoodInfoVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/statistics/vo/StEatFoodInfoVo.java new file mode 100644 index 0000000..0e10350 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/statistics/vo/StEatFoodInfoVo.java @@ -0,0 +1,113 @@ +package org.jeecg.statistics.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author qwer2 + */ +@Data +public class StEatFoodInfoVo { + + /** + * 所属单位 + */ + private String departName; + /** + * 食堂名称 + */ + private String restName; + /** + * 食堂编号 + */ + private String restNo; + /** + * 餐品id + */ + private String foodId; + /** + * 餐品名称 + */ + private String foodName; + /** + * 餐次 + */ + private String dinnerType; + /** + * 荤素 + */ + private String meatVege; + /** + * 凉热 + */ + private String hotCold; + /** + * 制作量(kg) + */ + private BigDecimal initial; + /** + * 天 + */ + private String days; + /** + * 取餐人数 + */ + private int personNum; + /** + * 取用量 + */ + private BigDecimal eatWeight; + + private Integer goodsNumType; + private Integer goodsNumMin; + private Integer goodsNumMax; + /** + * 食材数 + */ + private Integer goodsNum; + + private String userId; + private String canteenId; + + private Integer pageNo; + private Integer pageSize; + /** + * 部门id + */ + private String departId; + + private String orgCode; + + // 统计行相关 ↓ ↓ ↓ + /** + * 热菜数量(去重) + */ + private String hotCount; + /** + * 量菜数量(去重) + */ + private String coldCount; + /** + * 荤菜数量(去重) + */ + private String meatCount; + /** + * 素菜数量(去重) + */ + private String vegeCount; + /** + * 食材总数 + */ + private String totalGoodsNum; + /** + * 条数 食材总数/条数 = 平均食材数 + */ + private String totalSize; + + public StEatFoodInfoVo() { + this.pageNo = 1; + this.pageSize = 10; + } + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/statistics/vo/SubscriptionViewMBeanVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/statistics/vo/SubscriptionViewMBeanVo.java new file mode 100644 index 0000000..55dc131 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/statistics/vo/SubscriptionViewMBeanVo.java @@ -0,0 +1,31 @@ +package org.jeecg.statistics.vo; + +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + +@Data +@Tag(name="ActiveMq队列信息", description="ActiveMq队列信息") +public class SubscriptionViewMBeanVo { + + //System.out.println("客户编号 :" + queueBean.getClientId()); + private String clientId; + //System.out.println("订阅名称 :" + queueBean.getSubscriptionName()); + private String subscriptionName; + //System.out.println("连接 ID :" + queueBean.getConnectionId()); + private String connectionId; + //System.out.println("目的地 :" + queueBean.getDestinationName()); + private String destinationName; + //System.out.println("选择器:" + queueBean.getSelector()); + private String selector; + //System.out.println("待处理队列大小 :" + queueBean.getPendingQueueSize()); + private long pendingQueueSize; + //System.out.println("调度队列大小:" + queueBean.getDispatchedQueueSize()); + private long dispatchedQueueSize; + //System.out.println("调度柜台 :" + queueBean.getDispatchedCounter()); + private long dispatchedCounter; + //System.out.println("排队计数器:" + queueBean.getEnqueueCounter()); + private long enqueueCounter; + //System.out.println("出队计数器 :" + queueBean.getDequeueCounter()); + private long dequeueCounter; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/statistics/vo/TopicViewMBeanVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/statistics/vo/TopicViewMBeanVo.java new file mode 100644 index 0000000..472dda9 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/statistics/vo/TopicViewMBeanVo.java @@ -0,0 +1,23 @@ +package org.jeecg.statistics.vo; + +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; + +@Data +@Tag(name="ActiveMq队列信息", description="ActiveMq队列信息") +public class TopicViewMBeanVo { + + + //System.out.println("队列的名称:" + queueBean.getName()); + private String name; + //System.out.println("队列中剩余的消息数:" + queueBean.getQueueSize()); + private long queueSize; + //System.out.println("消费者数:" + queueBean.getConsumerCount()); + private long consumerCount; + //System.out.println("消息入队的数量:" + queueBean.getEnqueueCount()); + private long enqueueCount; + //System.out.println("消息出队的数量:" + queueBean.getDequeueCount()); + private long dequeueCount; + + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/system/dto/SysLoginModel.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/system/dto/SysLoginModel.java new file mode 100644 index 0000000..fabb251 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/system/dto/SysLoginModel.java @@ -0,0 +1,102 @@ +package org.jeecg.system.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; + + +/** + * 登录表单 + * + * @Author scott + * @since 2019-01-18 + */ +@Tag(name="登录对象", description="登录对象") +public class SysLoginModel { + @Schema(title = "id") + private String userId; + @Schema(title = "账号") + private String username; + @Schema(title = "密码") + private String password; + @Schema(title = "验证码") + private String captcha; + @Schema(title = "验证码key") + private String checkKey; + + @Schema(title = "身份证号") + private String idcard; + + @Schema(title = "微信code") + private String code; + + @Schema(title = "时间戳") + private String times; + + public String getTimes() { + return times; + } + + public void setTimes(String times) { + this.times = times; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getIdcard() { + return idcard; + } + + public void setIdcard(String idcard) { + this.idcard = idcard; + } + + + + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getCaptcha() { + return captcha; + } + + public void setCaptcha(String captcha) { + this.captcha = captcha; + } + + public String getCheckKey() { + return checkKey; + } + + public void setCheckKey(String checkKey) { + this.checkKey = checkKey; + } + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/system/entity/SysGatewayRoute.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/system/entity/SysGatewayRoute.java new file mode 100644 index 0000000..0eaaace --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/system/entity/SysGatewayRoute.java @@ -0,0 +1,111 @@ +package org.jeecg.system.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecg.common.aspect.annotation.Dict; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** + * @Description: gateway路由管理 + * @Author: jeecg-boot + * @Date: 2020-05-26 + * @Version: V1.0 + */ +@Data +@TableName("sys_gateway_route") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@ApiModel(value="sys_gateway_route对象", description="gateway路由管理") +public class SysGatewayRoute implements Serializable { + private static final long serialVersionUID = 1L; + + /**主键*/ + @TableId(type = IdType.ASSIGN_ID) + @ApiModelProperty(value = "主键") + private String id; + + /**routerKEy*/ + @ApiModelProperty(value = "路由ID") + private String routerId; + + /**服务名*/ + @Excel(name = "服务名", width = 15) + @ApiModelProperty(value = "服务名") + private String name; + + /**服务地址*/ + @Excel(name = "服务地址", width = 15) + @ApiModelProperty(value = "服务地址") + private String uri; + + /** + * 断言配置 + */ + private String predicates; + + /** + * 过滤配置 + */ + private String filters; + + /**是否忽略前缀0-否 1-是*/ + @Excel(name = "忽略前缀", width = 15) + @ApiModelProperty(value = "忽略前缀") + @Dict(dicCode = "yn") + private Integer stripPrefix; + + /**是否重试0-否 1-是*/ + @Excel(name = "是否重试", width = 15) + @ApiModelProperty(value = "是否重试") + @Dict(dicCode = "yn") + private Integer retryable; + + /**是否为保留数据:0-否 1-是*/ + @Excel(name = "保留数据", width = 15) + @ApiModelProperty(value = "保留数据") + @Dict(dicCode = "yn") + private Integer persistable; + + /**是否在接口文档中展示:0-否 1-是*/ + @Excel(name = "在接口文档中展示", width = 15) + @ApiModelProperty(value = "在接口文档中展示") + @Dict(dicCode = "yn") + private Integer showApi; + + /**状态 1有效 0无效*/ + @Excel(name = "状态", width = 15) + @ApiModelProperty(value = "状态") + @Dict(dicCode = "yn") + private Integer status; + + /**创建人*/ + @ApiModelProperty(value = "创建人") + private String createBy; + /**创建日期*/ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "创建日期") + private Date createTime; + /* *//**更新人*//* + @ApiModelProperty(value = "更新人") + private String updateBy; + *//**更新日期*//* + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "更新日期") + private Date updateTime; + *//**所属部门*//* + @ApiModelProperty(value = "所属部门") + private String sysOrgCode;*/ +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/StUserFaceimgSubIncremInfo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/StUserFaceimgSubIncremInfo.java new file mode 100644 index 0000000..c25a666 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/StUserFaceimgSubIncremInfo.java @@ -0,0 +1,75 @@ +package org.jeecg.user.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * 支付用户人脸信息增量记录表 + */ +@Data +@TableName("st_user_faceimg_sub_increm_info") +public class StUserFaceimgSubIncremInfo { + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_ID) + @Schema(title = "主键") + private String id; + @Schema(title = "删除状态(0-正常,1-已删除)") + private Integer delFlag; + + /** + * 创建人登录名称 + */ + @Schema(title = "创建人登录名称") + private String createBy; + /** + * 创建日期 + */ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + @Schema(title = "创建日期") + private Date createTime; + /** + * 更新人登录名称 + */ + @Schema(title = "更新人登录名称") + private String updateBy; + /** + * 更新日期 + */ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + @Schema(title = "更新日期") + private Date updateTime; + + //用户ID + private String userId; + + //人脸信息ID + private String userFaceId; + + //用户人脸特征数据 + private String faceFeatureString; + + //用户名称 + private String realname; + + //用户状态(1:正常 2:冻结 ) + private Integer status; + + /** + * 操作标记: + 0 人员特征删除数据 + 1 人员特征新增数据 + */ + private Integer faceType; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SwUserFaceimg.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SwUserFaceimg.java new file mode 100644 index 0000000..65a6da5 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SwUserFaceimg.java @@ -0,0 +1,153 @@ +package org.jeecg.user.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.util.Base64Utils; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * @Description: 用户人脸数据 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +@Data +@TableName("st_user_faceimg") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Tag(name = "st_user_faceimg对象", description = "用户人脸数据") +public class SwUserFaceimg implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_ID) + @Schema(title = "主键") + private String id; + /** + * 创建人登录名称 + */ + @Schema(title = "创建人登录名称") + private String createBy; + /** + * 创建日期 + */ + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @Schema(title = "创建日期") + private Date createTime; + /** + * 更新人登录名称 + */ + @Schema(title = "更新人登录名称") + private String updateBy; + /** + * 更新日期 + */ + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @Schema(title = "更新日期") + private Date updateTime; + /** + * 用户ID + */ + @Excel(name = "用户ID", width = 15) + @Schema(title = "用户ID") + private String userId; + /** + * 身份证号 + */ + @Excel(name = "身份证号", width = 15) + @Schema(title = "身份证号") + private String cardNumber; + /** + * 用户人脸信息 + */ + @Excel(name = "用户人脸信息", width = 15) + private transient String faceImgDataString; + + @Schema(title = "删除状态(0-正常,1-已删除)") + private Integer delFlag; + + private byte[] faceImgData; + + public byte[] getFaceImgData() { + if (faceImgDataString == null) { + return null; + } + return faceImgDataString.getBytes(); + } + + public String getFaceImgDataString() { + if (faceImgData == null || faceImgData.length == 0) { + return ""; + } + return new String(faceImgData); + } + + /** + * 有效标识 + */ + @Excel(name = "有效标识", width = 15) + @Schema(title = "有效标识") + private String faceStatus; + /** + * 用户人脸特征数据 + */ + @Excel(name = "用户人脸特征数据", width = 15) + private transient String faceFeatureDataString; + + /**同步标记(1:已同步,0:未同步)*/ + @Excel(name = "同步标记(1:已同步,0:未同步)", width = 15) + @Schema(title = "同步标记(1:已同步,0:未同步)") + private Integer isSync; + + /**数据来源(0:内部,1:外部)*/ + @Excel(name = "数据来源(0:内部,1:外部)", width = 15) + @Schema(title = "数据来源(0:内部,1:外部)") + private Integer dataSource; + + /**数据标记 0:食堂,1:体重秤,2:体重秤+食堂*/ + @Excel(name = "数据标记 0:食堂,1:体重秤,2:体重秤+食堂", width = 15) + @Schema(title = "数据标记 0:食堂,1:体重秤,2:体重秤+食堂") + private Integer dataType; + + /** + * 相似度 + */ + @Excel(name = "相似度", width = 15) + @Schema(title = "相似度") + private BigDecimal faceSimilar; + + /**特征来源(0:非绑盘机渠道获取,1:绑盘机渠道获取)*/ + @Excel(name = "特征来源(0:非绑盘机渠道获取,1:绑盘机渠道获取)", width = 15) + @Schema(title = "特征来源(0:非绑盘机渠道获取,1:绑盘机渠道获取)") + private Integer faceSource; + + private byte[] faceFeatureData; + + public String getFaceFeatureDataString() { + if (faceFeatureData == null || faceFeatureData.length == 0) { + return ""; + } + return Base64Utils.encodeToString(faceFeatureData); + } + + /** + * 人臉图片路径 + */ + private String faceImg; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SwUserFaceimgSelf.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SwUserFaceimgSelf.java new file mode 100644 index 0000000..87432d2 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SwUserFaceimgSelf.java @@ -0,0 +1,117 @@ +package org.jeecg.user.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** + * @Description: 用户人脸数据-自研引擎 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +@Data +@TableName("st_user_faceimg_self") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Tag(name = "st_user_faceimg_self对象", description = "用户人脸数据-自研引擎") +public class SwUserFaceimgSelf implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_ID) + @Schema(title = "主键") + private String id; + /** + * 创建人登录名称 + */ + @Schema(title = "创建人登录名称") + private String createBy; + /** + * 创建日期 + */ + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @Schema(title = "创建日期") + private Date createTime; + /** + * 更新人登录名称 + */ + @Schema(title = "更新人登录名称") + private String updateBy; + /** + * 更新日期 + */ + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @Schema(title = "更新日期") + private Date updateTime; + /** + * 用户ID + */ + @Excel(name = "用户ID", width = 15) + @Schema(title = "用户ID") + private String userId; + /** + * 用户人脸信息 + */ + @Excel(name = "用户人脸信息", width = 15) + private transient String faceImgDataString; + + /**同步标记(1:已同步,0:未同步)*/ + @Excel(name = "同步标记(1:已同步,0:未同步)", width = 15) + @Schema(title = "同步标记(1:已同步,0:未同步)") + private Integer isSync; + + /**数据来源(0:内部,1:外部)*/ + @Excel(name = "数据来源(0:内部,1:外部)", width = 15) + @Schema(title = "数据来源(0:内部,1:外部)") + private Integer dataSource; + + @Schema(title = "删除状态(0-正常,1-已删除)") + private Integer delFlag; + + private byte[] faceImgData; + + + public String getFaceImgDataString() { + if (faceImgData == null || faceImgData.length == 0) { + return ""; + } + return new String(faceImgData); + } + + /** + * 有效标识 + */ + @Excel(name = "有效标识", width = 15) + @Schema(title = "有效标识") + private String faceStatus; + /** + * 用户人脸特征数据 + */ + @Excel(name = "用户人脸特征数据", width = 15) + private transient String faceFeatureDataString; + + private byte[] faceFeatureData; + + public String getFaceFeatureDataString() { + if (faceFeatureData == null || faceFeatureData.length == 0) { + return ""; + } + return new String(faceFeatureData); + } +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SwUserFaceimgSub.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SwUserFaceimgSub.java new file mode 100644 index 0000000..c18a85a --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SwUserFaceimgSub.java @@ -0,0 +1,173 @@ +package org.jeecg.user.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.util.Base64Utils; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * @Description: 用户人脸数据 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +@Data +@TableName("st_user_faceimg_sub") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Tag(name = "st_user_faceimg对象", description = "用户人脸数据") +public class SwUserFaceimgSub implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_ID) + @Schema(title = "主键") + private String id; + /** + * 创建人登录名称 + */ + @Schema(title = "创建人登录名称") + private String createBy; + /** + * 创建日期 + */ + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @Schema(title = "创建日期") + private Date createTime; + /** + * 更新人登录名称 + */ + @Schema(title = "更新人登录名称") + private String updateBy; + /** + * 更新日期 + */ + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @Schema(title = "更新日期") + private Date updateTime; + /** + * 用户ID + */ + @Excel(name = "用户ID", width = 15) + @Schema(title = "用户ID") + private String userId; + /** + * 身份证号 + */ + @Excel(name = "身份证号", width = 15) + @Schema(title = "身份证号") + private String cardNumber; + /** + * 用户人脸信息 + */ + @Excel(name = "用户人脸信息", width = 15) + private transient String faceImgDataString; + + @Schema(title = "删除状态(0-正常,1-已删除)") + private Integer delFlag; + + private byte[] faceImgData; + + public byte[] getFaceImgData() { + if (faceImgDataString == null) { + return null; + } + return faceImgDataString.getBytes(); + } + + public String getFaceImgDataString() { + if (faceImgData == null || faceImgData.length == 0) { + return ""; + } + return new String(faceImgData); + } + + /** + * 有效标识 + */ + @Excel(name = "有效标识", width = 15) + @Schema(title = "有效标识") + private String faceStatus; + /** + * 用户人脸特征数据 + */ + @Excel(name = "用户人脸特征数据", width = 15) + private transient String faceFeatureDataString; + + /**同步标记(1:已同步,0:未同步)*/ + @Excel(name = "同步标记(1:已同步,0:未同步)", width = 15) + @Schema(title = "同步标记(1:已同步,0:未同步)") + private Integer isSync; + + /**数据来源(0:内部,1:外部)*/ + @Excel(name = "数据来源(0:内部,1:外部)", width = 15) + @Schema(title = "数据来源(0:内部,1:外部)") + private Integer dataSource; + + /**数据标记 0:食堂,1:体重秤*/ + @Excel(name = "数据标记 0:食堂,1:体重秤", width = 15) + @Schema(title = "数据标记 0:食堂,1:体重秤") + private Integer dataType; + + /** + * 相似度 + */ + @Excel(name = "相似度", width = 15) + @Schema(title = "相似度") + private BigDecimal faceSimilar; + + /**特征来源(0:非绑盘机渠道获取,1:绑盘机渠道获取)*/ + @Excel(name = "特征来源(0:非绑盘机渠道获取,1:绑盘机渠道获取)", width = 15) + @Schema(title = "特征来源(0:非绑盘机渠道获取,1:绑盘机渠道获取)") + private Integer faceSource; + + private byte[] faceFeatureData; + + public String getFaceFeatureDataString() { + if (faceFeatureData == null || faceFeatureData.length == 0) { + return ""; + } + return Base64Utils.encodeToString(faceFeatureData); + } + + private String faceImg; + /** + * 0内部 1外部 余:只有长庆正式员工属于内部员工 , 所有家庭成员以及 非正式员工都数据外部 + */ + @TableField(exist = false) + private Integer userType; + + /** + * 真实姓名 + */ + @Schema(title = "真实姓名") + @TableField(exist = false) + private String realname; + + /** + * 状态(1-正常,2-冻结) + */ + @TableField(exist = false) + private Integer status; + + @Excel(name = "用户人脸特征数据-闸机使用该特征码", width = 15) + @TableField(exist = false) + private String faceFeatureString; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SysPayDepart.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SysPayDepart.java new file mode 100644 index 0000000..22b6832 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SysPayDepart.java @@ -0,0 +1,177 @@ +package org.jeecg.user.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.jeecg.common.aspect.annotation.Dict; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; +import java.util.Objects; + +/** + *

+ * 部门表 + *

+ * + * @Author Steve + * @Since 2019-01-22 + */ +@Data +@TableName("sys_pay_depart") +public class SysPayDepart implements Serializable { + private static final long serialVersionUID = 1L; + + /**ID*/ + @TableId(type = IdType.ASSIGN_ID) + private String id; + /**父机构ID*/ + private String parentId; + /**机构/部门名称*/ + @Excel(name="机构/部门名称",width=15) + private String departName; + /**英文名*/ + @Excel(name="英文名",width=15) + private String departNameEn; + /**缩写*/ + private String departNameAbbr; + /**排序*/ + @Excel(name="排序",width=15) + private Integer departOrder; + /**描述*/ + @Excel(name="描述",width=15) + private String description; + /**机构类别 1=公司,2=组织机构,3=岗位*/ + @Excel(name="机构类别",width=15,dicCode="org_category") + private String orgCategory; + /**机构类型*/ + private String orgType; + /**机构编码*/ + @Excel(name="机构编码",width=15) + private String orgCode; + /**手机号*/ + @Excel(name="手机号",width=15) + private String mobile; + /**传真*/ + @Excel(name="传真",width=15) + private String fax; + /**地址*/ + @Excel(name="地址",width=15) + private String address; + /**备注*/ + @Excel(name="备注",width=15) + private String memo; + /**状态(1启用,0不启用)*/ + @Dict(dicCode = "depart_status") + private String status; + /**删除状态(0,正常,1已删除)*/ + @Dict(dicCode = "del_flag") + private String delFlag; + /**对接企业微信的ID*/ + private String qywxIdentifier; + /**创建人*/ + private String createBy; + /**创建日期*/ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + private Date createTime; + /**更新人*/ + private String updateBy; + /**更新日期*/ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @Schema(title = "同步标记(1:已同步,0:未同步)") + private Integer isSync; + + /** + * 用户id + */ + @TableField(exist = false) + private String userId; + /** + * 登录账号 + */ + @TableField(exist = false) + private String username; + /** + * 真实姓名 + */ + @TableField(exist = false) + private String realname; + /** + * 密码 + */ + @TableField(exist = false) + private String password; + + /** + * 角色id + */ + @TableField(exist = false) + private String selectedroles; + + /** + * 用户类型 + */ + @TableField(exist = false) + private String userIdentity; + + + + /** + * 重写equals方法 + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + SysPayDepart depart = (SysPayDepart) o; + return Objects.equals(id, depart.id) && + Objects.equals(parentId, depart.parentId) && + Objects.equals(departName, depart.departName) && + Objects.equals(departNameEn, depart.departNameEn) && + Objects.equals(departNameAbbr, depart.departNameAbbr) && + Objects.equals(departOrder, depart.departOrder) && + Objects.equals(description, depart.description) && + Objects.equals(orgCategory, depart.orgCategory) && + Objects.equals(orgType, depart.orgType) && + Objects.equals(orgCode, depart.orgCode) && + Objects.equals(mobile, depart.mobile) && + Objects.equals(fax, depart.fax) && + Objects.equals(address, depart.address) && + Objects.equals(memo, depart.memo) && + Objects.equals(status, depart.status) && + Objects.equals(delFlag, depart.delFlag) && + Objects.equals(createBy, depart.createBy) && + Objects.equals(createTime, depart.createTime) && + Objects.equals(updateBy, depart.updateBy) && + Objects.equals(updateTime, depart.updateTime); + } + + /** + * 重写hashCode方法 + */ + @Override + public int hashCode() { + + return Objects.hash(super.hashCode(), id, parentId, departName, + departNameEn, departNameAbbr, departOrder, description,orgCategory, + orgType, orgCode, mobile, fax, address, memo, status, + delFlag, createBy, createTime, updateBy, updateTime); + } +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SysUser.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SysUser.java new file mode 100644 index 0000000..d1dc806 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SysUser.java @@ -0,0 +1,217 @@ +package org.jeecg.user.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecg.common.aspect.annotation.Dict; +import org.jeecg.common.system.vo.ScreenVo; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + *

+ * 用户表 + *

+ * + * @Author scott + * @since 2018-12-20 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +public class SysUser implements Serializable { + + + /** + * id + */ + @TableId(type = IdType.ASSIGN_ID) + private String id; + + /** + * 登录账号 + */ + @Excel(name = "用户账号", width = 15) + private String username; + + /** + * 真实姓名 + */ + @Excel(name = "用户姓名", width = 15) + private String realname; + + /** + * 密码 + */ +// @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private String password; + + /** + * md5密码盐 + */ +// @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private String salt; + + /** + * 头像 + */ + @Excel(name = "头像", width = 15,type = 2) + private String avatar; + + /** + * 生日 + */ +// @Excel(name = "生日", width = 15, format = "yyyy-MM-dd") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date birthday; + + /** + * 性别(1:男 2:女) + */ + @Excel(name = "性别", width = 15,dicCode="sex") + @Dict(dicCode = "sex") + private Integer sex; + + /** + * 电子邮件 + */ +// @Excel(name = "电子邮件", width = 15) + private String email; + + /** + * 电话 + */ + @Excel(name = "电话", width = 15) + private String phone; + + /** + * 部门code(当前选择登录部门) + */ + private String sysOrgCode; + + /**部门名称*/ + private transient String orgCodeTxt; + + /** + * 状态(1:正常 2:冻结 ) + */ + @Excel(name = "状态", width = 15,dicCode="user_status") + @Dict(dicCode = "user_status") + private Integer status; + + /** + * 删除状态(0,正常,1已删除) + */ +// @Excel(name = "删除状态", width = 15,dicCode="del_flag") + //@TableLogic + private Integer delFlag; + + /** + * 工号,唯一键 + */ +// @Excel(name = "工号", width = 15) + private String workNo; + + /** + * 职务,关联职务表 + */ +// @Excel(name = "职务", width = 15) +// @Dict(dictTable ="sys_position",dicText = "name",dicCode = "code") + private String post; + + /** + * 座机号 + */ +// @Excel(name = "座机号", width = 15) + private String telephone; + + /** + * 创建人 + */ + private String createBy; + + /** + * 创建时间 + */ + @Excel(name = "创建日期", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新人 + */ + private String updateBy; + + /** + * 更新时间 + */ + @Excel(name = "创建日期", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + /** + * 同步工作流引擎1同步0不同步 + */ + private Integer activitiSync; + + /** + * 身份 + */ +// @Excel(name="身份",width = 15) + private Integer userIdentity; + + /** + * 负责部门 + */ +// @Excel(name="负责部门",width = 15,dictTable ="sys_depart",dicText = "depart_name",dicCode = "id") + // @Dict(dictTable ="sys_depart",dicText = "depart_name",dicCode = "id") + private String departIds; + + /** + * 多租户id配置,编辑用户的时候设置 + */ + private String relTenantIds; + + /**设备id uniapp推送用*/ + private String clientId; + + // @Excel(name = "身份证号", width = 15) + private String cardNo; + + /** + * 第三方ID + */ + private String thirdId; + + @TableField(exist = false) + private List screenVoList; + + @TableField(exist = false) + private List userRole; + + /**机构名称*/ + @Excel(name = "所属机构", width = 15) + @Schema(title = "所属机构") + @TableField(exist = false) + private String departName; + + /**用户类型*/ + @Excel(name = "用户类型", width = 15) + @Schema(title = "用户类型") + @TableField(exist = false) + private String userIdentityName2; + + @Schema(title = "同步标记(1:已同步,0:未同步)") + private Integer isSync; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SysUserScreen.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SysUserScreen.java new file mode 100644 index 0000000..f67d39d --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SysUserScreen.java @@ -0,0 +1,49 @@ +package org.jeecg.user.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecgframework.poi.excel.annotation.Excel; + +import java.io.Serializable; + +/** + * @Description: s + * @Author: yucl + * @Date: 2022-04-20 + * @Version: V1.0 + */ +@Data +@TableName("sys_user_screen") +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@Tag(name="sys_user_screen对象", description="s") +public class SysUserScreen implements Serializable{ + + /**id*/ + @TableId(type = IdType.ASSIGN_ID) + @Schema(title = "id") + private String id; + /**用户id*/ + @Excel(name = "用户id", width = 15) + @Schema(title = "用户id") + private String userId; + /**食堂id*/ + @Excel(name = "食堂id", width = 15) + @Schema(title = "食堂id") + private String screenId; + /**食堂部门id*/ + @Excel(name = "食堂部门id", width = 15) + @Schema(title = "食堂部门id") + private String screenDeptId; + //同步标记(1:已同步,0:未同步) + private Integer isSync; + + //删除状态(0-正常,1-已删除) + private Integer delFlag; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SysUserSub.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SysUserSub.java new file mode 100644 index 0000000..cb1dfbb --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/entity/SysUserSub.java @@ -0,0 +1,211 @@ +package org.jeecg.user.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecg.common.aspect.annotation.Dict; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 子用户表 + *

+ * + * @Author scott + * @since 2018-12-20 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("sys_user_sub") +public class SysUserSub implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_ID) + private String id; + + /** + * 登录账号 + */ + @Excel(name = "用户账号", width = 15) + private String username; + + /** + * 真实姓名 + */ + @Excel(name = "用户姓名", width = 15) + private String realname; + + /** + * 密码 + */ +// @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private String password; + + /** + * md5密码盐 + */ +// @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private String salt; + + /** + * 头像 + */ + @Excel(name = "头像", width = 15,type = 2) + private String avatar; + + /** + * 生日 + */ +// @Excel(name = "生日", width = 15, format = "yyyy-MM-dd") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date birthday; + + /** + * 性别(1:男 2:女) + */ + @Excel(name = "性别", width = 15,dicCode="sex") + @Dict(dicCode = "sex") + private Integer sex; + + + /** + * 电话 + */ + @Excel(name = "电话", width = 15) + private String phone; + + /** + * 部门code(当前选择登录部门) + */ + private String sysOrgCode; + + /**部门名称*/ + private transient String orgCodeTxt; + + /** + * 状态(1:正常 2:冻结 ) + */ + @Excel(name = "状态", width = 15,dicCode="user_status") + @Dict(dicCode = "user_status") + private Integer status; + + /** + * 删除状态(0,正常,1已删除) + */ +// @Excel(name = "删除状态", width = 15,dicCode="del_flag") + //@TableLogic + private Integer delFlag; + + /** + * 卡号 + */ + private String cardCode; + + /** + * 卡号状态(1:正常 , 2:挂失) + */ + private Integer cardCodeStatus; + + /** + * 创建人 + */ + private String createBy; + + /** + * 创建时间 + */ + @Excel(name = "创建日期", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新人 + */ + private String updateBy; + + /** + * 更新时间 + */ + @Excel(name = "创建日期", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + + /** + * 身份 + */ +// @Excel(name="身份",width = 15) + private Integer userIdentity; + + + // @Excel(name = "身份证号", width = 15) + private String cardNo; + + /** + * 负责部门 + */ +// @Excel(name="负责部门",width = 15,dictTable ="sys_depart",dicText = "depart_name",dicCode = "id") + // @Dict(dictTable ="sys_depart",dicText = "depart_name",dicCode = "id") + private String departIds; + + @Schema(title ="工号") + private String workNo; + + /** + * 用户类型(0:正式员工,1:非正式员工,2:家庭用户) + */ + private Integer userType; + + /**绑定主用户ID*/ + @Excel(name = "绑定主用户ID", width = 15) + @Schema(title = "绑定主用户ID") + private String mainUserId; + + /**与主账户关系(1:亲子,2:夫妻,3:兄弟姐妹,4:父母)*/ + @Excel(name = "与主账户关系(1:亲子,2:夫妻,3:兄弟姐妹,4:父母)", width = 15) + @Schema(title = "与主账户关系(1:亲子,2:夫妻,3:兄弟姐妹,4:父母)") + private String mainUserRelation; + + + /**食堂id*/ + @Excel(name = "食堂id", width = 15) + @Schema(title = "食堂id") + private String canteenId; + + + /**机构名称*/ + @Excel(name = "所属机构", width = 15) + @Schema(title = "所属机构") + @TableField(exist = false) + private String departName; + + /***/ + @Excel(name = "", width = 15) + @Schema(title = "") + @TableField(exist = false) + private String userIdentityName2; + + @Schema(title = "同步标记(1:已同步,0:未同步)") + private Integer isSync; + + + private String remark; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/PayDepartIdModel.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/PayDepartIdModel.java new file mode 100644 index 0000000..51b2ff5 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/PayDepartIdModel.java @@ -0,0 +1,96 @@ +package org.jeecg.user.vo; + + + + + +import org.jeecg.user.entity.SysPayDepart; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + *

+ * 部门表 封装树结构的部门的名称的实体类 + *

+ * + * @Author Steve + * @Since 2019-01-22 + * + */ +public class PayDepartIdModel implements Serializable { + + private static final long serialVersionUID = 1L; + + // 主键ID + private String key; + + // 主键ID + private String value; + + // 部门名称 + private String title; + + List children = new ArrayList<>(); + + /** + * 将SysDepartTreeModel的部分数据放在该对象当中 + * @param treeModel + * @return + */ + public PayDepartIdModel convert(SysPayDepartTreeModel treeModel) { + this.key = treeModel.getId(); + this.value = treeModel.getId(); + this.title = treeModel.getDepartName(); + return this; + } + + /** + * 该方法为用户部门的实现类所使用 + * @param sysDepart + * @return + */ + public PayDepartIdModel convertByUserDepart(SysPayDepart sysDepart) { + this.key = sysDepart.getId(); + this.value = sysDepart.getId(); + this.title = sysDepart.getDepartName(); + return this; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/StClientMemberBalanceVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/StClientMemberBalanceVo.java new file mode 100644 index 0000000..04759b9 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/StClientMemberBalanceVo.java @@ -0,0 +1,100 @@ +package org.jeecg.user.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import org.jeecg.common.system.base.entity.JeecgEntity; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 用户钱包表 + */ + +@Tag(name="user-entity-SwClientMemberBalance") +@Data +public class StClientMemberBalanceVo extends JeecgEntity implements Serializable { + + @Schema(title ="会员id") + private String memberId; + + @Schema(title ="充值余额(元)") + private BigDecimal topUpBalance; + + @Schema(title ="累计充值") + private BigDecimal topUpTotal; + + @Schema(title ="赠送余额") + private BigDecimal rewardBalance; + + @Schema(title ="积分余额") + private Integer integralBalance; + + @Schema(title ="消费次数") + private Integer consumptionCount; + + @Schema(title ="累计消费合计") + private BigDecimal consumptionTotal; + + @Schema(title ="最后消费时间") + private Date lastConsumptionTime; + + @Schema(title ="首充时间") + private Date firstTopUpTime; + + @Schema(title ="可开发票金额") + private BigDecimal invoiceTotal; + + @Schema(title ="用户类型:内部用户=0,外部用户=1") + private Integer userInnerType; + + /** + * 钱包状态(1-禁用,0-正常) + */ + @Schema(title ="钱包状态(1-禁用,0-正常)") + private Integer isStatus; + + /** + * 是否删除(1-已删除,0-正常) + */ +// @TableLogic( value = "0", delval = "1") +// @JsonIgnore + @Schema(title ="是否已删除(1-true,0-false)") + private Integer isDel; + + @Schema(title ="同步标记(1:已同步,0:未同步)") + private Integer isSync; + + /** + * 食堂ID + */ + @Schema(title ="食堂ID") +// @JsonIgnore + private String canteenId; + + private static final long serialVersionUID = 1L; + + public static final String COL_ID = "id"; + + public static final String COL_MEMBER_ID = "member_id"; + + public static final String COL_TOP_UP_BALANCE = "top_up_balance"; + + public static final String COL_REWARD_BALANCE = "reward_balance"; + + public static final String COL_INTEGRAL_BALANCE = "integral_balance"; + + public static final String COL_CONSUMPTION_COUNT = "consumption_count"; + + public static final String COL_CONSUMPTION_TOTAL = "consumption_total"; + + public static final String COL_LAST_CONSUMPTION_TIME = "last_consumption_time"; + + public static final String COL_FIRST_TOP_UP_TIME = "first_top_up_time"; + + public static final String COL_IS_DEL = "is_del"; + + public static final String COL_EA_ID = "canteen_id"; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SwUserFaceimgWeightScaleV2Vo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SwUserFaceimgWeightScaleV2Vo.java new file mode 100644 index 0000000..0794ec4 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SwUserFaceimgWeightScaleV2Vo.java @@ -0,0 +1,58 @@ +package org.jeecg.user.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecgframework.poi.excel.annotation.Excel; + +import java.io.Serializable; + +/** + * @Description: 用户人脸数据 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Tag(name = "SwUserFaceimgWeightScaleVo 对象", description = "用户人脸数据") +public class SwUserFaceimgWeightScaleV2Vo implements Serializable { + /** + * 身份证号 + */ + @Excel(name = "身份证号", width = 15) + @Schema(title = "身份证号") + private String cardNumber; + + private byte[] faceImgData; + + /** + * 用户人脸信息 + */ + @Excel(name = "用户人脸信息", width = 15) + private transient String faceImgDataString; + + public byte[] getFaceImgData() { + if (faceImgDataString == null) { + return null; + } + return faceImgDataString.getBytes(); + } + + public String getFaceImgDataString() { + if (faceImgData == null || faceImgData.length == 0) { + return ""; + } + return new String(faceImgData); + } + /** + * 用户姓名 + */ + @Excel(name = "用户姓名", width = 15) + @Schema(title = "用户姓名") + private String name; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SwUserFaceimgWeightScaleVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SwUserFaceimgWeightScaleVo.java new file mode 100644 index 0000000..a84ab99 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SwUserFaceimgWeightScaleVo.java @@ -0,0 +1,48 @@ +package org.jeecg.user.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecgframework.poi.excel.annotation.Excel; + +import java.io.Serializable; + +/** + * @Description: 用户人脸数据 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Tag(name = "SwUserFaceimgWeightScaleVo 对象", description = "用户人脸数据") +public class SwUserFaceimgWeightScaleVo implements Serializable { + /** + * 身份证号 + */ + @Excel(name = "身份证号", width = 15) + @Schema(title = "身份证号") + private String cardNumber; + /** + * 用户人脸信息 + */ + @Excel(name = "用户人脸信息", width = 15) + private transient String faceFeatureDataString; + + /** + * 用户人脸信息 + */ + @Excel(name = "用户人脸信息", width = 15) + private transient String faceImgDataString; + + /** + * 用户姓名 + */ + @Excel(name = "用户姓名", width = 15) + @Schema(title = "用户姓名") + private String name; + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SysPayDepartTreeModel.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SysPayDepartTreeModel.java new file mode 100644 index 0000000..4e38e4a --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SysPayDepartTreeModel.java @@ -0,0 +1,384 @@ +package org.jeecg.user.vo; + + +import org.jeecg.user.entity.SysPayDepart; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + *

+ * 部门表 存储树结构数据的实体类 + *

+ * + * @Author Steve + * @Since 2019-01-22 + */ +public class SysPayDepartTreeModel implements Serializable{ + + private static final long serialVersionUID = 1L; + + /** 对应SysDepart中的id字段,前端数据树中的key*/ + private String key; + + /** 对应SysDepart中的id字段,前端数据树中的value*/ + private String value; + + /** 对应depart_name字段,前端数据树中的title*/ + private String title; + + + private boolean isLeaf; + // 以下所有字段均与SysDepart相同 + + private String id; + + private String parentId; + + private String departName; + + private String departNameEn; + + private String departNameAbbr; + + private Integer departOrder; + + private String description; + + private String orgCategory; + + private String orgType; + + private String orgCode; + + private String mobile; + + private String fax; + + private String address; + + private String memo; + + private String status; + + private String delFlag; + + private String qywxIdentifier; + + private String createBy; + + private Date createTime; + + private String updateBy; + + private Date updateTime; + + private List children = new ArrayList<>(); + + + /** + * 将SysDepart对象转换成SysDepartTreeModel对象 + * @param sysDepart + */ + public SysPayDepartTreeModel(SysPayDepart sysDepart) { + this.key = sysDepart.getId(); + this.value = sysDepart.getId(); + this.title = sysDepart.getDepartName(); + this.id = sysDepart.getId(); + this.parentId = sysDepart.getParentId(); + this.departName = sysDepart.getDepartName(); + this.departNameEn = sysDepart.getDepartNameEn(); + this.departNameAbbr = sysDepart.getDepartNameAbbr(); + this.departOrder = sysDepart.getDepartOrder(); + this.description = sysDepart.getDescription(); + this.orgCategory = sysDepart.getOrgCategory(); + this.orgType = sysDepart.getOrgType(); + this.orgCode = sysDepart.getOrgCode(); + this.mobile = sysDepart.getMobile(); + this.fax = sysDepart.getFax(); + this.address = sysDepart.getAddress(); + this.memo = sysDepart.getMemo(); + this.status = sysDepart.getStatus(); + this.delFlag = sysDepart.getDelFlag(); + this.qywxIdentifier = sysDepart.getQywxIdentifier(); + this.createBy = sysDepart.getCreateBy(); + this.createTime = sysDepart.getCreateTime(); + this.updateBy = sysDepart.getUpdateBy(); + this.updateTime = sysDepart.getUpdateTime(); + } + + public boolean getIsLeaf() { + return isLeaf; + } + + public void setIsLeaf(boolean isleaf) { + this.isLeaf = isleaf; + } + + public String getKey() { + return key; + } + + + public void setKey(String key) { + this.key = key; + } + + + public String getValue() { + return value; + } + + + public void setValue(String value) { + this.value = value; + } + + + public String getTitle() { + return title; + } + + + public void setTitle(String title) { + this.title = title; + } + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + if (children==null){ + this.isLeaf=true; + } + this.children = children; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public String getDepartName() { + return departName; + } + + public void setDepartName(String departName) { + this.departName = departName; + } + + public String getOrgCategory() { + return orgCategory; + } + + public void setOrgCategory(String orgCategory) { + this.orgCategory = orgCategory; + } + + public String getOrgType() { + return orgType; + } + + public void setOrgType(String orgType) { + this.orgType = orgType; + } + + public String getOrgCode() { + return orgCode; + } + + public void setOrgCode(String orgCode) { + this.orgCode = orgCode; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public String getFax() { + return fax; + } + + public void setFax(String fax) { + this.fax = fax; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getMemo() { + return memo; + } + + public void setMemo(String memo) { + this.memo = memo; + } + + public String getDepartNameEn() { + return departNameEn; + } + + public void setDepartNameEn(String departNameEn) { + this.departNameEn = departNameEn; + } + + public String getDepartNameAbbr() { + return departNameAbbr; + } + + public void setDepartNameAbbr(String departNameAbbr) { + this.departNameAbbr = departNameAbbr; + } + + public Integer getDepartOrder() { + return departOrder; + } + + public void setDepartOrder(Integer departOrder) { + this.departOrder = departOrder; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getDelFlag() { + return delFlag; + } + + public void setDelFlag(String delFlag) { + this.delFlag = delFlag; + } + + public String getQywxIdentifier() { + return qywxIdentifier; + } + + public void setQywxIdentifier(String qywxIdentifier) { + this.qywxIdentifier = qywxIdentifier; + } + + public String getCreateBy() { + return createBy; + } + + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getUpdateBy() { + return updateBy; + } + + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public SysPayDepartTreeModel() { } + + /** + * 重写equals方法 + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SysPayDepartTreeModel model = (SysPayDepartTreeModel) o; + return Objects.equals(id, model.id) && + Objects.equals(parentId, model.parentId) && + Objects.equals(departName, model.departName) && + Objects.equals(departNameEn, model.departNameEn) && + Objects.equals(departNameAbbr, model.departNameAbbr) && + Objects.equals(departOrder, model.departOrder) && + Objects.equals(description, model.description) && + Objects.equals(orgCategory, model.orgCategory) && + Objects.equals(orgType, model.orgType) && + Objects.equals(orgCode, model.orgCode) && + Objects.equals(mobile, model.mobile) && + Objects.equals(fax, model.fax) && + Objects.equals(address, model.address) && + Objects.equals(memo, model.memo) && + Objects.equals(status, model.status) && + Objects.equals(delFlag, model.delFlag) && + Objects.equals(qywxIdentifier, model.qywxIdentifier) && + Objects.equals(createBy, model.createBy) && + Objects.equals(createTime, model.createTime) && + Objects.equals(updateBy, model.updateBy) && + Objects.equals(updateTime, model.updateTime) && + Objects.equals(children, model.children); + } + + /** + * 重写hashCode方法 + */ + @Override + public int hashCode() { + + return Objects.hash(id, parentId, departName, departNameEn, departNameAbbr, + departOrder, description, orgCategory, orgType, orgCode, mobile, fax, address, + memo, status, delFlag, qywxIdentifier, createBy, createTime, updateBy, updateTime, + children); + } + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SysUserSubInfo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SysUserSubInfo.java new file mode 100644 index 0000000..d244a68 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SysUserSubInfo.java @@ -0,0 +1,209 @@ +package org.jeecg.user.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecg.common.aspect.annotation.Dict; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 子用户表 + *

+ * + * @Author scott + * @since 2018-12-20 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +public class SysUserSubInfo implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + private String id; + + /** + * 登录账号 + */ + @Excel(name = "用户账号", width = 15) + private String username; + + /** + * 真实姓名 + */ + @Excel(name = "用户姓名", width = 15) + private String realname; + + /** + * 密码 + */ +// @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private String password; + + /** + * md5密码盐 + */ +// @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private String salt; + + /** + * 头像 + */ + @Excel(name = "头像", width = 15,type = 2) + private String avatar; + + /** + * 生日 + */ +// @Excel(name = "生日", width = 15, format = "yyyy-MM-dd") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date birthday; + + /** + * 性别(1:男 2:女) + */ + @Excel(name = "性别", width = 15,dicCode="sex") + @Dict(dicCode = "sex") + private Integer sex; + + /** + * 卡号 + */ + private String cardCode; + + /** + * 卡号状态(1:正常 , 2:挂失) + */ + private Integer cardCodeStatus; + + /** + * 电话 + */ + @Excel(name = "电话", width = 15) + private String phone; + + /** + * 部门code(当前选择登录部门) + */ + private String sysOrgCode; + + /**部门名称*/ + private transient String orgCodeTxt; + + /** + * 状态(1:正常 2:冻结 ) + */ + @Excel(name = "状态", width = 15,dicCode="user_status") + @Dict(dicCode = "user_status") + private Integer status; + + /** + * 删除状态(0,正常,1已删除) + */ +// @Excel(name = "删除状态", width = 15,dicCode="del_flag") + //@TableLogic + private Integer delFlag; + + + + /** + * 创建人 + */ + private String createBy; + + /** + * 创建时间 + */ + @Excel(name = "创建日期", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新人 + */ + private String updateBy; + + /** + * 更新时间 + */ + @Excel(name = "创建日期", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + + /** + * 身份 + */ +// @Excel(name="身份",width = 15) + private Integer userIdentity; + + + // @Excel(name = "身份证号", width = 15) + private String cardNo; + + /** + * 负责部门 + */ +// @Excel(name="负责部门",width = 15,dictTable ="sys_depart",dicText = "depart_name",dicCode = "id") + // @Dict(dictTable ="sys_depart",dicText = "depart_name",dicCode = "id") + private String departIds; + + @Schema(title ="工号") + private String workNo; + + /** + * 用户类型(0:正式员工,1:非正式员工,2:家庭用户) + */ + private Integer userType; + + /**绑定主用户ID*/ + @Excel(name = "绑定主用户ID", width = 15) + @Schema(title = "绑定主用户ID") + private String mainUserId; + + /**与主账户关系(1:亲子,2:夫妻,3:兄弟姐妹,4:父母)*/ + @Excel(name = "与主账户关系(1:亲子,2:夫妻,3:兄弟姐妹,4:父母)", width = 15) + @Schema(title = "与主账户关系(1:亲子,2:夫妻,3:兄弟姐妹,4:父母)") + private String mainUserRelation; + + + /**食堂id*/ + @Excel(name = "食堂id", width = 15) + @Schema(title = "食堂id") + private String canteenId; + + + /**机构名称*/ + @Excel(name = "所属机构", width = 15) + @Schema(title = "所属机构") + private String departName; + + /***/ + @Excel(name = "", width = 15) + @Schema(title = "") + private String userIdentityName2; + + @Schema(title = "同步标记(1:已同步,0:未同步)") + private Integer isSync; + + + + private String remark; + + + //钱包id + private String balanceId; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SysUserSubVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SysUserSubVo.java new file mode 100644 index 0000000..9812bed --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SysUserSubVo.java @@ -0,0 +1,41 @@ +package org.jeecg.user.vo; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + *

+ * 子用户表 + *

+ * + * @Author scott + * @since 2018-12-20 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +public class SysUserSubVo implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private String userId; + + /** + * 状态(1:正常 2:作废 ) + */ + private Integer status; + + + /** + * 用户类型(0: 正式员工 1:非正式员工,2:家庭用户) + */ + private Integer userType; + + +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SysUserVo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SysUserVo.java new file mode 100644 index 0000000..f3e1483 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/SysUserVo.java @@ -0,0 +1,36 @@ +package org.jeecg.user.vo; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.jeecg.user.entity.SysUser; + +@Data +public class SysUserVo extends SysUser { +// @Schema(title = "所属机构") +// public String departName; + + @Schema(title = "用户等级") + public String userIdentityName; + + // + @Schema(title = "食堂部门id") + private String canteenDeptId; + + @Schema(title = "食堂部门Name") + private String canteenDeptName; + + // + @Schema(title = "食堂id") + private String canteenId; + + @Schema(title = "所属职工部门Name") + private String screenDeptName; + @Schema(title = "所属职工部门Name") + private String screenDept; + + /** + * 当前的用户类型 (0:超级管理员(系统内置-内部使用) 1:admin 2:二级管理员 3:平台用户 4:食堂管理员 5:食堂职工 6:员工(就餐人员)) + */ + private Integer userIdentityRange; +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/UserCompareInfo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/UserCompareInfo.java new file mode 100644 index 0000000..cc1d329 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/UserCompareInfo.java @@ -0,0 +1,14 @@ +package org.jeecg.user.vo; + +public class UserCompareInfo extends UserFaceInfo { + + private Float similar; + + public Float getSimilar() { + return similar; + } + + public void setSimilar(Float similar) { + this.similar = similar; + } +} diff --git a/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/UserFaceInfo.java b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/UserFaceInfo.java new file mode 100644 index 0000000..7320ba6 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/jeecg-system-local-api/src/main/java/org/jeecg/user/vo/UserFaceInfo.java @@ -0,0 +1,40 @@ +package org.jeecg.user.vo; + +import lombok.Data; + +@Data +public class UserFaceInfo { + + private String userId; + + private byte[] faceFeature; + + private String userFaceId; + + private String faceFeatureString; + + private String realname; + + /** + * 有效标识 0 无效,1:有效 + */ + private Integer status; + + /** + * 操作标记 + * faceType =0 人员特征删除数据 + * faceType =1 人员特征新增数据 + */ + private String faceType; + /**数据标记 + * 数据标记 0:食堂,1:体重秤,2:体重秤+食堂 + * + * */ + private Integer dataType; + + /** + * 用户借调数据状态(1:借调数据 ,否则默认非借调数据 ) + * + * */ + private String dataStatus; +} diff --git a/jeecg-module-system/jeecg-system-api/pom.xml b/jeecg-module-system/jeecg-system-api/pom.xml new file mode 100644 index 0000000..a13f180 --- /dev/null +++ b/jeecg-module-system/jeecg-system-api/pom.xml @@ -0,0 +1,25 @@ + + + + jeecg-module-system + org.jeecgframework.boot + 3.4.0 + + 4.0.0 + + jeecg-system-api + pom + + + jeecg-system-local-api + + + + + org.jeecgframework.boot + jeecg-boot-base-core + + + \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-biz/.gitattributes b/jeecg-module-system/jeecg-system-biz/.gitattributes new file mode 100644 index 0000000..d479839 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/.gitattributes @@ -0,0 +1,4 @@ +*.js linguist-language=Java +*.css linguist-language=Java +*.html linguist-language=Java +*.vue linguist-language=Java diff --git a/jeecg-module-system/jeecg-system-biz/pom.xml b/jeecg-module-system/jeecg-system-biz/pom.xml new file mode 100644 index 0000000..f2e72c7 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/pom.xml @@ -0,0 +1,89 @@ + + + org.jeecgframework.boot + jeecg-module-system + 3.4.0 + + 4.0.0 + + jeecg-system-biz + + + + org.jeecgframework.boot + jeecg-system-local-api + + + org.hibernate + hibernate-core + + + org.jeecgframework.boot + hibernate-re + + + com.arcsoft + arcsoft-sdk-face + 4.1.1.0 + system + ${project.basedir}/src/main/resources/lib/arcsoft-sdk-face-4.1.1.0.jar + + + org.springframework.boot + spring-boot-starter-activemq + + + + org.apache.activemq + activemq-pool + 5.16.7 + + + + + cn.hutool + hutool-all + 5.8.28 + + + + + org.jeecgframework + jeewx-api + + + + org.jeecgframework.jimureport + jimureport-spring-boot-starter + + + + net.sf.barcode4j + barcode4j-light + 2.0 + + + org.springframework + spring-test + 5.2.10.RELEASE + + + org.bouncycastle + bcprov-jdk15on + 1.62 + compile + + + org.apache.pulsar + pulsar-client + 3.3.0 + + + com.yixiong.tools + yixiong + 1.0.3 + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/activemq/QueueConsumerListener.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/activemq/QueueConsumerListener.java new file mode 100644 index 0000000..4633806 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/activemq/QueueConsumerListener.java @@ -0,0 +1,71 @@ +package org.jeecg.activemq; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.activemq.command.ActiveMQMessage; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.api.vo.ActiveMqEntityVO; +import org.jeecg.equipment.entity.StEquipment; +import org.jeecg.equipment.service.IStEquipmentService; +import org.springframework.jms.annotation.JmsListener; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.jms.JMSException; +import javax.jms.Session; +import javax.jms.TextMessage; + +@Component +@Slf4j +public class QueueConsumerListener { + + @Resource + private IStEquipmentService iStEquipmentService; + /** + * 接受设备服务 投递给本服务的 设备数据同步 主题消息 + */ + @JmsListener(destination="queue_msg_equipment_finance", containerFactory="equipmentQueueListener") + public void receiveQueueStEquipment(ActiveMQMessage message, Session session) throws JMSException { + log.info("receive topic_msg:1"); + try { + String text = null; + if (message instanceof TextMessage) { + TextMessage textMessage = (TextMessage) message; + text = textMessage.getText(); +// log.info("topic_msg接收到消息:{}", text); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false); + ActiveMqEntityVO activeMqEntityVO = objectMapper.readValue(text, ActiveMqEntityVO.class); + if (activeMqEntityVO.getEntityReference().contains("entity.StEquipment")) { + StEquipment stEquipment = objectMapper.readValue(activeMqEntityVO.getDataJsonString(), StEquipment.class); +// log.info("接受某服务 投递给 zhst服务 的 消息 queue_msg_equipment_zhst 接收到 StEquipment :{}", JSONObject.toJSONString(stEquipment)); + + if(StringUtils.isNotBlank(stEquipment.getId()) && null == stEquipment.getCreateTime()){//表示删除 + boolean flag = iStEquipmentService.removeById(stEquipment.getId()); +// if(flag){ +// log.info("queue_msg_equipment_zhst 接收到消息,删除设备:{} 成功", stEquipment.getId()); +// } + } else { + boolean flag = iStEquipmentService.saveOrUpdate(stEquipment); + if(!flag) { + throw new Exception("执行 StEquipment 保存数据异常"); + } + } + } + + + // 确认消息。只要被确认后 就会出队,接受失败没有确认成功,会在原队列里面 + message.acknowledge(); +// log.info("手动ACK确认ok"); + } + } catch (Exception e) { + log.error("出现异常,消费失败,放弃消费"+e.getMessage()); + // 此不可省略 重发信息使用,如果不写此方法,将不会实现重发操作。失败的消息将会一直在队列中,因为没有进行消息确认。6次异常消息会放入死信队列ActiveMQ.DLQ + session.recover(); + } + + } + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/aspect/AspectUtil.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/aspect/AspectUtil.java new file mode 100644 index 0000000..582f5de --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/aspect/AspectUtil.java @@ -0,0 +1,34 @@ +package org.jeecg.aspect; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.Signature; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +@Component +public class AspectUtil { + + /** + * + * 功能描述:通过JoinPoint获取注解MyOperationLog + * + * 访问目标方法参数,有三种方法(实际有四种): + * 1.joinpoint.getargs():获取带参方法的参数 + * 2.joinpoint.getTarget():.获取他们的目标对象信息 + * 3.joinpoint.getSignature():(signature是信号,标识的意思):获取被增强的方法相关信息 + * + */ + public SendNotification getMyOperationLogByJoinPoint(JoinPoint joinPoint) { + Signature signature = joinPoint.getSignature(); + MethodSignature methodSignature = (MethodSignature) signature; + Method method = methodSignature.getMethod(); + if (null == method) { + return null; + } + return method.getAnnotation(SendNotification.class); + } + +} + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/aspect/SendNotification.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/aspect/SendNotification.java new file mode 100644 index 0000000..45d0f04 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/aspect/SendNotification.java @@ -0,0 +1,17 @@ +package org.jeecg.aspect; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface SendNotification { + // 方法名称 + String methodName() default ""; + + // 当前操作人 + String currentUser() default ""; + + // 操作 + String operate() default ""; +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/emp/controller/CqBaseEmployeePayController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/emp/controller/CqBaseEmployeePayController.java new file mode 100644 index 0000000..389fbe7 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/emp/controller/CqBaseEmployeePayController.java @@ -0,0 +1,230 @@ +package org.jeecg.emp.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.aspect.annotation.AutoLog; +import org.jeecg.common.system.base.controller.JeecgController; +import org.jeecg.emp.entity.CqBaseEmployee; +import org.jeecg.emp.service.ICqBaseEmployeeService; +import org.jeecg.emp.vo.CqBaseEmpVo; +import org.jeecg.system.service.ISwUserFaceimgService; +import org.jeecg.system.service.ISwUserFaceimgSubService; +import org.jeecg.system.service.ISysUserSubService; +import org.jeecg.user.entity.SwUserFaceimgSub; +import org.jeecg.user.entity.SysUserSub; +import org.jeecg.user.vo.SysUserSubVo; +import org.jeecg.user.vo.UserFaceInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @Description: 子员工信息表 + * @Author: jeecg-boot + * @Date: 2021-12-31 + * @Version: V1.0 + */ +@Tag(name = "员工信息表") +@RestController +@RequestMapping("/emp/cqBaseEmployee/sub") +@Slf4j +public class CqBaseEmployeePayController extends JeecgController { + @Autowired + private ICqBaseEmployeeService cqBaseEmployeeService; + @Resource + private ISysUserSubService sysUserSubService; + @Autowired + private ISwUserFaceimgSubService iSwUserFaceimgSubService; + @Autowired + private ISwUserFaceimgService swUserFaceimgService; + /** + * 分页列表查询 + * + * @param baseEmpVo + * @param pageNo + * @param pageSize + * @param req + * @return + */ + @AutoLog(value = "员工信息表-分页列表查询") + @Operation(summary = "员工信息表-分页列表查询", description = "员工信息表-分页列表查询") + @GetMapping(value = "/list") + + public Result queryPageList(CqBaseEmpVo baseEmpVo, + @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, + @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, + HttpServletRequest req) { + Page pageList = new Page(pageNo, pageSize); + pageList = cqBaseEmployeeService.queryListV2(pageList, baseEmpVo); + return Result.OK(pageList); + + } + + /** + * 分页列表查询 + * + * @param baseEmpVo + * @param pageNo + * @param pageSize + * @param req + * @return + */ + @AutoLog(value = "员工信息表-分页列表查询") + @Operation(summary = "员工信息表-分页列表查询", description = "员工信息表-分页列表查询") + @GetMapping(value = "/list/V3") + + public Result queryPageListV3(CqBaseEmpVo baseEmpVo, + @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, + @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, + HttpServletRequest req) { + Page pageList = new Page(pageNo, pageSize); + pageList = cqBaseEmployeeService.queryListV3(pageList, baseEmpVo); + return Result.OK(pageList); + + } + + /** + * 非正式、家庭用户添加 + * + * @return + */ + @AutoLog(value = "非正式、家庭用户添加-添加") + @Operation(summary = "非正式、家庭用户添加-添加", description = "非正式、家庭用户添加-添加") + @PostMapping(value = "/add") + public Result add(@RequestBody SysUserSub sysUserSub) { + if(StringUtils.isBlank(sysUserSub.getRealname())){ + return Result.error("用户名不能为空"); + } + //家庭用户 + if(StringUtils.isNotBlank(sysUserSub.getMainUserId()) && StringUtils.isBlank(sysUserSub.getMainUserRelation())){ + return Result.error("与主账户关系不能为空"); + } + //非正式员工 + if(StringUtils.isBlank(sysUserSub.getMainUserId()) && StringUtils.isBlank(sysUserSub.getPhone())){ + return Result.error("手机号不能为空"); + } + return cqBaseEmployeeService.saveEmpSub(sysUserSub); + } + + + /** + * 非正式、家庭用户编辑 + * + * @return + */ + @AutoLog(value = "非正式、家庭用户编辑-编辑") + @Operation(summary = "非正式、家庭用户编辑-编辑", description = "非正式、家庭用户编辑-编辑") + @PutMapping(value = "/edit") + public Result edit(@RequestBody SysUserSub sysUserSub) { + if(StringUtils.isBlank(sysUserSub.getId())){ + return Result.error("用户ID不能为空"); + } + Result result = cqBaseEmployeeService.updateEmpSub(sysUserSub); + Map stringMap = new HashMap<>(); + stringMap.put("userId", sysUserSub.getId()); + return result; + } + + /** + * 查询用户 + * @return + */ + @AutoLog(value = "查询用户") + @Operation(summary = "查询用户", description = "查询用户") + @GetMapping(value = "/getByIdSub") + public Result getByIdSub(@RequestParam(name = "userId") String userId) { + if(StringUtils.isBlank(userId)){ + return Result.error("用户ID不能为空"); + } + return Result.OK(sysUserSubService.getById(userId)); + } + + /** + * 用户作废与恢复 + * //用户类型(0: 正式员工 1:非正式员工,2:家庭用户) + * //当前用户是正式员工:停用钱包状态 钱包状态(1-禁用,0-正常)' + * //当前用户是非正式员工:停用钱包状态 钱包状态(1-禁用,0-正常)' + * //当前用户是家庭用户:停用用户状态(状态(1:正常 2:冻结 )) + * @return + */ + @AutoLog(value = "用户作废与恢复") + @Operation(summary = "用户作废与恢复", description = "用户作废与恢复") + @PostMapping(value = "/cancelOrRecovery") + public Result cancellationAndRecovery(@RequestBody SysUserSubVo sysUserSub) { + if(StringUtils.isBlank(sysUserSub.getUserId())){ + return Result.error("用户ID不能为空"); + } + if(null == sysUserSub.getStatus()){ + return Result.error("操作状态不能为空"); + } + Result result = cqBaseEmployeeService.cancellationAndRecovery(sysUserSub); + return result; + } + + + /** + * 通过id删除 + * + * @param id + * @return + */ + @AutoLog(value = "员工信息表-通过id删除") + @Operation(summary = "员工信息表-通过id删除", description = "员工信息表-通过id删除") + @DeleteMapping(value = "/delete") + public Result delete(@RequestParam(name = "id", required = true) String id) { + //2025-05-07 余:逻辑删除用户 扣费人员,油田员工逻辑删除不动人脸,非正式员工逻辑删除,人脸逻辑删除) + SysUserSub sysUserSub = sysUserSubService.getById(id); + if(null == sysUserSub){ + return Result.error("用户不存在"); + } + // 2025-05-09 ,如果主用户下面有子用户,删除主用户时是否关联删除子用户?余:做简单点,有子用户的主用户不能删除,要删逐级删除。 + // 判断删除的是主用户,当前用户下存在子用户不能直接删除 + if(StringUtils.isBlank(sysUserSub.getMainUserId())){ + LambdaQueryWrapper subLambdaQueryWrapper = new LambdaQueryWrapper<>(); + subLambdaQueryWrapper.eq(SysUserSub::getMainUserId, id); + subLambdaQueryWrapper.eq(SysUserSub::getDelFlag, 0); + //查询所有子用户 + List sysUserSubs = sysUserSubService.list(subLambdaQueryWrapper); + if(CollectionUtils.isNotEmpty(sysUserSubs)){ + return Result.error("当前用户下存在子用户不能直接删除"); + } + } + if(0 != sysUserSub.getUserType()){ + sysUserSubService.deleteUserSub(id); + //删除用户信息时删除人脸数据 + List slUserFaceimgList = iSwUserFaceimgSubService.lambdaQuery().eq(SwUserFaceimgSub::getUserId, id). + eq(SwUserFaceimgSub::getDelFlag, 0).list();//人脸图片和特征 + if(CollectionUtils.isNotEmpty(slUserFaceimgList)){ + slUserFaceimgList.forEach(swUserFaceimgSub->{ + swUserFaceimgSub.setDelFlag(1); + //2025-05-07 余:逻辑删除用户特征 + iSwUserFaceimgSubService.updateById(swUserFaceimgSub); + //发送删除扣费闸机人脸特征数据 + UserFaceInfo userInfo = new UserFaceInfo(); + userInfo.setUserId(swUserFaceimgSub.getUserId()); + swUserFaceimgService.sendPaymentGateUserInfo(userInfo,0,false,null); + }); + } + } else { + //正式员工-逻辑删除serSub表 + sysUserSubService.deleteUserSub(id); + //正式员工--发送删除扣费闸机人脸特征数据 + UserFaceInfo userInfo = new UserFaceInfo(); + userInfo.setUserId(sysUserSub.getId()); + swUserFaceimgService.sendPaymentGateUserInfo(userInfo,0,false,sysUserSub.getId()); + } + return Result.OK("删除成功!"); + } + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/emp/mapper/CqBaseEmployeeMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/emp/mapper/CqBaseEmployeeMapper.java new file mode 100644 index 0000000..21181e3 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/emp/mapper/CqBaseEmployeeMapper.java @@ -0,0 +1,112 @@ +package org.jeecg.emp.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import org.jeecg.emp.entity.CqBaseEmployee; +import org.jeecg.emp.vo.CqBaseEmpVo; +import org.jeecg.emp.vo.DepartVo; +import org.jeecg.emp.vo.EmpInfoVo; +import org.jeecg.emp.vo.UserInfoVo; +import org.jeecg.user.entity.SysUser; + +import java.util.List; +import java.util.Map; + +/** + * @Description: 员工信息表 + * @Author: jeecg-boot + * @Date: 2021-12-31 + * @Version: V1.0 + */ +public interface CqBaseEmployeeMapper extends BaseMapper { + + List queryList(Page page, @Param("vo") CqBaseEmpVo baseEmpVo); + + List queryListV2(Page page, @Param("vo") CqBaseEmpVo baseEmpVo); + + + /** + * 查询用户树形结构(支持分页和搜索) + * @param page 分页参数 + * @return 分页结果 + */ + List selectUserTreePage(Page page,@Param("vo") CqBaseEmpVo baseEmpVo); + + void updateIsStatusByMemberId(Map map); + + void updateStatusById(Map map); + + List queryListSubUser(@Param("list") List userIdList); + + List queryList(@Param("vo") CqBaseEmpVo baseEmpVo); + + public EmpInfoVo getEmpInfo(String idcard); + + + public EmpInfoVo getEmpInfoByUsername(String username); + + public EmpInfoVo getEmpInfoByUserId(String userId); + + + @Select("select id,depart_name,org_code from sys_depart where org_code like 'A01%' and length(org_code) = 6 and del_flag = 0 order by depart_order asc") + public List getSecondDepartList(); + + /** + * + * @return + */ + @Select("select id,depart_name,org_code from sys_depart where org_code like concat((select left(org_code,6) from sys_depart where FIND_IN_SET(id,#{departIds})limit 1),'%') and length(org_code) = 6 AND del_flag = 0 order by depart_order asc") + public List getChargeSecondDepartList(String departIds); + + /** + * + * @return + */ + @Select("select id,depart_name,org_code from sys_depart where org_code = #{orgCode} and length(org_code) = 6 AND del_flag = 0 order by depart_order asc") + public List getChargeSecondDepartListV2(String orgCode); + + @Select("select id,depart_name,org_code from sys_depart where FIND_IN_SET(id,#{departIds}) and length(org_code) = 9 AND del_flag = 0 order by depart_order asc") + public List getChargeThirdDepartList(String departIds); + + @Select("select id,depart_name,org_code from sys_depart where parent_id = (SELECT id FROM sys_depart WHERE org_code = #{orgCode} ) and length(org_code) = 9 AND del_flag = 0 order by depart_order asc") + public List getChargeThirdDepartListV2(String departIds); + + @Select("select id,depart_name,org_code from sys_depart where org_code like 'A01%' and length(org_code) = 9 and parent_id = #{parentId} AND del_flag = 0 order by depart_order asc") + public List getThirdDepartList(String parentId); + + + @Select("select id,username,avatar,phone,realname from sys_user where FIND_IN_SET(id,#{userIds}) AND del_flag = 0 ") + public List geUserNameByIdList(String userIds); + + /** + * 获取用户明文手机号 + * @param userId 用户id + * @return 手机号 + */ + String getPhoneByUserId(@Param("userId") String userId); + + /** + * 获取用户明文手机号 + * @param userId 用户id + * @return 手机号 + */ + String getIdCardByUserId(@Param("userId") String userId); + + /** + * 根据身份证号 获取用户信息 + * @param idCardList 身份证列表 + * @return 用户列表 + */ + List getUserList(@Param("list") List idCardList); + + public EmpInfoVo getEmpInfoByJobNumberLimit1(@Param("workNo") String workNo); + + public List getEmpInfoByUsernameList(@Param("username") String username); + + + EmpInfoVo getEmpInfoVoByUserId(String userId); + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/emp/mapper/xml/CqBaseEmployeeMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/emp/mapper/xml/CqBaseEmployeeMapper.xml new file mode 100644 index 0000000..55f3e47 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/emp/mapper/xml/CqBaseEmployeeMapper.xml @@ -0,0 +1,464 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + UPDATE st_client_member_balance + + + is_status = #{isStatus} + + + WHERE member_id = #{memberId} + + + + UPDATE sys_user_sub + + + status = #{status} , + + + remark = #{remark} + + + WHERE id = #{id} + + + + + + + + + + + + + + + + + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/emp/service/ICqBaseEmployeeService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/emp/service/ICqBaseEmployeeService.java new file mode 100644 index 0000000..1c719d8 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/emp/service/ICqBaseEmployeeService.java @@ -0,0 +1,41 @@ +package org.jeecg.emp.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.jeecg.common.api.vo.Result; +import org.jeecg.emp.entity.CqBaseEmployee; +import org.jeecg.emp.vo.CqBaseEmpVo; +import org.jeecg.emp.vo.DepartVo; +import org.jeecg.user.entity.SysUser; +import org.jeecg.user.entity.SysUserSub; +import org.jeecg.user.vo.SysUserSubVo; + +import java.util.List; + +/** + * @Description: 员工信息表 + * @Author: jeecg-boot + * @Date: 2021-12-31 + * @Version: V1.0 + */ +public interface ICqBaseEmployeeService extends IService { + + public Result saveEmpSub(SysUserSub jsonObject); + + Page queryListV2(Page pageList, CqBaseEmpVo baseEmpVo); + + Page queryListV3(Page pageList, CqBaseEmpVo baseEmpVo); + + Result updateEmpSub(SysUserSub jsonObject); + + Result cancellationAndRecovery(SysUserSubVo sysUserSub); + + /** + * 获取二级部门列表 + * + * @return + */ + public List getSecondDepartList(); + + List getUserList(List idCardList); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/emp/service/impl/CqBaseEmployeeServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/emp/service/impl/CqBaseEmployeeServiceImpl.java new file mode 100644 index 0000000..d780497 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/emp/service/impl/CqBaseEmployeeServiceImpl.java @@ -0,0 +1,176 @@ +package org.jeecg.emp.service.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.util.*; +import org.jeecg.emp.entity.CqBaseEmployee; +import org.jeecg.emp.mapper.CqBaseEmployeeMapper; +import org.jeecg.emp.service.ICqBaseEmployeeService; +import org.jeecg.emp.vo.CqBaseEmpVo; +import org.jeecg.emp.vo.DepartVo; +import org.jeecg.system.service.ISysPayDepartService; +import org.jeecg.system.service.ISysUserService; +import org.jeecg.system.service.ISysUserSubService; +import org.jeecg.user.entity.SysUser; +import org.jeecg.user.entity.SysUserSub; +import org.jeecg.user.vo.SysUserSubVo; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @Description: 员工信息表 + * @Author: jeecg-boot + * @Date: 2021-12-31 + * @Version: V1.0 + */ +@Service +@Slf4j +public class CqBaseEmployeeServiceImpl extends ServiceImpl implements ICqBaseEmployeeService { + @Resource + private CqBaseEmployeeMapper employeeMapper; + @Autowired + private ICqBaseEmployeeService cqBaseEmployeeService; + @Resource + private ISysUserSubService sysUserSubService; + @Resource + private ISysUserService sysUserService; + + @Resource + private ISysPayDepartService iSysPayDepartService; + + + @Override + public Result saveEmpSub(SysUserSub user) { + Result result = new Result<>(); + try { + //设置创建时间 + user.setCreateTime(new Date()); + user.setStatus(1); + user.setDelFlag(CommonConstant.DEL_FLAG_0); + user.setSysOrgCode("A01A54");//20250411 余:同一放采气六厂的,orgCode A01A54 + user.setUserIdentity(6); + if(StringUtils.isBlank(user.getMainUserId())){ + user.setMainUserId(null); + user.setUserType(1);//非正式员工 + } else { + user.setUserType(2);//家庭用户 + } + Result result1 = sysUserService.saveUserSub(user); + if(result1.getCode() != 200){ + return result1; + } + result.success("添加成功!"); + result.setMessage("添加成功!"); + } catch (Exception e) { + log.error(e.getMessage(), e); + result.error500("操作失败"); + } + + return result; + } + + + + + @Override + public Page queryListV2(Page page, CqBaseEmpVo baseEmpVo) { + //SQL注入:MyBatis-高危解决 + if(StringUtils.isNotBlank(baseEmpVo.getDepartId())){ + List allCascadeDeptIds = iSysPayDepartService.getAllCascadeDeptIds(baseEmpVo.getDepartId()); + if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(allCascadeDeptIds)){ + baseEmpVo.setList(allCascadeDeptIds); + } + } + List list = employeeMapper.queryListV2(page, baseEmpVo); + if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(list)){ + List stringList = list.stream().map(CqBaseEmpVo::getId).collect(Collectors.toList()); + List subList = employeeMapper.queryListSubUser(stringList); + list.forEach(user->{ + List subUserList = subList.stream().filter(emp -> emp.getMainUserId().equals(user.getId())).collect(Collectors.toList()); + user.setSubUserList(subUserList); + }); + } + return page.setRecords(list); + } + + @Override + public Page queryListV3(Page page, CqBaseEmpVo baseEmpVo) { + //SQL注入:MyBatis-高危解决 + List list = employeeMapper.selectUserTreePage(page, baseEmpVo); + return page.setRecords(list); + } + + @Override + public Result updateEmpSub(SysUserSub jsonObject) { + Result result = new Result(); + try { + //更新用户表 + SysUserSub sysUser = sysUserSubService.getById(jsonObject.getId()); + BeanUtils.copyProperties(jsonObject, sysUser); + sysUser.setUpdateTime(new Date());//设置创建时间 + sysUserService.editUserSub(sysUser); + result.success("编辑成功!"); + } catch (Exception e) { + log.error(e.getMessage(), e); + result.error500("操作失败"); + } + + return result; + } + + @Override + public Result cancellationAndRecovery(SysUserSubVo sysUserSub){ + //用户类型(0: 正式员工 1:非正式员工,2:家庭用户) + //当前用户是正式员工:停用用户+钱包状态 钱包状态(1-禁用,0-正常)' + //当前用户是非正式员工:停用用户+钱包状态 钱包状态(1-禁用,0-正常)' + //当前用户是家庭用户:停用用户状态(状态(1:正常 2:冻结 )) + SysUserSub sysUserSub1 = sysUserSubService.getById(sysUserSub.getUserId()); + if(null == sysUserSub1){ + return Result.error("用户不存在"); + } + if(0 == sysUserSub1.getUserType() || 1 == sysUserSub1.getUserType()){ + Map params = new HashMap<>(); + params.put("memberId", sysUserSub.getUserId()); + //状态(1:正常 2:作废 ) + if(1 == sysUserSub.getStatus()){ + params.put("isStatus", 0); // 钱包状态(1-禁用,0-正常)' + } + if(2 == sysUserSub.getStatus()){ + params.put("isStatus", 1); // 钱包状态(1-禁用,0-正常)' + } + employeeMapper.updateIsStatusByMemberId(params); + } + Map params = new HashMap<>(); + params.put("id", sysUserSub.getUserId()); + params.put("status", sysUserSub.getStatus()); // 停用用户状态(状态(1:正常 2:冻结 )) + String name = sysUserSub.getStatus() == 1 ? "恢复正常" : "冻结"; + String msg = DateUtils.formatDateTime() + "用户【"+sysUserSub1.getRealname()+"】被"+name; + params.put("remark", sysUserSub1.getRemark()+","+msg); + employeeMapper.updateStatusById(params); + return Result.OK("操作成功"); + } + + + @Override + public List getSecondDepartList() { + return employeeMapper.getSecondDepartList(); + } + + + @Override + public List getUserList(List idCardList) { + return baseMapper.getUserList(idCardList); + } +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/controller/StEquipmentController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/controller/StEquipmentController.java new file mode 100644 index 0000000..6e21167 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/controller/StEquipmentController.java @@ -0,0 +1,24 @@ +package org.jeecg.equipment.controller; + +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.system.base.controller.JeecgController; +import org.jeecg.equipment.entity.StEquipment; +import org.jeecg.equipment.service.IStEquipmentService; +import org.springframework.web.bind.annotation.*; + + /** + * @Description: 设备管理表 + * @Author: jeecg-boot + * @Date: 2022-08-30 + * @Version: V1.0 + */ +@Tag(name="设备管理表") +@RestController +@RequestMapping("/equipment/stEquipment") +@Slf4j +public class StEquipmentController extends JeecgController { + + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/controller/StRestaurantEquipmentController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/controller/StRestaurantEquipmentController.java new file mode 100644 index 0000000..0def169 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/controller/StRestaurantEquipmentController.java @@ -0,0 +1,55 @@ +package org.jeecg.equipment.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.aspect.annotation.AutoLog; +import org.jeecg.common.system.base.controller.JeecgController; +import org.jeecg.common.system.util.JwtUtil; +import org.jeecg.equipment.entity.StEquipment; +import org.jeecg.equipment.service.IStEquipmentService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.List; + +/** +* @Description: 设备管理表 +* @Author: jeecg-boot +* @Date: 2022-08-30 +* @Version: V1.0 +*/ +@Tag(name="设备管理表") +@RestController +@RequestMapping("/restaurant/equipment/stEquipment") +@Slf4j +public class StRestaurantEquipmentController extends JeecgController { + @Autowired + private IStEquipmentService stEquipmentService; + + + /** + * 食堂直连外网设备统一根据设备号获取token(配比秤) + */ + @Operation(summary = "食堂直连外网设备统一根据设备号获取token", description = "食堂直连外网设备统一根据设备号获取token") + @GetMapping("/getEquipmentToken") + @AutoLog(value = "食堂直连外网设备统一根据设备号获取token") + public Result getEquipmentToken(@RequestParam String qrcodeId) { + if(StringUtils.isBlank(qrcodeId)){ + return Result.error("设备编号不能为空"); + } + LambdaQueryWrapper stEquipmentLambdaQueryWrapper = new LambdaQueryWrapper<>(); + stEquipmentLambdaQueryWrapper.eq(StEquipment::getEquipmentCode, qrcodeId); + stEquipmentLambdaQueryWrapper.orderByDesc(StEquipment::getCreateTime); + List stEquipmentList = stEquipmentService.list(stEquipmentLambdaQueryWrapper); + if(org.apache.commons.collections4.CollectionUtils.isEmpty(stEquipmentList)){ + log.error("根据设备号获取token,异常:设备编号不存在,qrcodeId="+qrcodeId); + return Result.error("设备编号不存在"); + } + String token = JwtUtil.signUserId("0000000000000000001", qrcodeId, qrcodeId,"4"); + return Result.OK(token); + } + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/mapper/StEquipmentMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/mapper/StEquipmentMapper.java new file mode 100644 index 0000000..77679b7 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/mapper/StEquipmentMapper.java @@ -0,0 +1,14 @@ +package org.jeecg.equipment.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.jeecg.equipment.entity.StEquipment; + +/** + * @Description: 设备管理表 + * @Author: jeecg-boot + * @Date: 2022-08-30 + * @Version: V1.0 + */ +public interface StEquipmentMapper extends BaseMapper { + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/mapper/StWeighSacleOrgCodeMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/mapper/StWeighSacleOrgCodeMapper.java new file mode 100644 index 0000000..4e1ae9b --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/mapper/StWeighSacleOrgCodeMapper.java @@ -0,0 +1,15 @@ +package org.jeecg.equipment.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.jeecg.equipment.entity.StWeighSacleOrgCode; + +/** + * @Description: 体重秤设备二级部门关系表 + * @Author: jeecg-boot + * @Date: 2022-09-16 + * @Version: V1.0 + */ +public interface StWeighSacleOrgCodeMapper extends BaseMapper { + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/mapper/xml/StEquipmentMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/mapper/xml/StEquipmentMapper.xml new file mode 100644 index 0000000..36a6c7b --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/mapper/xml/StEquipmentMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/mapper/xml/StWeighSacleOrgCodeMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/mapper/xml/StWeighSacleOrgCodeMapper.xml new file mode 100644 index 0000000..d3aa72d --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/mapper/xml/StWeighSacleOrgCodeMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/service/IStEquipmentService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/service/IStEquipmentService.java new file mode 100644 index 0000000..28735d9 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/service/IStEquipmentService.java @@ -0,0 +1,14 @@ +package org.jeecg.equipment.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.jeecg.equipment.entity.StEquipment; + +/** + * @Description: 设备管理表 + * @Author: jeecg-boot + * @Date: 2022-08-30 + * @Version: V1.0 + */ +public interface IStEquipmentService extends IService { + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/service/IStWeighSacleOrgCodeService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/service/IStWeighSacleOrgCodeService.java new file mode 100644 index 0000000..bdb7beb --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/service/IStWeighSacleOrgCodeService.java @@ -0,0 +1,14 @@ +package org.jeecg.equipment.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.jeecg.equipment.entity.StWeighSacleOrgCode; + +/** + * @Description: 体重秤设备二级部门关系表 + * @Author: jeecg-boot + * @Version: V1.0 + */ +public interface IStWeighSacleOrgCodeService extends IService { + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/service/impl/StEquipmentServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/service/impl/StEquipmentServiceImpl.java new file mode 100644 index 0000000..39bdbd4 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/service/impl/StEquipmentServiceImpl.java @@ -0,0 +1,19 @@ +package org.jeecg.equipment.service.impl; + +import org.jeecg.equipment.entity.StEquipment; +import org.jeecg.equipment.mapper.StEquipmentMapper; +import org.jeecg.equipment.service.IStEquipmentService; +import org.springframework.stereotype.Service; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +/** + * @Description: 设备管理表 + * @Author: jeecg-boot + * @Date: 2022-08-30 + * @Version: V1.0 + */ +@Service +public class StEquipmentServiceImpl extends ServiceImpl implements IStEquipmentService { + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/service/impl/StWeighSacleOrgCodeServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/service/impl/StWeighSacleOrgCodeServiceImpl.java new file mode 100644 index 0000000..64ac69e --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/equipment/service/impl/StWeighSacleOrgCodeServiceImpl.java @@ -0,0 +1,18 @@ +package org.jeecg.equipment.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.jeecg.equipment.entity.StWeighSacleOrgCode; +import org.jeecg.equipment.mapper.StWeighSacleOrgCodeMapper; +import org.jeecg.equipment.service.IStWeighSacleOrgCodeService; +import org.springframework.stereotype.Service; + +/** + * @Description: 体重秤设备二级部门关系表 + * @Author: jeecg-boot + * @Date: 2022-09-16 + * @Version: V1.0 + */ +@Service +public class StWeighSacleOrgCodeServiceImpl extends ServiceImpl implements IStWeighSacleOrgCodeService { + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/face/factory/FaceEngineFactory.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/face/factory/FaceEngineFactory.java new file mode 100644 index 0000000..092ffbd --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/face/factory/FaceEngineFactory.java @@ -0,0 +1,61 @@ +package org.jeecg.face.factory; + +import com.arcsoft.face.EngineConfiguration; +import com.arcsoft.face.FaceEngine; +import com.arcsoft.face.enums.ErrorInfo; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.pool2.BasePooledObjectFactory; +import org.apache.commons.pool2.PooledObject; +import org.apache.commons.pool2.impl.DefaultPooledObject; + +@Slf4j +public class FaceEngineFactory extends BasePooledObjectFactory { + + + private String libPath; + private String appId; + private String sdkKey; + private String activeKey; + private EngineConfiguration engineConfiguration; + + + public FaceEngineFactory(String libPath, String appId, String sdkKey, String activeKey, EngineConfiguration engineConfiguration) { + this.appId = appId; + this.sdkKey = sdkKey; + this.activeKey = activeKey; + this.libPath = libPath; + this.engineConfiguration = engineConfiguration; + } + + + @Override + public FaceEngine create() { + + + FaceEngine faceEngine = new FaceEngine(libPath); + int activeCode = faceEngine.activeOnline(appId, sdkKey, activeKey); + if (activeCode != ErrorInfo.MOK.getValue() && activeCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) { + log.error("引擎激活失败" + activeCode); +// throw new BusinessException("引擎激活失败" + activeCode); + } + int initCode = faceEngine.init(engineConfiguration); + if (initCode != ErrorInfo.MOK.getValue()) { + log.error("引擎初始化失败" + initCode); +// throw new BusinessException(ErrorCodeEnum.FAIL, "引擎初始化失败" + initCode); + } + return faceEngine; + } + + @Override + public PooledObject wrap(FaceEngine faceEngine) { + return new DefaultPooledObject<>(faceEngine); + } + + + @Override + public void destroyObject(PooledObject p) throws Exception { + FaceEngine faceEngine = p.getObject(); + int result = faceEngine.unInit(); + super.destroyObject(p); + } +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/face/service/FaceEngineService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/face/service/FaceEngineService.java new file mode 100644 index 0000000..419d0d1 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/face/service/FaceEngineService.java @@ -0,0 +1,25 @@ +package org.jeecg.face.service; + + +import com.arcsoft.face.FaceInfo; +import com.arcsoft.face.toolkit.ImageInfo; +import org.jeecg.user.vo.UserCompareInfo; +import org.jeecg.user.vo.UserFaceInfo; + +import java.util.List; + + +public interface FaceEngineService { + + // 人脸数据键名 + String FACE_CACHE = "FACE_CACHE"; + + // 食堂支付人脸数据键名 + String FACE_CACHE_PAY = "FACE_CACHE_PAY"; + + List detectFaces(ImageInfo imageInfo); + + byte[] extractFaceFeature(ImageInfo imageInfo, FaceInfo faceInfo); + + List faceRecognition(byte[] faceFeature, List userInfoList, float passRate); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/face/service/impl/FaceEngineServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/face/service/impl/FaceEngineServiceImpl.java new file mode 100644 index 0000000..22c1662 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/face/service/impl/FaceEngineServiceImpl.java @@ -0,0 +1,250 @@ +package org.jeecg.face.service.impl; + +import com.arcsoft.face.*; +import com.arcsoft.face.enums.DetectMode; +import com.arcsoft.face.enums.DetectOrient; +import com.arcsoft.face.enums.ExtractType; +import com.arcsoft.face.toolkit.ImageInfo; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.pool2.impl.GenericObjectPool; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.jeecg.common.executor.CustomExecutors; +import org.jeecg.face.factory.FaceEngineFactory; +import org.jeecg.face.service.FaceEngineService; +import org.jeecg.system.service.UserFaceCacheService; +import org.jeecg.user.vo.UserCompareInfo; +import org.jeecg.user.vo.UserFaceInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + + +@Service("faceEngineService") +@Slf4j +public class FaceEngineServiceImpl implements FaceEngineService { + + @Value(value = "${face.appId}") + private String appId; + @Value(value = "${face.sdkKey}") + private String sdkKey; + @Value(value = "${face.activeKey}") + private String activeKey; + @Value(value = "${face.libPath}") + private String libPath; + @Value(value = "${detect.pool.size}") + public Integer detectPooSize; + @Value(value = "${compare.pool.size}") + public Integer comparePooSize; + + private ExecutorService compareExecutorService; + + //通用人脸识别引擎池 + private GenericObjectPool faceEngineGeneralPool; + + //人脸比对引擎池 + private GenericObjectPool faceEngineComparePool; + + @Autowired + private UserFaceCacheService userFaceCacheService; + + //初始化注册人脸,注册到本地内存 + @PostConstruct + public void initFace() { + //查询所有人脸信息 + CustomExecutors.execute(() -> { + //存放到redis + userFaceCacheService.init(); + }); + log.info("正在初始化人脸数据中~"); + } + + @PostConstruct + public void init() { + log.info("初始化人脸特征比较线程池及人脸引擎---开始"); + GenericObjectPoolConfig detectPoolConfig = new GenericObjectPoolConfig(); + detectPoolConfig.setMaxIdle(detectPooSize); + detectPoolConfig.setMaxTotal(detectPooSize); + detectPoolConfig.setMinIdle(detectPooSize); + detectPoolConfig.setLifo(false); + + //引擎配置 + EngineConfiguration detectCfg = new EngineConfiguration(); + detectCfg.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);//图片检测模式,如果是连续帧的视频流图片,那么改成VIDEO模式 + detectCfg.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);//人脸旋转角度 + detectCfg.setDetectFaceMaxNum(10); + + // 功能配置 + FunctionConfiguration detectFunctionCfg = new FunctionConfiguration(); + detectFunctionCfg.setSupportFaceDetect(true);//开启人脸检测功能 + detectFunctionCfg.setSupportFaceRecognition(true);//开启人脸识别功能 + detectFunctionCfg.setSupportAge(true);//开启年龄检测功能 + detectFunctionCfg.setSupportGender(true);//开启性别检测功能 + detectFunctionCfg.setSupportLiveness(true);//开启活体检测功能 + detectFunctionCfg.setSupportIRLiveness(true); + detectFunctionCfg.setSupportImageQuality(true); + detectFunctionCfg.setSupportMaskDetect(true); + detectFunctionCfg.setSupportUpdateFaceData(true); + detectCfg.setFunctionConfiguration(detectFunctionCfg); + + faceEngineGeneralPool = new GenericObjectPool(new FaceEngineFactory(System.getProperty("user.dir")+ File.separator+libPath, appId, sdkKey, activeKey, detectCfg), detectPoolConfig);//底层库算法对象池 + + //初始化特征比较线程池 + GenericObjectPoolConfig comparePoolConfig = new GenericObjectPoolConfig(); + comparePoolConfig.setMaxIdle(comparePooSize); + comparePoolConfig.setMaxTotal(comparePooSize); + comparePoolConfig.setMinIdle(comparePooSize); + comparePoolConfig.setLifo(false); + EngineConfiguration compareCfg = new EngineConfiguration(); + FunctionConfiguration compareFunctionCfg = new FunctionConfiguration(); + compareFunctionCfg.setSupportFaceRecognition(true);//开启人脸识别功能 + compareCfg.setFunctionConfiguration(compareFunctionCfg); + compareCfg.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);//图片检测模式,如果是连续帧的视频流图片,那么改成VIDEO模式 + compareCfg.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);//人脸旋转角度 + compareExecutorService = Executors.newFixedThreadPool(comparePooSize); + faceEngineComparePool = new GenericObjectPool(new FaceEngineFactory(System.getProperty("user.dir")+ File.separator+libPath, appId, sdkKey, activeKey, compareCfg), comparePoolConfig); + log.info("初始化人脸特征比较线程池及人脸引擎---完成"); + } + + private class CompareFaceTask implements Callable> { + private List userInfoList; + private FaceFeature targetFaceFeature; + private float passRate; + + public CompareFaceTask(List userInfoList, FaceFeature targetFaceFeature, float passRate) { + this.userInfoList = userInfoList; + this.targetFaceFeature = targetFaceFeature; + this.passRate = passRate; + } + + @Override + public List call() throws Exception { + FaceEngine faceEngine = null; + List resultUserInfoList = Lists.newLinkedList();//识别到的人脸列表 + try { + faceEngine = faceEngineComparePool.borrowObject(); + for (UserFaceInfo userInfo : userInfoList) { + FaceFeature sourceFaceFeature = new FaceFeature(); + sourceFaceFeature.setFeatureData(userInfo.getFaceFeature()); + FaceSimilar faceSimilar = new FaceSimilar(); + faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar); + if (faceSimilar.getScore() > passRate) {//相似值大于配置预期,加入到识别到人脸的列表 + UserCompareInfo info = new UserCompareInfo(); + info.setUserId(userInfo.getUserId()); + info.setSimilar(faceSimilar.getScore()); + resultUserInfoList.add(info); + } + } + } catch (Exception e) { + log.error("", e); + } finally { + if (faceEngine != null) { + faceEngineComparePool.returnObject(faceEngine); + } + } + return resultUserInfoList; + } + + } + + @Override + public List faceRecognition(byte[] faceFeature, List userInfoList, float passRate) { + List resultUserInfoList = Lists.newLinkedList();//识别到的人脸列表 + + FaceFeature targetFaceFeature = new FaceFeature(); + targetFaceFeature.setFeatureData(faceFeature); + + List> faceUserInfoPartList = Lists.partition(userInfoList, 1000);//分成1000一组,多线程处理 + CompletionService> completionService = new ExecutorCompletionService(compareExecutorService); + for (List part : faceUserInfoPartList) { + completionService.submit(new CompareFaceTask(part, targetFaceFeature, passRate)); + } + for (int i = 0; i < faceUserInfoPartList.size(); i++) { + List faceUserInfoList = null; + try { + faceUserInfoList = completionService.take().get(); + } catch (InterruptedException | ExecutionException e) { + } + if (CollectionUtils.isNotEmpty(userInfoList)) { + resultUserInfoList.addAll(faceUserInfoList); + } + } + + resultUserInfoList.sort((h1, h2) -> h2.getSimilar().compareTo(h1.getSimilar()));//从大到小排序 + + return resultUserInfoList; + } + + /** + * 人脸特征 + * + * @param imageInfo + * @return + */ + @Override + public byte[] extractFaceFeature(ImageInfo imageInfo, FaceInfo faceInfo) { + + FaceEngine faceEngine = null; + try { + faceEngine = faceEngineGeneralPool.borrowObject(); + if (faceEngine == null) { + log.error("获取引擎失败"); + return null; + } + + FaceFeature faceFeature = new FaceFeature(); + //提取人脸特征 + int errorCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfo, ExtractType.REGISTER, 0,faceFeature); + if (errorCode == 0) { + return faceFeature.getFeatureData(); + } else { + log.error("特征提取失败,errorCode:" + errorCode); + } + } catch (Exception e) { + log.error("", e); + } finally { + if (faceEngine != null) { + //释放引擎对象 + faceEngineGeneralPool.returnObject(faceEngine); + } + } + return null; + } + + @Override + public List detectFaces(ImageInfo imageInfo) { + FaceEngine faceEngine = null; + try { + faceEngine = faceEngineGeneralPool.borrowObject(); + if (faceEngine == null) { + log.error("获取引擎失败"); + return null; + } + //人脸检测得到人脸列表 + List faceInfoList = new ArrayList(); + + //人脸检测 + int errorCode = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList); + if (errorCode == 0) { + return faceInfoList; + } else { + log.error("人脸检测失败,errorCode:" + errorCode); + } + } catch (Exception e) { + log.error("", e); + } finally { + if (faceEngine != null) { + //释放引擎对象 + faceEngineGeneralPool.returnObject(faceEngine); + } + } + return null; + } +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/pay/controller/StPayController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/pay/controller/StPayController.java new file mode 100644 index 0000000..8b254f0 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/pay/controller/StPayController.java @@ -0,0 +1,429 @@ +package org.jeecg.pay.controller; + +import cn.hutool.core.date.DateUtil; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.aspect.annotation.AutoLog; +import org.jeecg.common.system.util.JwtUtil; +import org.jeecg.common.util.RedisUtil; +import org.jeecg.equipment.entity.StEquipment; +import org.jeecg.equipment.service.IStEquipmentService; +import org.jeecg.payment.entity.StBbzxOrderInfo; +import org.jeecg.payment.listUtils.MapUtil; +import org.jeecg.payment.service.IStBbzxOrderInfoService; +import org.jeecg.payment.service.IStClientMemberBalanceService; +import org.jeecg.payment.service.IStClientMemberLevelDeductRuleService; +import org.jeecg.payment.vo.*; +import org.jeecg.restaurant.entity.SlBaseDinnerTime; +import org.jeecg.restaurant.entity.SlBaseRestaurant; +import org.jeecg.restaurant.service.ISlBaseDinnerTimeService; +import org.jeecg.restaurant.service.ISlBaseRestaurantService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +/** + 食堂API接口支付相关 + * @throws + * @since + */ +@Tag(name = "0.食堂API接口支付相关 ") +@RestController +@RequestMapping("/zhstapi/pay/") +@Slf4j +public class StPayController { +// @Autowired +// private IStBaseService stBaseService; + @Autowired + private ISlBaseRestaurantService slBaseRestaurantService; + @Autowired + private RedisUtil redisUtil; + @Resource + private IStClientMemberBalanceService iStClientMemberBalanceService; + @Resource + private IStBbzxOrderInfoService iStBbzxOrderInfoService; + @Resource + private IStClientMemberLevelDeductRuleService iStClientMemberLevelDeductRuleService; + @Autowired + private IStEquipmentService iStEquipmentService; + + @Resource + private ISlBaseDinnerTimeService slBaseDinnerTimeService; + + /** + * 根据设备号获取token + */ + @Operation(summary = "根据设备号获取token", description = "根据设备号获取token") + @GetMapping("/getEquipmentToken") + @AutoLog(value = "根据设备号获取token") + public Result getEquipmentToken(@RequestParam String qrcodeId) { + if(StringUtils.isNotBlank(qrcodeId)){ + LambdaQueryWrapper stEquipmentLambdaQueryWrapper = new LambdaQueryWrapper<>(); + stEquipmentLambdaQueryWrapper.eq(StEquipment::getEquipmentCode, qrcodeId); + stEquipmentLambdaQueryWrapper.orderByDesc(StEquipment::getCreateTime); + List stEquipmentList = iStEquipmentService.list(stEquipmentLambdaQueryWrapper); + if(CollectionUtils.isEmpty(stEquipmentList)){ + log.error("根据设备号获取token,异常:设备编号不存在,qrcodeId="+qrcodeId); + return Result.error("设备编号不存在"); + } else { +// try { +// //查询设备编码 +// StEquipment stEquipment = stEquipmentList.get(0); +// String canteenId = slBaseRestaurantService.getCanteenId(stEquipment.getCanteenId()); +// SlBaseDinnerTime slBaseDinnerTime = stBaseService.getCanteenDinnerType(canteenId); +// if(null != slBaseDinnerTime && StringUtils.isNotBlank(slBaseDinnerTime.getDinnerType())){ +// String timesName = slBaseDinnerTime.getDinnerType(); +// //异步上传设备统计信息 +// iStRfidDeviceLogService.syncUploadDeviceInfo(qrcodeId,canteenId,timesName,null,slBaseDinnerTime,null); +// } +// } catch (Exception e) { +// e.printStackTrace(); +// } + } + } + String token = JwtUtil.signUserId("0000000000000000001", qrcodeId, qrcodeId,"4"); + return Result.OK(token); + } + + + @Operation(summary = "", description = "提供根据食堂ID获取食堂配置信息") + @GetMapping(value = "/getDinnerType/byCanteenId") + @AutoLog(value = "提供根据食堂ID获取食堂配置信息") + public Result getCanteenInfoByCanteenId(@RequestParam(name = "restId", required = true) String restId) { + SlBaseRestaurant slBaseRestaurant = slBaseRestaurantService.getById(restId); + if(null == slBaseRestaurant){ + return Result.error("食堂未找到,请检查参数"); + } + //扣费规则(1:固定扣费,2:固定+补贴扣费,3:按量扣费) + StClientMemberLevelDeductRuleVo levelDeductRuleVo = iStClientMemberLevelDeductRuleService.getDeductRuleByCanteenId(restId); + if(null == levelDeductRuleVo || null == levelDeductRuleVo.getDeductionType()){ + return Result.error("食堂未设置扣费规则"); + } + levelDeductRuleVo.setIsOverdraftDine(slBaseRestaurant.getIsOverdraftDine()); + levelDeductRuleVo.setIsExternalDineFlag(slBaseRestaurant.getIsExternalDine()); + return Result.OK(levelDeductRuleVo); + } + + + + + @Operation(summary = "获取用户余额信息", description = "获取用户余额信息") + @AutoLog(value = "获取用户余额信息") + @PostMapping(value = "/getUserBalance") + public Result getUserBalance(@RequestBody StRfidVo stRfidVo){ + Result stMiniMineBalanceVoResult = iStClientMemberBalanceService.getUserBalance(stRfidVo.getUserId()); + StMiniMineBalanceVo stMiniMineBalanceVo = new StMiniMineBalanceVo(); + if(stMiniMineBalanceVoResult.isSuccess()){ + stMiniMineBalanceVo = stMiniMineBalanceVoResult.getResult(); + }else { + return Result.error(stMiniMineBalanceVoResult.getMessage()); + } + if(null == stMiniMineBalanceVo){ + return Result.error("用户账户数据异常"); + } + //根据时间判断当前餐次 + SlBaseDinnerTime slBaseDinnerTime = slBaseDinnerTimeService.getCanteenDinnerType(stRfidVo.getRestId()); + String timesName = slBaseDinnerTime.getDinnerType(); + //从redis查询用户本餐次是否已经扣款(第一次扣款模式下扣费节点会根据,根据用户ID+日期+餐次为key,保存用户已扣费标记) + String key = stMiniMineBalanceVo.getMemberId()+ "_" + DateUtil.today() + "_" + timesName; + Object flag = redisUtil.hget(MapUtil.USER_ORDER_FLAG, key); + if(null != flag){ + stMiniMineBalanceVo.setSecondReminder(true); + } else { + stMiniMineBalanceVo.setSecondReminder(false); + } + stMiniMineBalanceVo.setUserId(stRfidVo.getUserId()); + stMiniMineBalanceVoResult.setResult(stMiniMineBalanceVo); + return stMiniMineBalanceVoResult; + } + + + + @Operation(summary = "获取指定食堂的当前餐次", description = "获取指定食堂的当前餐次") + @GetMapping(value = "/getCanteenDinnerType") + @AutoLog(value = "获取指定食堂的当前餐次") + public Result getCanteenDinnerType(@RequestParam(name = "restId", required = true) String restId) { + log.error("/getCanteenDinnerType 获取指定食堂的当前餐次,canteenId={}",restId); + Map map = new HashMap<>(); + map.put("dinnerType", slBaseDinnerTimeService.getCanteenDinnerType(restId)); + log.error("/getCanteenDinnerType 获取指定食堂的当前餐次,canteenId={},返回值{}",restId,JSONObject.toJSONString(map)); + return Result.OK(map); + } + + + + + @Operation(summary = "离线人脸扣费v2", description = "离线人脸扣费v2") + @PostMapping(value = "/payUser/offline") + @AutoLog(value = "离线人脸扣费v2") + public Result bindUserOfflineV2(@RequestBody StRfidoffLineVo stRfidoffLineVo) { + String deviceCode = stRfidoffLineVo.getDeviceCode(); + String restId = stRfidoffLineVo.getRestId(); + List voListAll = stRfidoffLineVo.getVoList(); + if(CollectionUtils.isNotEmpty(voListAll)){ + //扣费规则(1:固定扣费,2:固定+补贴扣费,3:按量扣费) + StClientMemberLevelDeductRuleVo levelDeductRuleVo = iStClientMemberLevelDeductRuleService.getDeductRuleByCanteenId(restId); + if(null == levelDeductRuleVo){ + log.error("食堂扣费规则未设置"+restId); + return Result.error("食堂扣费规则未设置"+restId); + } + //加异步处理 + CompletableFuture.runAsync(() -> { + + //将扣费信息按照餐次分组 + Map> stringListMap = iStClientMemberLevelDeductRuleService.groupByMeal(voListAll,restId); + for (Map.Entry> entry : stringListMap.entrySet()) { + System.out.println("餐次: " + entry.getKey() + ", 数量: " + entry.getValue().size()); + List voList = entry.getValue(); + voList.forEach(stRfidVo->{ + stRfidVo.setDeviceCode(deviceCode); + stRfidVo.setRestId(restId); + log.error("/payUser/offline 离线人脸扣费,bindUser={}", JSONObject.toJSONString(stRfidVo)); + if(StringUtils.isNotBlank(stRfidVo.getPayId())){ + StBbzxOrderInfo stBbzxOrderInfo = iStBbzxOrderInfoService.getSwBbzxOrderInfoByPayId(stRfidVo.getPayId()); + if(null != stBbzxOrderInfo){ + log.error("闸机记录的支付唯一id已经有支付信息,stRfidVo.getPayId()={},已经存在的数据订单编号={}",stRfidVo.getPayId(),stBbzxOrderInfo.getOrderNo()); + //跳过本次循环执行下次循环 + return; + } + } + //当前餐次 + String timesName = entry.getKey(); + //实际扣减额 + BigDecimal spendMoney = BigDecimal.ZERO;//用户实际扣费金额 + BigDecimal rewardDeduction = BigDecimal.ZERO;//用户扣费补贴金额 + BigDecimal totalDeduction = BigDecimal.ZERO;//原订单总金额 + //毫秒级时间戳 + Date dateTime = new Date(Long.parseLong(stRfidVo.getDateTime())); + String userId = stRfidVo.getUserId(); + Result userBalanceResult = iStClientMemberBalanceService.getUserBalance(userId); + StMiniMineBalanceVo stMiniMineBalanceVo = new StMiniMineBalanceVo(); + if(userBalanceResult.isSuccess()){ + stMiniMineBalanceVo = userBalanceResult.getResult(); + } else { + log.error("离线人脸扣费v2,根据userId"+userId+"查询用户钱包数据"+userBalanceResult.getMessage()); +// StClientMemberSpendDataVO stClientMemberSpendDataVO = iStClientMemberBalanceService.fixedFee(levelDeductRuleVo,timesName,totalDeduction,spendMoney,rewardDeduction,stMiniMineBalanceVo.getUserInnerType()); +// spendMoney = stClientMemberSpendDataVO.getSpendMoney(); +// stRfidVo.setSpendMoney(spendMoney); + iStBbzxOrderInfoService.buildOrderInfoNew(dateTime,stRfidVo.getRestId(),timesName,deviceCode,stRfidVo.getUserId(),null,null, + 1,levelDeductRuleVo,"离线人脸扣费v2订单支付异常,根据userId:"+userId+",查询用户钱包数据:"+userBalanceResult.getMessage(),stRfidVo); + return; + } + //计算本次扣费金额(一次扣费模式返回实际扣费金额,按量扣费直接返回0) + StClientMemberSpendDataVO stClientMemberSpendDataVO = iStClientMemberBalanceService.fixedFee(levelDeductRuleVo,timesName,totalDeduction,spendMoney,rewardDeduction,stMiniMineBalanceVo.getUserInnerType()); + spendMoney = stClientMemberSpendDataVO.getSpendMoney(); + stMiniMineBalanceVo.setAmountThis(spendMoney); + String data = iStClientMemberLevelDeductRuleService.syncMemberSpendOffLine(dateTime,restId,userId,stRfidVo,timesName, stMiniMineBalanceVo,levelDeductRuleVo); + if(StringUtils.isNotBlank(data)) { + log.error("扣费操作失败: {}", data); + return; + } + }); + + + } + + }); + } + return Result.OK("扣费成功"); + } + + /** + * 离线人脸扣费v3 ,加自定义金额、卡号扣款 + * @param stRfidoffLineVo + * @return + */ + @Operation(summary = "离线人脸扣费v3", description = "离线人脸扣费v2") + @PostMapping(value = "/payUser/offline/v3") + @AutoLog(value = "离线人脸扣费v3") + public Result bindUserOfflineV3(@RequestBody StRfidoffLineVo stRfidoffLineVo) { + return iStClientMemberLevelDeductRuleService.bindUserOfflineCardCode(stRfidoffLineVo); + } + + + /** + * 在线扣费 + * @return + */ + @Operation(summary = "在线扣费", description = "在线扣费") + @PostMapping(value = "/payUser/online") + @AutoLog(value = "在线扣费") + public Result bindUserOnline(@RequestBody StRfidVo stRfidVo) { + StRfidoffLineVo stRfidoffLineVo = new StRfidoffLineVo(); + stRfidoffLineVo.setDeviceCode(stRfidVo.getDeviceCode()); + stRfidoffLineVo.setVoList(Collections.singletonList(stRfidVo)); + stRfidoffLineVo.setRestId(stRfidVo.getRestId()); + return iStClientMemberLevelDeductRuleService.bindUserOfflineCardCode(stRfidoffLineVo); + } + + + + /** + * 补偿人脸扣费闸机离线扣费方法(只考虑透支消费版本) + * @param list + * @return + */ + @Operation(summary = "补偿人脸扣费闸机离线扣费方法(只考虑透支消费版本)", description = "补偿人脸扣费闸机离线扣费方法(只考虑透支消费版本)") + @PostMapping(value = "/payUser/memberSpendOfflineCompensate") + @AutoLog(value = "补偿人脸扣费闸机离线扣费方法(只考虑透支消费版本)") + public Result> memberSpendOfflineCompensate(@RequestBody List list) { + return Result.OK(iStClientMemberBalanceService.memberSpendOfflineCompensate(list)); + } + + + + + + //============================================弃用接口======================== + + @Operation(summary = "获取用户二次扣费提示性-弃用前端本地保存二次扣费标记扣费数据批量提交", description = "获取用户二次扣费提示性-弃用前端本地保存二次扣费标记扣费数据批量提交") + @AutoLog(value = "获取用户二次扣费提示性-弃用前端本地保存二次扣费标记扣费数据批量提交") + @PostMapping(value = "/getUserBalance/second") + public Result getUserBalanceSecond(@RequestBody StRfidVo stRfidVo){ + //根据时间判断当前餐次 + SlBaseDinnerTime slBaseDinnerTime = slBaseDinnerTimeService.getCanteenDinnerType(stRfidVo.getRestId()); + String timesName = slBaseDinnerTime.getDinnerType(); + String userId = stRfidVo.getUserId(); + Result userBalanceResult = iStClientMemberBalanceService.getUserBalance(userId); + StMiniMineBalanceVo stMiniMineBalanceVo = new StMiniMineBalanceVo(); + if(userBalanceResult.isSuccess()){ + stMiniMineBalanceVo = userBalanceResult.getResult(); + }else { + return Result.error(userBalanceResult.getMessage()); + } + if(null == stMiniMineBalanceVo){ + return Result.error("用户账户数据异常"); + } + //从redis查询用户本餐次是否已经扣款(第一次扣款模式下扣费节点会根据,根据用户ID+日期+餐次为key,保存用户已扣费标记) + String key = stMiniMineBalanceVo.getMemberId()+ "_" + DateUtil.today() + "_" + timesName; + Object flag = redisUtil.hget(MapUtil.USER_ORDER_FLAG, key); + if(null != flag){ + return Result.OK(true); + } + return Result.OK(false); + } + + + + @Operation(summary = "人脸扣费-弃用前端本地进行扣费批量提交扣费数据", description = "人脸扣费-弃用前端本地进行扣费批量提交扣费数据") + @PostMapping(value = "/payUser") + @AutoLog(value = "人脸扣费-弃用前端本地进行扣费批量提交扣费数据") + public Result bindUser(@RequestBody StRfidVo stRfidVo) { + log.error("/payUser 人脸扣费,bindUser={}", JSONObject.toJSONString(stRfidVo)); + if(StringUtils.isNotBlank(stRfidVo.getPayId())){ + StBbzxOrderInfo stBbzxOrderInfo = iStBbzxOrderInfoService.getSwBbzxOrderInfoByPayId(stRfidVo.getPayId()); + if(null != stBbzxOrderInfo){ + log.error("闸机记录的支付唯一id已经有支付信息,stRfidVo.getPayId()={},已经存在的数据订单编号={}",stRfidVo.getPayId(),stBbzxOrderInfo.getOrderNo()); + return Result.error("本次已支付,请勿重复支付!"); + } + } + //根据时间判断当前餐次 + SlBaseDinnerTime slBaseDinnerTime = slBaseDinnerTimeService.getCanteenDinnerType(stRfidVo.getRestId()); + String timesName = slBaseDinnerTime.getDinnerType(); + + String userId = stRfidVo.getUserId(); + String restId = stRfidVo.getRestId(); + StClientMemberSpendVO stClientMemberSpendVO = new StClientMemberSpendVO(); + //扣费规则(1:固定扣费,2:固定+补贴扣费,3:按量扣费) + StClientMemberLevelDeductRuleVo levelDeductRuleVo = iStClientMemberLevelDeductRuleService.getDeductRuleByCanteenId(restId); + if(null == levelDeductRuleVo){ + log.error("食堂扣费规则未设置"+restId); + iStBbzxOrderInfoService.buildOrderInfoNew(null,restId,timesName,stRfidVo.getDeviceCode(),stRfidVo.getUserId(),null, + null,1,null,"食堂扣费规则未设置:"+restId,stRfidVo); + return Result.error("食堂扣费规则设置异常,请检查!"); + } + Result userBalanceResult = iStClientMemberBalanceService.getUserBalance(userId); + StMiniMineBalanceVo stMiniMineBalanceVo = new StMiniMineBalanceVo(); + if(userBalanceResult.isSuccess()){ + stMiniMineBalanceVo = userBalanceResult.getResult(); + } else { + iStBbzxOrderInfoService.buildOrderInfoNew(null,restId,timesName,stRfidVo.getDeviceCode(),stRfidVo.getUserId(),null, + null,1,null,"用户钱包异常:"+userBalanceResult.getMessage(),stRfidVo); + return Result.error(userBalanceResult.getMessage()); + } + stClientMemberSpendVO.setRealname(stMiniMineBalanceVo.getRealname()); + stClientMemberSpendVO.setWorkNo(stMiniMineBalanceVo.getWorkNo()); + stClientMemberSpendVO.setPayRealname(stMiniMineBalanceVo.getPayRealname()); + stClientMemberSpendVO.setPayWorkNo(stMiniMineBalanceVo.getPayWorkNo()); + SlBaseRestaurant slBaseRestaurant = slBaseRestaurantService.getById(restId); + if (null == slBaseRestaurant) { + iStBbzxOrderInfoService.buildOrderInfoNew(null,restId,timesName,stRfidVo.getDeviceCode(),stRfidVo.getUserId(),null, + null,1,null,"食堂不存在:"+restId,stRfidVo); + return Result.error("食堂不存在"); + } + //"是否支持透支消费(0:不支持,1:支持)" + Integer isOverdraftDine = slBaseRestaurant.getIsOverdraftDine(); + + //实际扣减额 + BigDecimal spendMoney = BigDecimal.ZERO;//用户实际扣费金额 + BigDecimal rewardDeduction = BigDecimal.ZERO;//用户扣费补贴金额 + BigDecimal totalDeduction = BigDecimal.ZERO;//原订单总金额 + //计算本次扣费金额(一次扣费模式返回实际扣费金额,按量扣费直接返回0) + StClientMemberSpendDataVO stClientMemberSpendDataVO = iStClientMemberBalanceService.fixedFee(levelDeductRuleVo,timesName,totalDeduction,spendMoney,rewardDeduction,stMiniMineBalanceVo.getUserInnerType()); + spendMoney = stClientMemberSpendDataVO.getSpendMoney(); + rewardDeduction = stClientMemberSpendDataVO.getRewardDeduction(); + //判断食堂是否支持透支消费(此处的金额需要经过扣费规则计算实际扣费金额) + BigDecimal total = new BigDecimal(stMiniMineBalanceVo.getBalance()); +// if(levelDeductRuleVo.getDeductionType() != 3){//一次扣费模式,1进行预扣费计算 + 2余额不足校验 + if (spendMoney.compareTo(total) > -1 && isOverdraftDine == 0) { + return Result.error("用户余额不足!"); + } + //2:进行预扣费计算 + total = total.subtract(spendMoney).setScale(2, BigDecimal.ROUND_HALF_UP); + + stClientMemberSpendVO.setSuccess(true); + stClientMemberSpendVO.setPreferentialPrice(rewardDeduction); + stClientMemberSpendVO.setSpendMoney(spendMoney); + stClientMemberSpendVO.setBalance(total); + //==============================================异步异步生成订单和进行实际扣费流程操作========================================================= + StMiniMineBalanceVo finalStMiniMineBalanceVo = stMiniMineBalanceVo; + finalStMiniMineBalanceVo.setAmountThis(spendMoney); +// CompletableFuture.runAsync(() -> { + String data = syncMemberSpend(null,restId,userId,stRfidVo,timesName, finalStMiniMineBalanceVo); + if(StringUtils.isNotBlank(data)) { + return Result.error(data,stClientMemberSpendVO); + } +// }); + //==============================================异步异步生成订单和进行实际扣费流程操作========================================================= + return Result.OK("扣费成功",stClientMemberSpendVO); + } + + + /** + * 异步生成订单和进行实际扣费流程 + * @param dateTime 订单时间 + * @param restId + * @param userId + * @param stRfidVo + * @param timesName + */ + private String syncMemberSpend(Date dateTime,String restId,String userId,StRfidVo stRfidVo,String timesName,StMiniMineBalanceVo miniMineBalanceVo){ + //统一生成订单 + StBbzxOrderInfo stBbzxOrderInfo = iStBbzxOrderInfoService.crateOrderInfo(dateTime,restId,userId,stRfidVo.getDeviceCode(),timesName, stRfidVo); + if(null == stBbzxOrderInfo) { + log.error("食堂扣费规则设置异常,请检查!"); + return "食堂扣费规则设置异常,请检查!"; + } + StClientMemberSpendDTO stClientMemberSpendDTO = new StClientMemberSpendDTO(); + stClientMemberSpendDTO.setCanteenId(stRfidVo.getRestId()); + stClientMemberSpendDTO.setOrderNo(stBbzxOrderInfo.getOrderNo()); + stClientMemberSpendDTO.setId(miniMineBalanceVo.getMemberId());//钱包用户id + stClientMemberSpendDTO.setMiniMineBalanceVo(miniMineBalanceVo);//钱包用户 + StClientMemberSpendVO stClientMemberSpendVO = iStClientMemberBalanceService.memberSpendV3(stClientMemberSpendDTO); + if(null != stClientMemberSpendVO && !stClientMemberSpendVO.getSuccess()){ + return stClientMemberSpendVO.getMsg(); + } else { + return null; + } + } +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/controller/StBbzxOrderInfoController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/controller/StBbzxOrderInfoController.java new file mode 100644 index 0000000..ed7c5b9 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/controller/StBbzxOrderInfoController.java @@ -0,0 +1,394 @@ + +package org.jeecg.payment.controller; + + + +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.payment.dto.StAmountSpentEveryMemberDTO; +import org.jeecg.payment.dto.StClientMemberTransactionRecordDTO; +import org.jeecg.payment.service.IStBbzxOrderInfoService; +import org.jeecg.payment.service.IStClientMemberTransactionRecordService; +import org.jeecg.payment.utils.ExcelUtils; +import org.jeecg.payment.utils.GenericSumUtil; +import org.jeecg.payment.vo.StClientMemberTransactionRecordVO; +import org.jeecg.payment.vo.StatisticsTheAmountSpentVo; +import org.jeecg.restaurant.vo.StatisticsBusinessVo; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 订单 控制器 + */ +@RestController +@RequestMapping("/payment/swbbzxorderinfo") +@Tag(name = "报表中心-店内订单") +@Slf4j +public class StBbzxOrderInfoController { + + @Resource + private IStBbzxOrderInfoService iStBbzxOrderInfoService; + + @Resource + private IStClientMemberTransactionRecordService iStClientMemberTransactionRecordService; + + /** + * 食堂消费记录统计 + * @return 个人消费记录统计 + */ + @PostMapping("/statisticsTheAmountSpentList") + @Operation(summary = "食堂消费记录统计") + public Result getStatisticsTheAmountSpentList(@RequestBody StAmountSpentEveryMemberDTO dto) { + //处理时间 + if(CollectionUtils.isNotEmpty(dto.getDates())){ + dto.setStartDate(dto.getDates().get(0)); + dto.setEndDate(dto.getDates().get(1)); + } + + return Result.OK(iStBbzxOrderInfoService.getStatisticsTheAmountSpentList(dto.getCanteenId(), dto.getStartDate(), dto.getEndDate(), dto.getPageNo(), dto.getPageSize())); + } + + /** + * 食堂消费记录统计总计 + * @param startDate 开始时间 + * @param endDate 结束时间 + * @return 食堂消费记录统计总计 + */ + @GetMapping("/statisticsTheAmountSpentList/sum") + @Operation(summary = "食堂消费记录统计总计") + public Result getStatisticsTheAmountSpentListSum(@RequestParam(value = "canteenId", required = false) String canteenId, + @RequestParam(value = "startDate", required = false) String startDate, + @RequestParam(value = "endDate", required = false) String endDate) { + List dataList = iStBbzxOrderInfoService.getStatisticsTheAmountSpentList(canteenId, startDate, endDate); + StatisticsTheAmountSpentVo sumResult = GenericSumUtil.sumList(dataList, StatisticsTheAmountSpentVo.class); + sumResult.setRestName("总计"); + sumResult.setEatDay(startDate+"~"+endDate); + return Result.OK(sumResult); + } + + + /** + * 食堂消费记录统计-导出 + */ + @GetMapping("/statisticsTheAmountSpentList/export") + public void statisticsTheAmountSpentListExport(HttpServletResponse response, @RequestParam(value = "canteenId", required = false) String canteenId, + @RequestParam(value = "startDate", required = false) String startDate, + @RequestParam(value = "endDate", required = false) String endDate) { + List statisticsTheAmountSpentList = iStBbzxOrderInfoService.getStatisticsTheAmountSpentList(canteenId, startDate, endDate); + if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(statisticsTheAmountSpentList)){ + StatisticsTheAmountSpentVo sumResult = GenericSumUtil.sumList(statisticsTheAmountSpentList, StatisticsTheAmountSpentVo.class); + sumResult.setRestName("总计"); + sumResult.setEatDay(startDate+"~"+endDate); + statisticsTheAmountSpentList.add(sumResult); + String exportFields = "restName|食堂名称,customerPrice|消费总额(元),totalCount|消费人次,totalNum|消费人数," + + "breakfastAmount|早餐消费金额(元),breakfastPerson|早餐消费次数,lunchAmount|午餐消费金额(元),lunchPerson|午餐消费次数,dinnerAmount|晚餐消费金额(元),dinnerPerson|晚餐消费次数" + + ",otherAmount|其他消费金额(元),otherPerson|其他消费次数,orderPrice|结算金额(元),eatDay|消费日期"; + ExcelUtils.export(response, "食堂消费统计", statisticsTheAmountSpentList, exportFields, "yyyy-MM-dd HH:mm",true); + } + } + + + /** + * 个人消费记录统计 + * @return 个人消费记录统计 + */ + @PostMapping("/statisticsTheAmountSpentEveryMember") + @Operation(summary = "个人消费金额统计") + public Result getStatisticsTheAmountSpentEveryMember(@RequestBody StAmountSpentEveryMemberDTO dto) { + String secondDepartId = null; + String thirdDepartId = null; + if(StringUtils.isNotEmpty(dto.getSecondThirdDepartIds())){ + String[] split = dto.getSecondThirdDepartIds().split(","); + secondDepartId = split[0]; + if(split.length > 1){ + thirdDepartId = split[1]; + } + } + //处理时间 + if(CollectionUtils.isNotEmpty(dto.getDates())){ + dto.setStartDate(dto.getDates().get(0)); + dto.setEndDate(dto.getDates().get(1)); + } + + return Result.OK(iStBbzxOrderInfoService.statisticsTheAmountSpentEveryMember(dto.getCanteenId(),dto.getName(),dto.getPayName(),dto.getStartDate(),dto.getEndDate(),secondDepartId,thirdDepartId,dto.getPageNo(),dto.getPageSize())); + } + + /** + * 个人消费记录统计总计 + * @param startDate 开始时间 + * @param endDate 结束时间 + * @param secondThirdDepartIds 二/三级部门id + * @return 个人消费记录统计总计 + */ + @GetMapping("/statisticsTheAmountSpentEveryMember/sum") + @Operation(summary = "个人消费记录统计总计") + public Result getStatisticsTheAmountSpentEveryMemberSum(@RequestParam(value = "canteenId", required = false) String canteenId, + @RequestParam(value = "secondThirdDepartIds", required = false) String secondThirdDepartIds, + @RequestParam(value = "startDate", required = false) String startDate, + @RequestParam(value = "endDate", required = false) String endDate, + @RequestParam(value = "name", required = false) String name, + @RequestParam(value = "payName", required = false) String payName) { + String secondDepartId = null; + String thirdDepartId = null; + if(StringUtils.isNotEmpty(secondThirdDepartIds)){ + String[] split = secondThirdDepartIds.split(","); + secondDepartId = split[0]; + if(split.length > 1){ + thirdDepartId = split[1]; + } + } + List statisticsTheAmountSpentList = iStBbzxOrderInfoService.statisticsTheAmountSpentEveryMember(canteenId,name,payName,startDate, endDate,secondDepartId, thirdDepartId); + StatisticsTheAmountSpentVo sumResult = GenericSumUtil.sumList(statisticsTheAmountSpentList, StatisticsTheAmountSpentVo.class); + sumResult.setRestName("总计"); + sumResult.setEatDay(startDate+"~"+endDate); + return Result.OK(sumResult); + } + + + /** + * 个人消费记录统计-导出 + */ + @GetMapping("/statisticsTheAmountSpentEveryMember/export") + public void statisticsTheAmountSpentListExport(HttpServletResponse response, + @RequestParam(value = "canteenId", required = false) String canteenId, + @RequestParam(value = "secondThirdDepartIds", required = false) String secondThirdDepartIds, + @RequestParam(value = "startDate", required = false) String startDate, + @RequestParam(value = "endDate", required = false) String endDate, + @RequestParam(value = "name", required = false) String name, + @RequestParam(value = "payName", required = false) String payName) { + String secondDepartId = null; + String thirdDepartId = null; + if(StringUtils.isNotEmpty(secondThirdDepartIds)){ + String[] split = secondThirdDepartIds.split(","); + secondDepartId = split[0]; + if(split.length > 1){ + thirdDepartId = split[1]; + } + } + List statisticsTheAmountSpentList = iStBbzxOrderInfoService.statisticsTheAmountSpentEveryMember(canteenId,name,payName,startDate, endDate,secondDepartId, thirdDepartId); + if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(statisticsTheAmountSpentList)){ + StatisticsTheAmountSpentVo sumResult = GenericSumUtil.sumList(statisticsTheAmountSpentList, StatisticsTheAmountSpentVo.class); + sumResult.setRealname("总计"); + sumResult.setWorkNo(null); + sumResult.setDepartName(null); + sumResult.setEatDay(startDate+"~"+endDate); + statisticsTheAmountSpentList.add(sumResult); + String exportFields = "realname|姓名,customerPrice|消费总额(元),workNo|工号,departName|部门,totalCount|消费次数,totalNum|消费天数," + + "breakfastAmount|早餐消费金额(元),breakfastPerson|早餐消费次数,lunchAmount|午餐消费金额(元),lunchPerson|午餐消费次数,dinnerAmount|晚餐消费金额(元),dinnerPerson|晚餐消费次数" + + ",otherAmount|其他消费金额(元),otherPerson|其他消费次数,orderPrice|结算金额(元),eatDay|消费日期"; + ExcelUtils.export(response, "个人消费统计", statisticsTheAmountSpentList, exportFields, "yyyy-MM-dd HH:mm",true); + } + } + + /** + * 食堂营业查询报表 + * @return 食堂营业查询报表 + */ + @PostMapping("/statisticsCanteenBusiness") + @Operation(summary = "食堂营业查询报表") + public Result statisticsCanteenBusiness(@RequestBody StAmountSpentEveryMemberDTO dto) { + String secondDepartId = null; + String thirdDepartId = null; + if(StringUtils.isNotEmpty(dto.getSecondThirdDepartIds())){ + String[] split = dto.getSecondThirdDepartIds().split(","); + secondDepartId = split[0]; + if(split.length > 1){ + thirdDepartId = split[1]; + } + } + //处理时间 + if(CollectionUtils.isNotEmpty(dto.getDates())){ + dto.setStartDate(dto.getDates().get(0)); + dto.setEndDate(dto.getDates().get(1)); + } + return Result.OK(iStBbzxOrderInfoService.statisticsCanteenBusiness(dto.getCanteenId(), dto.getStartDate(), dto.getEndDate(), secondDepartId, thirdDepartId, dto.getPageNo(), dto.getPageSize())); + } + + + /** + * 食堂营业查询报表总计 + * @param startDate 开始时间 + * @param endDate 结束时间 + * @return 食堂营业查询报表 + */ + @GetMapping("/statisticsCanteenBusiness/sum") + @Operation(summary = "食堂营业查询报表总计") + public Result statisticsCanteenBusiness(@RequestParam(value = "secondThirdDepartIds", required = false) String secondThirdDepartIds, + @RequestParam(value = "startDate", required = false) String startDate, + @RequestParam(value = "endDate", required = false) String endDate, + @RequestParam(value = "canteenId", required = false) String canteenId) { + String secondDepartId = null; + String thirdDepartId = null; + if(StringUtils.isNotEmpty(secondThirdDepartIds)){ + String[] split = secondThirdDepartIds.split(","); + secondDepartId = split[0]; + if(split.length > 1){ + thirdDepartId = split[1]; + } + } + List statisticsCanteenBusiness = iStBbzxOrderInfoService.statisticsCanteenBusiness(canteenId, startDate, endDate, secondDepartId, thirdDepartId); + StatisticsBusinessVo sumResult = GenericSumUtil.sumList(statisticsCanteenBusiness, StatisticsBusinessVo.class); + sumResult.setRestName("总计"); + sumResult.setPayTime(null); + return Result.OK(sumResult); + } + + /** + * 食堂营业查询报表-导出 + */ + @GetMapping("/statisticsCanteenBusiness/export") + public void statisticsCanteenBusinessExport(HttpServletResponse response, + @RequestParam(value = "secondThirdDepartIds", required = false) String secondThirdDepartIds, + @RequestParam(value = "startDate", required = false) String startDate, + @RequestParam(value = "endDate", required = false) String endDate, + @RequestParam(value = "canteenId", required = false) String canteenId) { + String secondDepartId = null; + String thirdDepartId = null; + if(StringUtils.isNotEmpty(secondThirdDepartIds)){ + String[] split = secondThirdDepartIds.split(","); + secondDepartId = split[0]; + if(split.length > 1){ + thirdDepartId = split[1]; + } + } + List statisticsCanteenBusiness = iStBbzxOrderInfoService.statisticsCanteenBusiness(canteenId, startDate, endDate, secondDepartId, thirdDepartId); + if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(statisticsCanteenBusiness)){ + StatisticsBusinessVo sumResult = GenericSumUtil.sumList(statisticsCanteenBusiness, StatisticsBusinessVo.class); + sumResult.setRestName("总计"); + sumResult.setPayTime(null); + statisticsCanteenBusiness.add(sumResult); + String exportFields = "restName|食堂名称,payTime|日期,rechargeAmount|充值合计,totalRefund|退款合计,totalCount|消费次数合计,customerPrice|消费合计"; + ExcelUtils.export(response, "食堂营业统计", statisticsCanteenBusiness, exportFields, "yyyy-MM-dd HH:mm",true); + } + } + + + /** + * 充值表 + * @param query + * @return + */ + @PostMapping("/rechargeTransactionRecord") + @Operation(summary = "充值流水列表分页查询", description = "传入条件") + public Result> rechargeTransactionRecord(@RequestBody StClientMemberTransactionRecordDTO query) { + if(StringUtils.isNotBlank(query.getMainTimeDay()) && query.getMainTimeDay().contains(",")){ + String[] split = query.getMainTimeDay().split(","); + query.setStartTime(split[0]); + query.setEndTime(split[1]); + } + //处理时间 + if(CollectionUtils.isNotEmpty(query.getDates())){ + query.setStartTime(query.getDates().get(0)); + query.setEndTime(query.getDates().get(1)); + } + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + //餐品总库只查询内置食堂 + if(sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_0) || sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_1)){ + // 后期分数据权限时在解开注释,此处管理员账户能看到所有食堂数据 +// ScreenVo screenVo = new ScreenVo(); +// screenVo.setId(CommonConstant.INLAY_CANTEEN_ID); +// canteenIdList.add(screenVo); + } else if (sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_4)) { + //查询当前登录用户的食堂列表 + String canteenId = sysUser.getCanteenId(); + query.setCanteenId(canteenId); + } else { + return Result.OK(); + } + query.setStatus(3);//(充值=102 + return Result.OK(iStClientMemberTransactionRecordService.pageQuery(query)); + } + + /** + * 充值表汇总 + * @param query + * @return + */ + @GetMapping("/rechargeTransactionRecord/sum") + @Operation(summary = "充值流水列表分页查询汇总", description = "传入条件") + public Result rechargeTransactionRecordSum(StClientMemberTransactionRecordDTO query) { + if(StringUtils.isNotBlank(query.getMainTimeDay()) && query.getMainTimeDay().contains(",")){ + String[] split = query.getMainTimeDay().split(","); + query.setStartTime(split[0]); + query.setEndTime(split[1]); + } + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + //餐品总库只查询内置食堂 + if(sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_0) || sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_1)){ + // 后期分数据权限时在解开注释,此处管理员账户能看到所有食堂数据 +// ScreenVo screenVo = new ScreenVo(); +// screenVo.setId(CommonConstant.INLAY_CANTEEN_ID); +// canteenIdList.add(screenVo); + } else if (sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_4)) { + //查询当前登录用户的食堂列表 + String canteenId = sysUser.getCanteenId(); + query.setCanteenId(canteenId); + } else { + return Result.OK(); + } + query.setStatus(3);//(充值=102 + List statisticsCanteenBusiness = iStClientMemberTransactionRecordService.queryList(query); + StClientMemberTransactionRecordVO sumResult = GenericSumUtil.sumList(statisticsCanteenBusiness, StClientMemberTransactionRecordVO.class); + + StClientMemberTransactionRecordVO stClientMemberTransactionRecordVO = new StClientMemberTransactionRecordVO();//只汇总交易金额 + stClientMemberTransactionRecordVO.setSerialNo("总计"); + stClientMemberTransactionRecordVO.setTransactionAmount(sumResult.getTransactionAmount()); + return Result.OK(stClientMemberTransactionRecordVO); + } + + /** + * 充值流水报表-导出 + */ + @GetMapping("/rechargeTransactionRecord/export") + public void rechargeTransactionRecordExport(HttpServletResponse response,StClientMemberTransactionRecordDTO query) { + if(StringUtils.isNotBlank(query.getMainTimeDay()) && query.getMainTimeDay().contains(",")){ + String[] split = query.getMainTimeDay().split(","); + query.setStartTime(split[0]); + query.setEndTime(split[1]); + } + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + //餐品总库只查询内置食堂 + if(sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_0) || sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_1)){ + // 后期分数据权限时在解开注释,此处管理员账户能看到所有食堂数据 +// ScreenVo screenVo = new ScreenVo(); +// screenVo.setId(CommonConstant.INLAY_CANTEEN_ID); +// canteenIdList.add(screenVo); + } else if (sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_4)) { + //查询当前登录用户的食堂列表 + String canteenId = sysUser.getCanteenId(); + query.setCanteenId(canteenId); + } else { + return; + } + query.setStatus(3);//(充值=102 + List statisticsCanteenBusiness = iStClientMemberTransactionRecordService.queryList(query); + if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(statisticsCanteenBusiness)){ + StClientMemberTransactionRecordVO sumResult = GenericSumUtil.sumList(statisticsCanteenBusiness, StClientMemberTransactionRecordVO.class); + StClientMemberTransactionRecordVO stClientMemberTransactionRecordVO = new StClientMemberTransactionRecordVO();//只汇总交易金额 + stClientMemberTransactionRecordVO.setSerialNo("总计"); + stClientMemberTransactionRecordVO.setTransactionAmount(sumResult.getTransactionAmount()); + statisticsCanteenBusiness.add(stClientMemberTransactionRecordVO); + +// String exportFields = "serialNo|交易流水号,orderNo|交易单号,userName|姓名,phone|手机号,workNo|工号," + +// "statusName|操作类型,transactionAmount|交易金额,paymentMethodName|结账方式,storedBalance|储值金额变动(元),rewardBalance|赠送金额变动(元),availableBalance|余额," + +// "integralBalance|积分变动,createTime|交易时间,payTime|结账时间,updateStatusName|操作端,createOne|操作人"; + String exportFields = "serialNo|交易流水号,orderNo|交易单号,userName|姓名,phone|手机号,workNo|工号," + + "transactionAmount|交易金额,statusName|操作类型,paymentMethodName|充值方式,availableBalance|现金余额," + + "createTime|充值时间,createOne|操作人"; + ExcelUtils.export(response, "充值记录", statisticsCanteenBusiness, exportFields, "yyyy-MM-dd HH:mm",true); + } + + } + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/controller/StClientMemberBalanceController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/controller/StClientMemberBalanceController.java new file mode 100644 index 0000000..c1f006d --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/controller/StClientMemberBalanceController.java @@ -0,0 +1,722 @@ +package org.jeecg.payment.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.Operation; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.SecurityUtils; +import org.jeecg.aspect.SendNotification; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.aspect.annotation.AutoLog; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.payment.dto.StClientMemberChangeBalanceDTO; +import org.jeecg.payment.dto.StClientMemberTopUpDTO; +import org.jeecg.payment.dto.StClientMemberTransactionRecordDTO; +import org.jeecg.payment.entity.StClientMemberBalance; +import org.jeecg.payment.entity.StClientMemberLevelDemotionTime; +import org.jeecg.payment.entity.StClientMemberTransactionRecord; +import org.jeecg.payment.enums.MemberTransactionTypeEnum; +import org.jeecg.payment.service.IStClientMemberBalanceService; +import org.jeecg.payment.service.IStClientMemberLevelDemotionTimeService; +import org.jeecg.payment.service.IStClientMemberTransactionRecordService; +import org.jeecg.payment.utils.ExcelResultGenerator; +import org.jeecg.payment.utils.ExcelUtils; +import org.jeecg.payment.utils.GenericSumUtil; +import org.jeecg.payment.vo.*; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.Collectors; + +/** + 充值管理 + */ +@RestController +@RequestMapping("/payment/swClientMemberBalance") +public class StClientMemberBalanceController { + + @Resource + private IStClientMemberBalanceService iStClientMemberBalanceService; + @Resource + private IStClientMemberTransactionRecordService iStClientMemberTransactionRecordService; + @Resource + private IStClientMemberLevelDemotionTimeService iStClientMemberLevelDemotionTimeService; + + + //==============================用户充值================================================== + /** + * 后台用户充值列表 + * @param + * @return + */ + @Operation(summary = "用户充值-后台用户充值列表", description = "后台用户充值列表-列表") + @PostMapping(value = "/getSpendingOrRechargeInfoList") + public Result> getSpendingOrRechargeInfoList(@RequestBody StClientMemberTransactionRecordDTO stClientUserInfoShopPageDTO){ + if(StringUtils.isBlank(stClientUserInfoShopPageDTO.getCanteenId())){ + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + stClientUserInfoShopPageDTO.setCanteenId(sysUser.getCanteenId()); + } + //处理时间 + if(org.apache.commons.collections.CollectionUtils.isNotEmpty(stClientUserInfoShopPageDTO.getDates())){ + stClientUserInfoShopPageDTO.setStartTime(stClientUserInfoShopPageDTO.getDates().get(0)); + stClientUserInfoShopPageDTO.setEndTime(stClientUserInfoShopPageDTO.getDates().get(1)); + } + Page recordListVoPage = new Page<>(stClientUserInfoShopPageDTO.getPageNo(), stClientUserInfoShopPageDTO.getPageSize()); + IPage stRecordListVoIPage = iStClientMemberBalanceService.memberClientMemberBalanceService(recordListVoPage,stClientUserInfoShopPageDTO); + return Result.OK(stRecordListVoIPage); + } + + /** + * 用户充值-导出 + */ + @GetMapping("/getSpendingOrRechargeInfoList/export") + public void getSpendingOrRechargeInfoListExport(HttpServletResponse response, StClientMemberTransactionRecordDTO stClientUserInfoShopPageDTO) { + if(StringUtils.isBlank(stClientUserInfoShopPageDTO.getCanteenId())){ + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + stClientUserInfoShopPageDTO.setCanteenId(sysUser.getCanteenId()); + } + List statisticsTheAmountSpentList = iStClientMemberBalanceService.getSpendingOrRechargeInfoListExport(stClientUserInfoShopPageDTO); + if(CollectionUtils.isNotEmpty(statisticsTheAmountSpentList)){ + String exportFields = "workNo|工号,userName|姓名,phone|联系方式,topUpBalanceTotal|总余额(元),topUpBalance|储值金额(元),rewardBalance|赠送金额(元)," + + "consumptionCount|消费次数,consumptionTotal|累计消费(元),topUpCount|充值次数,lastConsumptionTime|最后一次消费时间,firstTopUpTime|首充时间"; + ExcelUtils.export(response, "用户充值", statisticsTheAmountSpentList, exportFields, "yyyy-MM-dd HH:mm",true); + } + } + + + + @GetMapping("/memberTopUp") + @Operation(summary = "用户充值-充值(线下)", description = "传入条件") + public Result memberTopUp(@RequestParam(name = "canteenId",required = false) String canteenId,@RequestParam String userId, @RequestParam BigDecimal value, @RequestParam int paymentMethod) { + if(StringUtils.isBlank(canteenId)){ + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + canteenId = sysUser.getCanteenId(); + } + LambdaQueryWrapper clientMemberWrapper = new LambdaQueryWrapper<>(); + clientMemberWrapper.eq(StClientMemberBalance::getMemberId, userId); + StClientMemberBalance balance = iStClientMemberBalanceService.getOne(clientMemberWrapper); + +// Map map = iStClientMemberLevelDemotionTimeService.topUpAwardService(null, value.floatValue(),canteenId); + BigDecimal rewardBalance = new BigDecimal(0); + int integralBalance = 0; + StClientMemberTopUpDTO query = new StClientMemberTopUpDTO(); + query.setId(balance.getId()); + query.setCanteenId(canteenId); +// if(null != map){ +// rewardBalance = BigDecimal.valueOf(Float.parseFloat(map.get("amount").toString())); +// integralBalance = Integer.parseInt(map.get("integral").toString()); +// } + query.setTopUpBalance(value); + query.setRewardBalance(rewardBalance); + query.setIntegralBalance(integralBalance); + query.setPaymentMethod(paymentMethod); + boolean b = iStClientMemberBalanceService.memberTopUp(query); + if(b){ + return Result.OK("操作成功!"); + } + return Result.error("操作失败"); + } + + + /** + * 导入充值数据 + * @param file 文件 + * @param canteenId 所属食堂 + */ + @AutoLog(value = "导入充值数据") + @PostMapping("/importRechargeData") + public Result importRechargeData(@RequestParam(name ="file", required = true) MultipartFile file, + @RequestParam(name = "canteenId",required = false) String canteenId) { + + try { + // 1. 处理食堂ID + if (StringUtils.isBlank(canteenId)) { + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + canteenId = sysUser.getCanteenId(); + } + + // 2. 解析原始Excel + List originalDtoList = iStClientMemberBalanceService.parseExcel(file, canteenId); + if (originalDtoList.isEmpty()) { + throw new RuntimeException("Excel文件中无有效数据,请检查文件内容"); + } + + // 3. 执行充值并获取结果 + Map> rechargeResultMap = + iStClientMemberBalanceService.importRechargeDataV2(originalDtoList); + + // 4. 全部成功时返回指定JSON格式 + if (rechargeResultMap.containsKey("allSuccess") && + CollectionUtils.isNotEmpty(rechargeResultMap.getOrDefault("allSuccess", Collections.emptyList()))) { + return Result.OK("导入成功"); + } else { + // 4. 提取各类结果列表 + List successList = rechargeResultMap.getOrDefault("newListSuccess", Collections.emptyList()); + List failList = rechargeResultMap.getOrDefault("newListFailure", Collections.emptyList()); + List duplicateSerialList = rechargeResultMap.getOrDefault("oldList", Collections.emptyList()); + List multiUserList = rechargeResultMap.getOrDefault("userListMoreFailure", Collections.emptyList()); + List missingUserList = rechargeResultMap.getOrDefault("userListMissingFailure", Collections.emptyList()); + List successCardNos = successList.stream() + .map(StClientMemberTopUpDTO::getCardNo) + .collect(Collectors.toList()); + + List failCardNos = failList.stream()//充值执行失败 + .map(StClientMemberTopUpDTO::getCardNo) + .collect(Collectors.toList()); + + List duplicateSerialCardNos = duplicateSerialList.stream()//缴费单号历史已导入 + .map(StClientMemberTopUpDTO::getSerialNo) + .collect(Collectors.toList()); + + List multiUserCardNos = multiUserList.stream()//用户/钱包信息重复 + .map(StClientMemberTopUpDTO::getCardNo) + .collect(Collectors.toList()); + + List missingUserCardNos = missingUserList.stream()//用户/钱包信息缺失 + .map(StClientMemberTopUpDTO::getCardNo) + .collect(Collectors.toList()); + // 构建失败信息字符串 + StringBuilder errorMsg = new StringBuilder(); + // 充值执行失败(非空才添加) + if (!failCardNos.isEmpty()) { + errorMsg.append("充值执行失败:").append(failCardNos.size()) + .append("条: ").append(failCardNos).append("; "); + } + // 缴费单号历史已导入(非空才添加) + if (!duplicateSerialCardNos.isEmpty()) { + errorMsg.append("缴费单号历史已导入:").append(duplicateSerialCardNos.size()) + .append("条: ").append(duplicateSerialCardNos).append("; "); + } + // 用户/钱包信息重复(非空才添加) + if (!multiUserCardNos.isEmpty()) { + errorMsg.append("用户/钱包信息重复:").append(multiUserCardNos.size()) + .append("条: ").append(multiUserCardNos).append("; "); + } + // 用户/钱包信息缺失(非空才添加) + if (!missingUserCardNos.isEmpty()) { + errorMsg.append("用户/钱包信息缺失:").append(missingUserCardNos.size()) + .append("条: ").append(missingUserCardNos).append("; "); + } + // 去除末尾可能多余的分号和空格 + if (errorMsg.length() > 0) { + errorMsg.setLength(errorMsg.length() - 2); // 移除最后的 "; " + } + return Result.error(errorMsg.toString()); + } + } catch (Exception e) { + // 异常处理 + return Result.error("导入异常"); + } + } + + + /** + * 导入充值数据(改造后:返回带标记的Excel文件+导入统计) + * @param file 上传的Excel文件 + * @param canteenId 所属食堂(为空时取当前登录用户的食堂ID) + * @return ResponseEntity 带Excel文件的响应,响应头附带统计信息 + */ + @AutoLog(value = "导入充值数据") + @PostMapping("/importRechargeData/v2") + public ResponseEntity importRechargeDataV2( + @RequestParam(name = "file", required = true) MultipartFile file, + @RequestParam(name = "canteenId", required = false) String canteenId) throws IOException { + + try { + // 1. 处理食堂ID + if (StringUtils.isBlank(canteenId)) { + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + canteenId = sysUser.getCanteenId(); + } + + // 2. 解析原始Excel + List originalDtoList = iStClientMemberBalanceService.parseExcel(file, canteenId); + if (originalDtoList.isEmpty()) { + throw new RuntimeException("Excel文件中无有效数据,请检查文件内容"); + } + + // 3. 执行充值并获取结果 + Map> rechargeResultMap = + iStClientMemberBalanceService.importRechargeDataV2(originalDtoList); + + // 4. 全部成功时返回指定JSON格式 + if (rechargeResultMap.containsKey("allSuccess") && + CollectionUtils.isNotEmpty(rechargeResultMap.getOrDefault("allSuccess", Collections.emptyList()))) { + // 构建统一响应JSON + Map successResponse = new HashMap<>(); + successResponse.put("success", true); + successResponse.put("message", "操作成功"); + successResponse.put("code", 200); + successResponse.put("result", "操作成功"); + successResponse.put("timestamp", System.currentTimeMillis()); + // 设置响应头为JSON类型 + HttpHeaders jsonHeaders = new HttpHeaders(); + jsonHeaders.setContentType(MediaType.APPLICATION_JSON); + return new ResponseEntity<>(successResponse, jsonHeaders, HttpStatus.OK); + } + + // 4. 提取各类结果列表 + List successList = rechargeResultMap.getOrDefault("newListSuccess", Collections.emptyList()); + List failList = rechargeResultMap.getOrDefault("newListFailure", Collections.emptyList()); + List duplicateSerialList = rechargeResultMap.getOrDefault("oldList", Collections.emptyList()); + List multiUserList = rechargeResultMap.getOrDefault("userListMoreFailure", Collections.emptyList()); + List missingUserList = rechargeResultMap.getOrDefault("userListMissingFailure", Collections.emptyList()); + + // 5. 生成结果Excel流 + InputStream resultExcelStream = ExcelResultGenerator.generateResultExcel( + file, successList, failList, duplicateSerialList, multiUserList, missingUserList); + + //将 InputStream 转为字节数组 + byte[] excelBytes = inputStreamToByteArray(resultExcelStream); + //用 ByteArrayResource 包装字节数组(Spring 支持的资源类型) + ByteArrayResource resource = new ByteArrayResource(excelBytes) { + // 重写 getFilename 方法,确保下载时能正确识别文件名(部分浏览器依赖) + @Override + public String getFilename() { + try { + String originalFileName = file.getOriginalFilename(); + String suffix = originalFileName.endsWith(".xls") ? ".xls" : ".xlsx"; + return URLEncoder.encode("充值导入结果_" + System.currentTimeMillis() + suffix, StandardCharsets.UTF_8.name()) + .replaceAll("\\+", "%20"); + } catch (Exception e) { + return "充值导入结果.xlsx"; // 兜底文件名 + } + } + }; + + // 6. 封装响应头 + HttpHeaders headers = new HttpHeaders(); + String originalFileName = file.getOriginalFilename(); + String downloadFileName = "充值导入结果_" + System.currentTimeMillis() + + (originalFileName.endsWith(".xls") ? ".xls" : ".xlsx"); + String encodedFileName = URLEncoder.encode(downloadFileName, StandardCharsets.UTF_8.name()) + .replaceAll("\\+", "%20"); + + // 设置文件下载头(Content-Disposition 必须正确配置) + headers.setContentDispositionFormData("attachment", encodedFileName); + // 明确设置内容类型(避免浏览器识别错误) + headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); + // 设置内容长度 + headers.setContentLength(excelBytes.length); + + // 7. 附带导入统计信息 + headers.add("Import-Success-Count", String.valueOf(successList.size())); + headers.add("Import-Fail-Count", String.valueOf(failList.size())); + headers.add("Import-Duplicate-Serial-Count", String.valueOf(duplicateSerialList.size())); + headers.add("Import-Multi-User-Count", String.valueOf(multiUserList.size())); + headers.add("Import-Missing-User-Count", String.valueOf(missingUserList.size())); + headers.add("Import-Total-Count", String.valueOf(originalDtoList.size())); + + // 8. 返回响应 + return new ResponseEntity<>(resource, headers, HttpStatus.OK); + + } catch (Exception e) { + // 异常处理 + HttpHeaders errorHeaders = new HttpHeaders(); + String errorMsg = URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.name()) + .replaceAll("\\+", "%20"); + errorHeaders.add("Error-Message", errorMsg); + // 返回空资源+500状态码 + return new ResponseEntity<>(null, errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + /** + * 工具方法:将 InputStream 转为字节数组 + * @param inputStream 输入流 + * @return 字节数组 + * @throws IOException 流读取异常 + */ + private byte[] inputStreamToByteArray(InputStream inputStream) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len; + // 循环读取流内容到字节数组输出流 + while ((len = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, len); + } + // 关闭流(避免内存泄漏) + inputStream.close(); + outputStream.flush(); + outputStream.close(); + return outputStream.toByteArray(); + } + + /** + * 自定义金额获取奖励 + * @param price 金额 + * @return + */ + @GetMapping("/getTopUpAward") + @Operation(summary = "用户充值-充值(线下)-自定义金额获取奖励", description = "用户充值-充值(线下)-自定义金额获取奖励") + public Result getTopUpAward(@RequestParam(name = "canteenId",required = false) String canteenId,@RequestParam String price){ + if(StringUtils.isBlank(canteenId)){ + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + canteenId = sysUser.getCanteenId(); + } + StClientMemberLevelDemotionTime ruleSource = iStClientMemberLevelDemotionTimeService.getOne(new LambdaQueryWrapper() + .eq(StClientMemberLevelDemotionTime::getCanteenId, canteenId)); + Map result = new HashMap<>(); + if (ruleSource == null) {//未设置规则返回null,前端判断跳转到设置规则页面 + result.put("amount", null); + result.put("integral", null); + return Result.OK(result); + } + Map map = iStClientMemberLevelDemotionTimeService.topUpAwardService(null, Float.parseFloat(price),canteenId); + return Result.OK(map); + } + + @PostMapping("/backOutTopUpList") + @Operation(summary = "用户充值-撤销充值列表查询(线下)", description = "传入条件") + public Result backOutTopUpList(@RequestBody StClientMemberBackOutTopUpDTO query) { + if(StringUtils.isBlank(query.getCanteenId())){//当前食堂管理员只能查询到充值到该食堂的充值记录 + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + query.setCanteenId(sysUser.getCanteenId()); + } + return Result.OK(iStClientMemberBalanceService.backOutTopUpList(query)); + } + + + + @PostMapping("/backOutTopUpAll") + @Operation(summary = "用户充值-撤销充值(线下线下二合一)", description = "流水表主键id") + public Result backOutTopUpAll(@RequestBody StClientMemberBackTopUpQueryDto query) throws Exception { + if(StringUtils.isBlank(query.getCanteenId())){//当前食堂管理员撤销充值该食堂的充值记录 + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + query.setCanteenId(sysUser.getCanteenId()); + } + StClientMemberTransactionRecord transactionRecord = iStClientMemberTransactionRecordService.getById(query.getId()); + if(null == transactionRecord || StringUtils.isBlank(transactionRecord.getId())){ + return Result.error("订单未找到"); + } +// List onLine = Arrays.asList(MemberEnum.PAYMENT_METHOD_BY_ALI.getCode(), +// MemberEnum.PAYMENT_METHOD_BY_WX.getCode()); +// if(!onLine.contains(transactionRecord.getPaymentMethod()) && onLine.contains(query.getPaymentMethod())){ +// //原充值方式不是线上,但是撤销充值时是线上提示,不支持线上退款请选择其他退回 +// return Result.error("原订单非线上充值,不支持原路退回,请选择其他方式"); +// } +// if(onLine.contains(transactionRecord.getPaymentMethod())){//线上退款 +// RefundDTO refundDTO = new RefundDTO(); +// refundDTO.setPayOrderNo(query.getPayOrderNo()); +// refundDTO.setTotalFee(query.getTotalFee()); +// refundDTO.setPaymentMethod(query.getPaymentMethod()); +// refundDTO.setRemark(query.getRemark()); +// if(StringUtils.isBlank(query.getPayOrderNo())){ +// return Result.error("商户订单号不能为空"); +// } +// return stBbzxOrderInfoService.returnPayInfoTopUpOnline(refundDTO); +// } else { + //线下退款 + return iStClientMemberBalanceService.backOutTopUpOffLine(query); +// } + } + + + @PostMapping("/changeBalanceAndIntegral") + @Operation(summary = "用户充值-余额调整", description = "传入条件") + @SendNotification() + public Result changeBalanceAndIntegral(@RequestBody StClientMemberChangeBalanceDTO query) { + if(StringUtils.isBlank(query.getCanteenId())){//当前食堂管理员撤销充值该食堂的充值记录 + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + query.setCanteenId(sysUser.getCanteenId()); + } + return iStClientMemberBalanceService.changeBalanceAndIntegral(query); + } + + //=================================流水明细========================================== + + @PostMapping("/pageQuery") + @Operation(summary = "流水明细-流水列表分页查询", description = "传入条件") + public Result> pageQuery(@RequestBody StClientMemberTransactionRecordDTO query) { + + if(StringUtils.isNotBlank(query.getMainTimeDay()) && query.getMainTimeDay().contains(",")){ + String[] split = query.getMainTimeDay().split(","); + query.setStartTime(split[0]); + query.setEndTime(split[1]); + } + //处理时间 + if(CollectionUtils.isNotEmpty(query.getDates())){ + query.setStartTime(query.getDates().get(0)); + query.setEndTime(query.getDates().get(1)); + } + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + String canteenId = sysUser.getCanteenId(); + query.setCanteenId(canteenId); + query.setStatus(1);//消费=101 + return Result.OK(iStClientMemberTransactionRecordService.pageQuery(query)); + } + + /** + * 消费明细汇总 + * @param query + * @return + */ + @GetMapping("/pageQuery/sum") + @Operation(summary = "消费明细列表分页查询汇总", description = "传入条件") + public Result rechargeTransactionRecordSum(StClientMemberTransactionRecordDTO query) { + if(StringUtils.isNotBlank(query.getMainTimeDay()) && query.getMainTimeDay().contains(",")){ + String[] split = query.getMainTimeDay().split(","); + query.setStartTime(split[0]); + query.setEndTime(split[1]); + } + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + //餐品总库只查询内置食堂 + if(sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_0) || sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_1)){ + // 后期分数据权限时在解开注释,此处管理员账户能看到所有食堂数据 +// ScreenVo screenVo = new ScreenVo(); +// screenVo.setId(CommonConstant.INLAY_CANTEEN_ID); +// canteenIdList.add(screenVo); + } else if (sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_4)) { + //查询当前登录用户的食堂列表 + String canteenId = sysUser.getCanteenId(); + query.setCanteenId(canteenId); + } else { + return Result.OK(); + } + query.setStatus(1);//消费=101 + List statisticsCanteenBusiness = iStClientMemberTransactionRecordService.queryList(query); + StClientMemberTransactionRecordVO sumResult = GenericSumUtil.sumList(statisticsCanteenBusiness, StClientMemberTransactionRecordVO.class); + + StClientMemberTransactionRecordVO stClientMemberTransactionRecordVO = new StClientMemberTransactionRecordVO();//只汇总交易金额 + stClientMemberTransactionRecordVO.setSerialNo("总计"); + stClientMemberTransactionRecordVO.setTransactionAmount(sumResult.getTransactionAmount()); + return Result.OK(stClientMemberTransactionRecordVO); + } + + /** + * 消费明细报表-导出 + */ + @GetMapping("/pageQuery/export") + public void rechargeTransactionRecordExport(HttpServletResponse response, StClientMemberTransactionRecordDTO query) { + if(StringUtils.isNotBlank(query.getMainTimeDay()) && query.getMainTimeDay().contains(",")){ + String[] split = query.getMainTimeDay().split(","); + query.setStartTime(split[0]); + query.setEndTime(split[1]); + } + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + //餐品总库只查询内置食堂 + if(sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_0) || sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_1)){ + // 后期分数据权限时在解开注释,此处管理员账户能看到所有食堂数据 +// ScreenVo screenVo = new ScreenVo(); +// screenVo.setId(CommonConstant.INLAY_CANTEEN_ID); +// canteenIdList.add(screenVo); + } else if (sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_4)) { + //查询当前登录用户的食堂列表 + String canteenId = sysUser.getCanteenId(); + query.setCanteenId(canteenId); + } else { + return; + } + query.setStatus(1);//消费=101 + List statisticsCanteenBusiness = iStClientMemberTransactionRecordService.queryList(query); + + StClientMemberTransactionRecordVO sumResult = GenericSumUtil.sumList(statisticsCanteenBusiness, StClientMemberTransactionRecordVO.class); + StClientMemberTransactionRecordVO stClientMemberTransactionRecordVO = new StClientMemberTransactionRecordVO();//只汇总交易金额 + stClientMemberTransactionRecordVO.setSerialNo("总计"); + stClientMemberTransactionRecordVO.setTransactionAmount(sumResult.getTransactionAmount()); + statisticsCanteenBusiness.add(stClientMemberTransactionRecordVO); + + String exportFields = "serialNo|交易流水号,orderNo|交易单号,userName|姓名,phone|手机号,workNo|工号,cardCode|卡号," + + "statusName|操作类型,transactionAmount|交易金额,paymentMethodName|结账方式,businessModelName|消费类型,availableBalance|余额," + + "payTime|结账时间,updateStatusName|操作端,createOne|操作人"; +// "dinnerType|餐次名称,restName|食堂名称,createTime|交易时间,payTime|结账时间,updateStatusName|操作端,createOne|操作人"; + ExcelUtils.export(response, "消费明细", statisticsCanteenBusiness, exportFields, "yyyy-MM-dd HH:mm",true); + } + + //==============================用户退费================================================== + + /** + * 余额退款后台审核分页列表查询 + * + * @param stClientMemberTransactionRecord + * @param pageNo + * @param pageSize + * @param req + * @return + */ + @Operation(summary="用户退费-余额退款后台审核分页列表查询", description="余额退款后台审核-分页列表查询") + @GetMapping(value = "/btoc/refund/list") + public Result> queryPageList(StClientMemberTransactionRecordDTO stClientMemberTransactionRecord, + @RequestParam(name="pageNo", defaultValue="1") Integer pageNo, + @RequestParam(name="pageSize", defaultValue="10") Integer pageSize, + HttpServletRequest req) { + if(StringUtils.isNotBlank(stClientMemberTransactionRecord.getMainTimeDay()) && stClientMemberTransactionRecord.getMainTimeDay().contains(",")){ + String[] split = stClientMemberTransactionRecord.getMainTimeDay().split(","); + stClientMemberTransactionRecord.setStartTime(split[0]); + stClientMemberTransactionRecord.setEndTime(split[1]); + } + stClientMemberTransactionRecord.setStatus(2); + stClientMemberTransactionRecord.setPayTimeStatus(2); + Page page = new Page<>(pageNo, pageSize); + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + String canteenId = sysUser.getCanteenId(); + stClientMemberTransactionRecord.setCanteenId(canteenId); + IPage pageList = iStClientMemberTransactionRecordService.pageBalanceRefundList(page, stClientMemberTransactionRecord); + return Result.OK(pageList); + } + + +// /** +// * 企业付款到个人微信余额退款后台审核接口 +// * @param orderNo +// * @return +// */ +// @GetMapping("/btoc/refund/check") +// @Operation(summary= "用户退费-企业付款到个人微信余额退款后台审核接口") +// @SendNotification() +// public Result btocRefund(@RequestParam String orderNo) { +// String auditorId = JwtUtil.getUserId(); +// LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); +// queryWrapper.eq(StClientMemberTransactionRecord::getOrderNo, orderNo); +// StClientMemberTransactionRecord one = iStClientMemberTransactionRecordService.getOne(queryWrapper); +// if(null == one){ +// return Result.error("申请订单不存在"); +// } +// if(one.getStatus().equals(MemberTransactionTypeEnum.B_TO_C_REFUND_301.getCode())){ +// return Result.error("订单已经退款成功!"); +// } +// TransfersDto transfersDto = new TransfersDto(); +// transfersDto.setAmount(one.getTransactionAmount().multiply(BigDecimal.valueOf(100)).intValue()); +// SysUser sysUser = feignSystemService.getById(one.getMemberId()); +// if(null == sysUser){ +// return Result.error("用户不存在"); +// } +// if(StringUtils.isBlank(sysUser.getThirdId())){ +// return Result.error("用户微信标识为空,请先绑定微信后再试"); +// } +// transfersDto.setOpenid(sysUser.getThirdId()); +// transfersDto.setCheck_name("FORCE_CHECK"); +// transfersDto.setRe_user_name(sysUser.getRealname()); +// transfersDto.setDesc("企业付款到个人微信余额退款"); +// transfersDto.setPartner_trade_no(StMemberUtil.getOrderNoTransfer(orderNo)); +// Result result = iPayInfo.btocRefund(transfersDto); +// one.setCreateBy(auditorId);//审核人ID +// one.setPayTime(new Date());//审核人时间 +// one.setIsSync(0); +// iStClientMemberTransactionRecordService.updateById(one); +// if(null != result && result.getCode() == HttpServletResponse.SC_OK && result.getResult()){ +// LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); +// wrapper.eq(StClientMemberBalance::getMemberId, one.getMemberId()); +// StClientMemberBalance swClientMemberBalance = iStClientMemberBalanceService.getBaseMapper().selectOne(wrapper); +// if (swClientMemberBalance != null) { +// swClientMemberBalance.setTopUpBalance(new BigDecimal(0)); +// swClientMemberBalance.setRewardBalance(new BigDecimal(0)); +// swClientMemberBalance.setIntegralBalance(0); +// swClientMemberBalance.setUpdateTime(new Date()); +// swClientMemberBalance.setCreateBy(one.getCreateBy()); +// swClientMemberBalance.setUpdateBy(one.getUpdateBy()); +// swClientMemberBalance.setCanteenId(one.getCanteenId()); +// swClientMemberBalance.setIsSync(0); +// boolean flag = iStClientMemberBalanceService.saveOrUpdate(swClientMemberBalance); +// if(flag){ +// one.setStatus(MemberTransactionTypeEnum.B_TO_C_REFUND_301.getCode()); +// one.setIsSync(0); +// iStClientMemberTransactionRecordService.updateById(one); +// } +// } +// return Result.OK("退款成功"); +// } else { +// return Result.error("退款失败"); +// } +// +// } + + //===================================================================================== + + /** + * 根据订单号获取 充值订单记录信息 + * @param payOrderNo 小程序充值成功后微信订单号 + * @return 订单记录信息 + */ + @GetMapping("/getRecordInfoAboutRechargeByPayOrderNo") + public StClientMemberTransactionRecord getRecordInfoAboutRechargeByPayOrderNo(String payOrderNo){ + return iStClientMemberBalanceService.getRecordInfoAboutRechargeByPayOrderNo(payOrderNo,null,null); + } + + /** + * 充值退款后逻辑 + * 通过充值记录 生成退款记录 , 计算balance表数据 + * @param czRecord 充值记录 + */ + @GetMapping("/backOrder") + @SendNotification() + public void backOrder(StClientMemberTransactionRecord czRecord){ + iStClientMemberBalanceService.backOrder(czRecord); + } + + + //======================pay服务feign接口 + @GetMapping("/getBalantceInfoFeign") + @Operation(summary= "会员余额feign") + public StClientMemberBalance memberBalanceInfoFeign(@RequestParam String memberId) { + return iStClientMemberBalanceService.getOne(new LambdaQueryWrapper() + .eq(StClientMemberBalance::getMemberId, memberId)); + } + + +// @PostMapping("/updateFeign") +// @Operation(summary = "会员余额更新feign") +// public void memberBalanceUpdateFeign(@RequestBody StClientMemberSpendDTO balance) throws Exception { +// balance.setInvoice(false); +//// iStClientMemberBalanceService.memberSpend(balance); 迁移至client服务更新余额 +// } + + @PostMapping("/returnSpendFeign") + @Operation(summary="会员余额退款") + @SendNotification() + public void returnSpendFeign(@RequestParam String orderNo) { + // + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if(orderNo.length()>19){ + orderNo= orderNo.substring(0, orderNo.length() - 3); + } + //特殊处理会员订单号 + queryWrapper.eq(StClientMemberTransactionRecord::getOrderNo, orderNo); + queryWrapper.eq(StClientMemberTransactionRecord::getStatus, MemberTransactionTypeEnum.EXPENDITURE.getCode()); + queryWrapper.orderByDesc(StClientMemberTransactionRecord::getPayTime); + List ones = iStClientMemberTransactionRecordService.list(queryWrapper); + if (ones != null && ones.size() > 0) { + StClientMemberTransactionRecord one = ones.get(0); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(StClientMemberBalance::getMemberId, one.getMemberId()); + StClientMemberBalance swClientMemberBalance = iStClientMemberBalanceService.getBaseMapper().selectOne(wrapper); + if (swClientMemberBalance != null) { + BigDecimal yue = swClientMemberBalance.getTopUpBalance().add(one.getStoredBalance()); + swClientMemberBalance.setTopUpBalance(yue); + BigDecimal zsye = swClientMemberBalance.getRewardBalance().add(one.getRewardBalance()); + swClientMemberBalance.setRewardBalance(zsye); + int jf = swClientMemberBalance.getIntegralBalance() + one.getIntegralBalance(); + swClientMemberBalance.setIntegralBalance(jf); + swClientMemberBalance.setUpdateTime(new Date()); + swClientMemberBalance.setCreateBy(one.getCreateBy()); + swClientMemberBalance.setUpdateBy(one.getUpdateBy()); + swClientMemberBalance.setCanteenId(one.getCanteenId()); + swClientMemberBalance.setIsSync(0); + iStClientMemberBalanceService.saveOrUpdate(swClientMemberBalance); + } + + } + + + } + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/controller/StClientMemberBalanceFeignController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/controller/StClientMemberBalanceFeignController.java new file mode 100644 index 0000000..350cdde --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/controller/StClientMemberBalanceFeignController.java @@ -0,0 +1,146 @@ +package org.jeecg.payment.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; + + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.aspect.SendNotification; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.aspect.annotation.AutoLog; +import org.jeecg.payment.entity.StClientMemberBalance; +import org.jeecg.payment.entity.StClientMemberTransactionRecord; +import org.jeecg.payment.enums.MemberTransactionTypeEnum; +import org.jeecg.payment.service.IStClientMemberBalanceService; +import org.jeecg.payment.service.IStClientMemberTransactionRecordService; +import org.jeecg.payment.vo.StClientMemberSpendDTO; +import org.jeecg.system.service.ISysUserService; +import org.jeecg.user.entity.SysUser; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + 会员卡余额 + */ +@RestController +@RequestMapping("/zhstapi/swClientMemberBalance") +@Tag(name = "会员卡余额列表接口") +@Slf4j +public class StClientMemberBalanceFeignController { + + @Resource + private IStClientMemberBalanceService iStClientMemberBalanceService; + @Resource + private IStClientMemberTransactionRecordService iStClientMemberTransactionRecordService; + @Resource + private ISysUserService feignSystemService; + + //======================pay服务feign接口 + + + @GetMapping("/getBalantceInfoFeign") + @Operation(summary = "会员余额feign") + @SendNotification() + public StClientMemberBalance memberBalanceInfoFeign(@RequestParam String memberId) { + return iStClientMemberBalanceService.getOne(new LambdaQueryWrapper() + .eq(StClientMemberBalance::getMemberId, memberId)); + } + + + @PostMapping("/updateFeign") + @Operation(summary = "会员余额更新feign") + @SendNotification() + public void memberBalanceUpdateFeign(@RequestBody StClientMemberSpendDTO balance) throws Exception { + balance.setInvoice(false); + iStClientMemberBalanceService.memberSpend(balance); + } + + @PostMapping("/returnSpendFeign") + @Operation(summary = "会员余额退款") + @SendNotification() + public void returnSpendFeign(@RequestParam String orderNo) { + // + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if(orderNo.length()>19){ + orderNo= orderNo.substring(0, orderNo.length() - 3); + } + //特殊处理会员订单号 + queryWrapper.eq(StClientMemberTransactionRecord::getOrderNo, orderNo); + queryWrapper.eq(StClientMemberTransactionRecord::getStatus, MemberTransactionTypeEnum.EXPENDITURE.getCode()); + queryWrapper.orderByDesc(StClientMemberTransactionRecord::getPayTime); + List ones = iStClientMemberTransactionRecordService.list(queryWrapper); + if (ones != null && ones.size() > 0) { + StClientMemberTransactionRecord one = ones.get(0); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(StClientMemberBalance::getMemberId, one.getMemberId()); + StClientMemberBalance swClientMemberBalance = iStClientMemberBalanceService.getBaseMapper().selectOne(wrapper); + if (swClientMemberBalance != null) { + BigDecimal yue = swClientMemberBalance.getTopUpBalance().add(one.getStoredBalance()); + swClientMemberBalance.setTopUpBalance(yue); + BigDecimal zsye = swClientMemberBalance.getRewardBalance().add(one.getRewardBalance()); + swClientMemberBalance.setRewardBalance(zsye); + int jf = swClientMemberBalance.getIntegralBalance() + one.getIntegralBalance(); + swClientMemberBalance.setIntegralBalance(jf); + swClientMemberBalance.setUpdateTime(new Date()); + swClientMemberBalance.setCreateBy(one.getCreateBy()); + swClientMemberBalance.setUpdateBy(one.getUpdateBy()); + swClientMemberBalance.setCanteenId(one.getCanteenId()); + swClientMemberBalance.setIsSync(0); + iStClientMemberBalanceService.saveOrUpdate(swClientMemberBalance); + } + + } + + + } + + /** + * 添加 + * + * @param stClientMemberBalance + * @return + */ + @AutoLog(value = "用户钱包初始数据添加") + @Operation(summary = "用户钱包初始数据添加", description = "用户钱包初始数据添加") + @PostMapping(value = "/add") +// @SendNotification() + public Result add(@RequestBody StClientMemberBalance stClientMemberBalance) { + if(StringUtils.isBlank(stClientMemberBalance.getMemberId())){ + log.error("用户钱包初始数据添加,用户不存在{}",stClientMemberBalance.getMemberId()); + return Result.OK("用户id为空!"); + } + + SysUser sysUser = feignSystemService.getById(stClientMemberBalance.getMemberId()); + if(null == sysUser){ + log.error("用户钱包初始数据添加,用户不存在{}",stClientMemberBalance.getMemberId()); + return Result.error("用户不存在"); + } + LambdaQueryWrapper stClientMemberBalanceLambdaQueryWrapper = new LambdaQueryWrapper<>(); + stClientMemberBalanceLambdaQueryWrapper.eq(StClientMemberBalance::getMemberId, stClientMemberBalance.getMemberId()); + StClientMemberBalance stClientMemberBalance1 = iStClientMemberBalanceService.getOne(stClientMemberBalanceLambdaQueryWrapper); + if(null != stClientMemberBalance1){ + log.error("用户钱包初始数据添加,用户不存在{}",stClientMemberBalance.getMemberId()); + return Result.error("用户钱包已存在"); + } + stClientMemberBalance.setTopUpTotal(BigDecimal.ZERO); + stClientMemberBalance.setTopUpBalance(BigDecimal.ZERO); + stClientMemberBalance.setRewardBalance(BigDecimal.ZERO); + stClientMemberBalance.setIntegralBalance(0); + stClientMemberBalance.setConsumptionCount(0); + stClientMemberBalance.setConsumptionTotal(BigDecimal.ZERO); + stClientMemberBalance.setIsDel(0); + stClientMemberBalance.setCreateTime(new Date()); + stClientMemberBalance.setCanteenId(stClientMemberBalance.getCanteenId()); + stClientMemberBalance.setIsSync(0); + iStClientMemberBalanceService.save(stClientMemberBalance); + return Result.OK("添加成功!"); + } + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/controller/StClientMemberLevelDeductRuleController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/controller/StClientMemberLevelDeductRuleController.java new file mode 100644 index 0000000..6a4a1c4 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/controller/StClientMemberLevelDeductRuleController.java @@ -0,0 +1,255 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.SecurityUtils; +import org.jeecg.aspect.SendNotification; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.payment.entity.StClientMemberLevelDeductRule; +import org.jeecg.payment.service.IStClientMemberLevelDeductRuleService; +import org.jeecg.payment.vo.*; +import org.jeecg.restaurant.entity.SlBaseRestaurant; +import org.jeecg.restaurant.mapper.SlBaseRestaurantMapper; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + + +/** + * 扣费规则管理 + */ +@RestController +@AllArgsConstructor +@RequestMapping("/payment/memberLevelDeductRule") +@Tag(name="扣费规则管理") +public class StClientMemberLevelDeductRuleController { + + + @Resource + private IStClientMemberLevelDeductRuleService iStClientMemberLevelDeductRuleService; + + @Resource + private SlBaseRestaurantMapper slBaseRestaurantMapper; + + + /** + * 详情 + */ + @GetMapping("/detail") + @Operation(summary = "详情", description = "传入StClientMemberLevelDemotionTime") + public Result detail(StClientMemberLevelDeductRuleVo StClientMemberLevelDemotionTime) { + return Result.ok(iStClientMemberLevelDeductRuleService.getOneService(StClientMemberLevelDemotionTime)); + } + + /** + * 启用停用 + */ + @GetMapping("/support/anableAndDisable") + @Operation(summary = "启用停用", description = "传入isEnable启用=1,禁用=0") + @SendNotification() + public Result anableAndDisable(@RequestParam(name = "canteenId",required = false) String canteenId, + @RequestParam(name = "isEnable") Integer isEnable, + @RequestParam(name = "id") String id) { + if(StringUtils.isBlank(canteenId)){ + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + canteenId = sysUser.getCanteenId(); + } + StClientMemberLevelDeductRule stClientMemberLevelDeductRule = iStClientMemberLevelDeductRuleService.getById(id); + if(null == stClientMemberLevelDeductRule){ + return Result.error("未查询到数据"); + } + if(!stClientMemberLevelDeductRule.getIsEnable().equals(isEnable)){ + stClientMemberLevelDeductRule.setIsEnable(isEnable); + stClientMemberLevelDeductRule.setIsSync(0); + boolean flag = iStClientMemberLevelDeductRuleService.updateById(stClientMemberLevelDeductRule); + if(flag && isEnable == 1){ + LambdaQueryWrapper levelDeductRuleLambdaQueryWrapper = new LambdaQueryWrapper<>(); + levelDeductRuleLambdaQueryWrapper.ne(StClientMemberLevelDeductRule::getId,id); + levelDeductRuleLambdaQueryWrapper.eq(StClientMemberLevelDeductRule::getIsEnable,isEnable); + levelDeductRuleLambdaQueryWrapper.eq(StClientMemberLevelDeductRule::getCanteenId,canteenId); + List levelDeductRuleList = iStClientMemberLevelDeductRuleService.list(levelDeductRuleLambdaQueryWrapper); + //关闭其他开着的规则 + if(CollectionUtils.isNotEmpty(levelDeductRuleList)){ + levelDeductRuleList.forEach(rule->{ + rule.setIsEnable(0); + rule.setIsSync(0); + }); + iStClientMemberLevelDeductRuleService.updateBatchById(levelDeductRuleList); + } + } + } + return Result.ok("操作成功"); + } + + /** + * 查询是否支持外部人员就餐标记 + */ + @GetMapping("/support/external/personnel") + @Operation(summary = "查询是否支持外部人员就餐标记,是否使用支持外部人员就餐(0:不支持,1:支持)") + public Result supportExternalPersonnel(@RequestParam(name = "canteenId",required = false) String canteenId) { + if(StringUtils.isBlank(canteenId)){ + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + canteenId = sysUser.getCanteenId(); + } + SlBaseRestaurant slBaseRestaurant = slBaseRestaurantMapper.selectById(canteenId); + if(null == slBaseRestaurant){ + return Result.error("未查询到食堂数据"); + } + return Result.ok(slBaseRestaurant.getIsExternalDine()); + } + + @GetMapping("/page") + @Operation(summary = "扣费规则列表分页查询") + public Result> pageQuery(StClientMemberLevelDemotionTimeVO query) { + if(StringUtils.isBlank(query.getCanteenId())){ + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + query.setCanteenId(sysUser.getCanteenId()); + } + return Result.OK(iStClientMemberLevelDeductRuleService.selectStClientMemberLevelDemotionTimePage(query)); + } + + /** + * 新增 + */ + @PostMapping("/saveOrUpdate") + @Operation(summary = "新增/更新") + @SendNotification() + public Result save(@Valid @RequestBody StClientMemberLevelDeductRuleVo stClientMemberLevelDeductRuleVo) { + if(StringUtils.isBlank(stClientMemberLevelDeductRuleVo.getCanteenId())){ + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + stClientMemberLevelDeductRuleVo.setCanteenId(sysUser.getCanteenId()); + } + Result result = verificationRule(stClientMemberLevelDeductRuleVo); + if(!result.isSuccess()){ + return result; + } + return Result.OK("操作成功",iStClientMemberLevelDeductRuleService.saveOrUpdateService(stClientMemberLevelDeductRuleVo)); + } + + /** + * 删除 + */ + @DeleteMapping("/remove") + @Operation(summary = "删除") + @SendNotification() + public Result remove( @RequestParam String id) { + StClientMemberLevelDeductRule stClientMemberLevelDeductRule = iStClientMemberLevelDeductRuleService.getById(id); + if(stClientMemberLevelDeductRule.getIsEnable() == 1){ + return Result.error("启用状态不能删除"); + } + boolean flag = iStClientMemberLevelDeductRuleService.removeById(id); + return Result.OK("操作成功"); + } + + + private Result verificationRule(StClientMemberLevelDeductRuleVo stClientMemberLevelDeductRuleVo){ + if(null == stClientMemberLevelDeductRuleVo.getDeductionType()){ + return Result.error("请选择扣费类型"); + } + Integer deductionType = stClientMemberLevelDeductRuleVo.getDeductionType(); + if(3 != deductionType){ + StMemberDeductRuleVO stMemberDeductRuleVO = stClientMemberLevelDeductRuleVo.getStMemberDeductRuleVO(); + StMemberDeductUserTypeRuleVO breakfast = stMemberDeductRuleVO.getBreakfast(); + if(null == breakfast){ + return Result.error("请设置早餐扣费规则"); + } + StMemberDeductUserTypeRuleVO lunch = stMemberDeductRuleVO.getLunch(); + if(null == lunch){ + return Result.error("请设置午餐扣费规则"); + } + StMemberDeductUserTypeRuleVO dinner = stMemberDeductRuleVO.getDinner(); + if(null == dinner){ + return Result.error("请设置晚餐扣费规则"); + } + + //=========================== + SlBaseRestaurant slBaseRestaurant = slBaseRestaurantMapper.selectById(stClientMemberLevelDeductRuleVo.getCanteenId()); + if(null == slBaseRestaurant){ + return Result.error("未查询到食堂数据"); + } + StMemberDeductTypeOfAuditorRuleVO breakfast1 = breakfast.getInner(); + if(null == breakfast1){ + return Result.error("请设置内部人员早餐相关扣费规则"); + } + StMemberDeductTypeOfAuditorRuleVO lunch1 = lunch.getInner(); + if(null == lunch1){ + return Result.error("请设置内部人员午餐相关扣费规则"); + } + StMemberDeductTypeOfAuditorRuleVO dinner1 = dinner.getInner(); + if(null == dinner1){ + return Result.error("请设置内部人员晚餐相关扣费规则"); + } + if(breakfast1.getFixedDeduction() == null){ + return Result.error("请设置内部人员早餐【固定扣除】"); + } + if(lunch1.getFixedDeduction() == null){ + return Result.error("请设置内部人员午餐【固定扣除】"); + } + if(dinner1.getFixedDeduction() == null){ + return Result.error("请设置内部人员晚餐【固定扣除】"); + } + //2:固定+补贴扣费 + if(deductionType == 2){ + if(breakfast1.getRatioDeduction() == null && breakfast1.getRewardDeduction() == null){ + return Result.error("请设置内部人员早餐【比例扣减】或【补贴减扣】"); + } + if(lunch1.getRatioDeduction() == null && lunch1.getRewardDeduction() == null){ + return Result.error("请设置内部人员午餐【比例扣减】或【补贴减扣】"); + } + if(dinner1.getRatioDeduction() == null && dinner1.getRewardDeduction() == null){ + return Result.error("请设置内部人员晚餐【比例扣减】或【补贴减扣】"); + } + } + //是否使用支持外部人员就餐(0:不支持,1:支持) + Integer isExternalDine = slBaseRestaurant.getIsExternalDine(); + if(isExternalDine == 1){ + StMemberDeductTypeOfAuditorRuleVO breakfast2 = breakfast.getExternal(); + if(null == breakfast2){ + return Result.error("请设置外部人员早餐相关扣费规则"); + } + StMemberDeductTypeOfAuditorRuleVO lunch2 = lunch.getExternal(); + if(null == lunch2){ + return Result.error("请设置外部人员午餐相关扣费规则"); + } + StMemberDeductTypeOfAuditorRuleVO dinner2 = dinner.getExternal(); + if(null == dinner2){ + return Result.error("请设置外部人员晚餐相关扣费规则"); + } + if(breakfast2.getFixedDeduction() == null){ + return Result.error("请设置外部人员早餐【固定扣除】"); + } + if(lunch2.getFixedDeduction() == null){ + return Result.error("请设置外部人员午餐【固定扣除】"); + } + if(dinner2.getFixedDeduction() == null){ + return Result.error("请设置外部人员晚餐【固定扣除】"); + } + //2:固定+补贴扣费 + if(deductionType == 2){ + if(breakfast2.getRatioDeduction() == null && breakfast2.getRewardDeduction() == null){ + return Result.error("请设置外部人员早餐【比例扣减】或【补贴减扣】"); + } + if(lunch2.getRatioDeduction() == null && lunch2.getRewardDeduction() == null){ + return Result.error("请设置外部人员午餐【比例扣减】或【补贴减扣】"); + } + if(dinner2.getRatioDeduction() == null && dinner2.getRewardDeduction() == null){ + return Result.error("请设置外部人员晚餐【比例扣减】或【补贴减扣】"); + } + } + } + } + return Result.OK(); + } + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/controller/StClientMemberLevelDemotionTimeController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/controller/StClientMemberLevelDemotionTimeController.java new file mode 100644 index 0000000..6f52d78 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/controller/StClientMemberLevelDemotionTimeController.java @@ -0,0 +1,130 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.SecurityUtils; +import org.jeecg.aspect.SendNotification; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.payment.entity.StClientMemberLevelDemotionTime; +import org.jeecg.payment.service.IStClientMemberLevelDemotionTimeService; +import org.jeecg.payment.vo.StClientMemberLevelDemotionTimeVO; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + + +/** + * 充值规则管理 + */ +@RestController +@AllArgsConstructor +@RequestMapping("/payment/StClientMemberLevelDemotionTime") +@Tag(name= "充值规则管理") +public class StClientMemberLevelDemotionTimeController { + + @Resource + private IStClientMemberLevelDemotionTimeService stClientMemberLevelDemotionTimeService; + /** + * 详情 + */ + @GetMapping("/detail") + @Operation(summary = "详情", description = "传入StClientMemberLevelDemotionTime") + public Result detail(StClientMemberLevelDemotionTime StClientMemberLevelDemotionTime) { + return Result.ok(stClientMemberLevelDemotionTimeService.getOneService(StClientMemberLevelDemotionTime)); + } + + /** + * 启用停用 + */ + @GetMapping("/support/anableAndDisable") + @Operation(summary = "启用停用", description = "传入isEnable启用=1,禁用=0") + @SendNotification() + public Result anableAndDisable( @RequestParam(name = "canteenId",required = false) String canteenId, + @RequestParam(name = "isEnable") Integer isEnable, + @RequestParam(name = "id") String id) { + if(StringUtils.isBlank(canteenId)){ + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + canteenId = sysUser.getCanteenId(); + } + StClientMemberLevelDemotionTime stClientMemberLevelDemotionTime = stClientMemberLevelDemotionTimeService.getById(id); + if(null == stClientMemberLevelDemotionTime){ + return Result.error("未查询到数据"); + } + if(!stClientMemberLevelDemotionTime.getIsEnable().equals(isEnable)){ + stClientMemberLevelDemotionTime.setIsEnable(isEnable); + stClientMemberLevelDemotionTime.setIsSync(0); + boolean flag = stClientMemberLevelDemotionTimeService.updateById(stClientMemberLevelDemotionTime); + if(flag && isEnable == 1){ + LambdaQueryWrapper clientMemberLevelDemotionTimeLambdaQueryWrapper = new LambdaQueryWrapper<>(); + clientMemberLevelDemotionTimeLambdaQueryWrapper.ne(StClientMemberLevelDemotionTime::getId,id); + clientMemberLevelDemotionTimeLambdaQueryWrapper.eq(StClientMemberLevelDemotionTime::getIsEnable,isEnable); + clientMemberLevelDemotionTimeLambdaQueryWrapper.eq(StClientMemberLevelDemotionTime::getCanteenId,canteenId); + List memberLevelDemotionTimes = stClientMemberLevelDemotionTimeService.list(clientMemberLevelDemotionTimeLambdaQueryWrapper); + //关闭其他开着的规则 + if(CollectionUtils.isNotEmpty(memberLevelDemotionTimes)){ + memberLevelDemotionTimes.forEach(rule->{ + rule.setIsEnable(0); + rule.setIsSync(0); + }); + stClientMemberLevelDemotionTimeService.updateBatchById(memberLevelDemotionTimes); + } + } + } + return Result.ok("操作成功"); + } + + + @GetMapping("/page") + + @Operation(summary = "储值规则列表分页查询", description = "传入条件") + public Result> pageQuery(StClientMemberLevelDemotionTimeVO query) { + if(StringUtils.isBlank(query.getCanteenId())){ + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + query.setCanteenId(sysUser.getCanteenId()); + } + return Result.OK(stClientMemberLevelDemotionTimeService.selectStClientMemberLevelDemotionTimePage(query)); + } + + /** + * 新增 + */ + @PostMapping("/saveOrUpdate") + @Operation(summary = "新增/更新", description = "传入StClientMemberLevelDemotionTime") + @SendNotification() + public Result save(@Valid @RequestBody StClientMemberLevelDemotionTimeVO stClientMemberLevelDemotionTimeVO) { + if(StringUtils.isBlank(stClientMemberLevelDemotionTimeVO.getCanteenId())){ + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + stClientMemberLevelDemotionTimeVO.setCanteenId(sysUser.getCanteenId()); + } + return Result.OK("操作成功",stClientMemberLevelDemotionTimeService.saveOrUpdateService(stClientMemberLevelDemotionTimeVO)); + } + + /** + * 删除 + */ + @DeleteMapping("/remove") + @Operation(summary = "删除", description = "传入id") + @SendNotification() + public Result remove(@RequestParam String id) { + StClientMemberLevelDemotionTime stClientMemberLevelDemotionTime = stClientMemberLevelDemotionTimeService.getById(id); + if(stClientMemberLevelDemotionTime.getIsEnable() == 1){ + return Result.error("启用状态不能删除"); + } + boolean flag = stClientMemberLevelDemotionTimeService.removeById(id); + return Result.OK(); + } + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/listUtils/ListUtil.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/listUtils/ListUtil.java new file mode 100644 index 0000000..8e29272 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/listUtils/ListUtil.java @@ -0,0 +1,126 @@ +package org.jeecg.payment.listUtils; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; + +import java.lang.reflect.Field; +import java.util.*; + +public class ListUtil { + + /** + * list 集合分页 + * @param list list + * @param pageNum 页数 + * @param pageSize 每页条数 + * @return IPage + */ + public static IPage listToPage(List list, int pageNum, int pageSize){ + List pageList = new ArrayList<>(); + int curIdx = pageNum > 1 ? (pageNum - 1) * pageSize : 0; + for (int i = 0; i < pageSize && curIdx + i < list.size(); i++) { + pageList.add(list.get(curIdx + i)); + } + IPage page = new Page<>(pageNum, pageSize); + page.setRecords(pageList); + page.setTotal(list.size()); + return page; + } + + + /** + * object转list对象 + * @param obj object对象 + * @param clazz 需要转换的集合 + * @param 泛型 + * @return list + */ + public static List changeList(Object obj,Class clazz){ + try{ + List result = new ArrayList<>(); + if (obj instanceof List){ + for (Object o : (List)obj){ + String s = JSONObject.toJSONString(o); + T t = JSONObject.parseObject(s,clazz); + result.add(t); + } + return result; + } + return null; + }catch (Exception e){ + e.printStackTrace(); + } + return null; + } + + /** + * 对象 转 map + */ + public static Map beanToMap(Object obj) { + Map map = new HashMap<>(); + Field[] fields = obj.getClass().getDeclaredFields(); + try { + for (Field field : fields) { + field.setAccessible(true); + map.put(field.getName(), field.get(obj)); + } + return map; + + } catch (IllegalAccessException i) { + return null; + } + + } + + //String转换成map + public Map parseJsonMap(String str){ + Map map = new HashMap<>(); + JSONObject jsonObject = JSONObject.parseObject(str); + for (Object k : jsonObject.keySet()){ + Object v = jsonObject.get(k); + if(v instanceof JSONArray){ + List> list = new ArrayList<>(); + Iterator it = ((JSONArray) v).iterator(); + while (it.hasNext()){ + Object json2 = it.hasNext(); + list.add(parseJsonMap(json2.toString())); + } + map.put(k.toString(),list); + }else { + map.put(k.toString(),v); + } + } + return map; + } + + /** + * 对Map中的键进行排序通常需要先将键提取出来,排序后再根据排序后的键重新构建一个有序的Map。如果你想对键进行“指定位置排序”,这可能意味着你有一个特定的顺序要求,而不是简单的自然排序或比较器排序。 + * @param map + * @param keyOrder + * @param + * @param + * @return + */ + public static Map sortMapByKeyOrder(Map map, List keyOrder) { + // 使用LinkedHashMap来保持插入顺序 + Map sortedMap = new LinkedHashMap<>(); + + // 遍历keyOrder列表,将map中的键值对按顺序放入sortedMap + for (K key : keyOrder) { + if (map.containsKey(key)) { + sortedMap.put(key, map.get(key)); + } + } + + // 如果keyOrder列表中没有包含map中的所有键,将剩余的键添加到sortedMap末尾 + for (Map.Entry entry : map.entrySet()) { + if (!sortedMap.containsKey(entry.getKey())) { + sortedMap.put(entry.getKey(), entry.getValue()); + } + } + + return sortedMap; + } +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/listUtils/MapUtil.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/listUtils/MapUtil.java new file mode 100644 index 0000000..3f0d2f6 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/listUtils/MapUtil.java @@ -0,0 +1,133 @@ +package org.jeecg.payment.listUtils; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import java.util.*; + +/** + * @author fans + * @date 2024/04/29 9:39 + */ +public class MapUtil { + + //第一次扣款节点,根据用户ID+日期+餐次为key,保存redis中12小时 + public static final String USER_ORDER_FLAG = "USER_ORDER_FLAG"; + //保存扣费规则实体类 + public static final String DeductRule = "DeductRule"; + + + /** + * Map拆分 (指定分组大小) + * @param map Map + * @param chunkSize 每个分组的大小 (>=1) + * @param Key + * @param Value + * @return 子Map列表 + */ + public static List> splitByChunkSize(Map map, int chunkSize){ + if(Objects.isNull(map) || map.isEmpty() || chunkSize<1){ + //空map或者分组大小<1,无法拆分 + return Collections.emptyList(); + } + + int mapSize = map.size(); //键值对总数 + int groupSize = mapSize/chunkSize + (mapSize%chunkSize==0?0:1); //计算分组个数 + List> list = Lists.newArrayListWithCapacity(groupSize); //子Map列表 + + if(chunkSize >= mapSize){ //只能分1组的情况 + list.add(map); + return list; + } + + int count = 0; //每个分组的组内计数 + Map subMap = Maps.newHashMapWithExpectedSize(chunkSize); //子Map + + for (Map.Entry entry : map.entrySet()) { + if (count < chunkSize) { + //给每个分组放chunkSize个键值对,最后一个分组可能会装不满 + subMap.put(entry.getKey(), entry.getValue()); + count++; //组内计数+1 + } else { + //结束上一个分组 + list.add(subMap); //当前分组装满了->加入列表 + + //开始下一个分组 + subMap = Maps.newHashMapWithExpectedSize(chunkSize); //新的分组 + subMap.put(entry.getKey(), entry.getValue()); //添加当前键值对 + count = 1; //组内计数重置为1 + } + } + + list.add(subMap); //添加最后一个分组 + return list; + } + + /** + * Map拆分(指定分组个数) + * @param map Map + * @param groupSize 分组个数 (>=1) + * @param Key + * @param Value + * @return 子Map列表 + */ + public static List> splitByGroupSize(Map map, int groupSize){ + if(Objects.isNull(map) || map.isEmpty() || groupSize<1){ + //空map或者分组数<1,无法拆分 + return Collections.emptyList(); + } + + List> list = Lists.newArrayListWithCapacity(groupSize); + if(groupSize == 1){ //只有1个分组的情况 + list.add(map); + return list; + } + + int mapSize = map.size(); //键值对总数 + int chunkIndex = 0; //当前分组的下标,[0, groupSize-1] + int restCount = mapSize % groupSize; //平均后剩余的键值对数 + int chunkSize0 = mapSize / groupSize; //每个分组键值对数量 + int chunkSize1 = chunkSize0 + 1; //多分一个 + int chunkSize = chunkIndex subMap = Maps.newHashMapWithExpectedSize(chunkSize);//子Map + + for (Map.Entry entry : map.entrySet()) { + if(count < chunkSize){ + //每个分组按实际分组大小(chunkSize)加入键值对 + subMap.put(entry.getKey(), entry.getValue()); + count ++; //组内计数+1 + }else{ + //结束上一个分组 + list.add(subMap); //当前分组装满了->加入列表 + chunkIndex ++; //分组个数+1 + + //开始下一个分组 + chunkSize = chunkIndex map = new HashMap<>(); + map.put("a", "1"); + map.put("b", "2"); + map.put("c", "3"); + map.put("d", "4"); + map.put("e", "5"); + map.put("f", "6"); + map.put("g", "7"); + + System.out.println("splitByChunkSize: " + splitByChunkSize(map, 3)); + System.out.println("splitByGroupSize: " + splitByGroupSize(map, 3)); + //splitByChunkSize: [{a=1, b=2, c=3}, {d=4, e=5, f=6}, {g=7}] + //splitByGroupSize: [{a=1, b=2, c=3}, {d=4, e=5}, {f=6, g=7}] + } +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/listUtils/OrderUtils.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/listUtils/OrderUtils.java new file mode 100644 index 0000000..afda1f4 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/listUtils/OrderUtils.java @@ -0,0 +1,132 @@ +package org.jeecg.payment.listUtils; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Random; + +/** + * * 订单编码码生成器,生成32位数字编码, + * * @生成规则 1位单号类型+17位时间戳+14位(用户id加密&随机数) + */ +public class OrderUtils { + /** + * 订单类别头 + */ + + private static final String ORDER_CODE = ""; + /** + * 退货类别头 + */ + + private static final String RETURN_ORDER = ""; + /** + * 退款类别头 + */ + + private static final String REFUND_ORDER = ""; + + + /** + * 随即编码 + */ + + private static final int[] r = new int[]{7, 9, 6, 2, 8, 1, 3, 0, 5, 4}; + /** + * 用户id和随机数总长度 + */ + + private static final int maxLength = 14; + + /** + * 根据id进行加密+加随机数组成固定长度编码 + */ + private static String toCode(Integer userId) { + String idStr = userId.toString(); + StringBuilder idsbs = new StringBuilder(); + for (int i = idStr.length() - 1; i >= 0; i--) { + idsbs.append(r[idStr.charAt(i) - '0']); + } + return idsbs.append(getRandom(maxLength - idStr.length())).toString(); + } + + /** + * 生成时间戳 + */ + private static String getDateTime() { + DateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); + return sdf.format(new Date()); + } + + /** + * 生成时间戳 + */ + private static String getDateTimeV2() { + DateFormat sdf = new SimpleDateFormat("yyMMddHHmmss"); + return sdf.format(new Date()); + } + + /** + * 生成固定长度随机码 + * + * @param n 长度 + */ + + private static long getRandom(long n) { + long min = 1, max = 9; + for (int i = 1; i < n; i++) { + min *= 10; + max *= 10; + } + long rangeLong = (((long) (new Random().nextDouble() * (max - min)))) + min; + return rangeLong; + } + + + /** + * 生成不带类别标头的编码 + * + * @param userId + */ + private static synchronized String getCode(Integer userId) { + userId = userId == null ? 10000 : userId; + return getDateTime()+ userId;//+ toCode(userId); + } + + + /** + * 生成订单单号编码(调用方法) + * @param userId 网站中该用户唯一ID 防止重复 + */ + + public static String getOrderCode(Integer userId) { + return ORDER_CODE + getCode(userId); + } + /** + * 生成订单单号编码(调用方法) + */ + + public static String getOrderCodeV2() { + return ORDER_CODE + getDateTimeV2(); + } + + + /** + * 生成退货单号编码(调用方法) + * @param userId 网站中该用户唯一ID 防止重复 + */ + public static String getReturnCode(Integer userId) { + return RETURN_ORDER + getCode(userId); + } + + + /** + * 生成退款单号编码(调用方法) + * @param userId 网站中该用户唯一ID 防止重复 + */ + public static String getRefundCode(Integer userId) { + return REFUND_ORDER + getCode(userId); + } + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StBbzxOrderInfoMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StBbzxOrderInfoMapper.java new file mode 100644 index 0000000..a5625de --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StBbzxOrderInfoMapper.java @@ -0,0 +1,78 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; +import org.jeecg.common.system.vo.ScreenVo; +import org.jeecg.payment.entity.StBbzxOrderInfo; +import org.jeecg.payment.vo.OrderShow; +import org.jeecg.payment.vo.StatisticsTheAmountSpentVo; +import org.jeecg.restaurant.vo.StatisticsBusinessVo; +import org.jeecg.statistics.vo.StEatFoodInfoVo; + +import java.util.List; + +/** + * 报表中心-店内订单 Mapper 接口 + */ +public interface StBbzxOrderInfoMapper extends BaseMapper { + + + + /** + * 自定义分页 + * + * @return + */ +// List selectSwBbzxOrderInfoPage(SwBbzxOrderInfoDTO swBbzxOrderInfo); + + + List selectSwOrderInfoPage(@Param("memberId") String memberId, @Param("openId") String open_id, @Param("orderType")Integer orderType, @Param("startDate")String startDate, @Param("endDate")String endDate, @Param("unionId") String unionId); + + /** + * 支付宝小程序订单列表 + * + * @param memberId + * @param open_id + * @return + */ + List selectSwOrderInfoPage_zfb(String memberId, + @Param("openId") String open_id); + + + List getAllByOpenId(@Param("openId") String openId); + + List getFoodInfoByOrderIdList(@Param("orderIds") List orderIds); + + /** + * 消费金额统计 + * @param canteenIds 食堂id + * @param startDate 开始时间 + * @param endDate 结束时间 + * @return 消费金额统计 + */ + List statisticsTheAmountSpent(@Param("list") List canteenIds, @Param("startDate") String startDate, @Param("endDate") String endDate); + + /** + * 个人消费记录统计 + * @param startDate 开始时间 + * @param endDate 结束时间 + * @param secondDepartId 二级单位id + * @param thirdDepartId 三级部门id + * @return 个人消费记录统计 + */ + List statisticsTheAmountSpentEveryMember(@Param("list") List canteenIds, @Param("name") String name,@Param("payName") String payName,@Param("startDate") String startDate, @Param("endDate") String endDate, + @Param("secondDepartId") String secondDepartId, + @Param("thirdDepartId") String thirdDepartId); + + /** + * 食堂营业表 + * @param startDate 开始时间 + * @param endDate 结束时间 + * @param canteenIdList 二级单位id + * @return 食堂营业表 + */ + List statisticsCanteenBusiness(@Param("list") List canteenIdList, @Param("startDate") String startDate, @Param("endDate") String endDate); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StBbzxOrderInfoPayMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StBbzxOrderInfoPayMapper.java new file mode 100644 index 0000000..0c334e1 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StBbzxOrderInfoPayMapper.java @@ -0,0 +1,17 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.jeecg.payment.entity.StBbzxOrderInfoPay; + +/** + * 支付信息 Mapper 接口 + + */ +public interface StBbzxOrderInfoPayMapper extends BaseMapper { + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StClientMemberBalanceMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StClientMemberBalanceMapper.java new file mode 100644 index 0000000..4350f6f --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StClientMemberBalanceMapper.java @@ -0,0 +1,119 @@ +package org.jeecg.payment.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import org.jeecg.payment.dto.*; +import org.jeecg.payment.entity.StClientMemberBalance; +import org.jeecg.payment.entity.StClientMemberTransactionRecord; +import org.jeecg.payment.vo.*; +import org.jeecg.user.entity.SysUserSub; +import org.jeecg.user.vo.SysUserSubInfo; + +import java.util.List; + +/** + * @author Ls. + * @date 2021/6/16. + */ + +public interface StClientMemberBalanceMapper extends BaseMapper { + /** + * 后台列表查询 + * + * @return + */ + List pageList(Page page, @Param("vo") StClientMemberTransactionRecordDTO query); + + /** + * 后台列表查询 + * + * @return + */ + List pageList(@Param("vo") StClientMemberTransactionRecordDTO query); + /** + * 余额/积分调整 + * + * @param query + * @return + */ + int changeBalanceAndIntegral(StClientMemberChangeBalanceDTO query); + + /** + * 会员充值 + * + * @return + */ + int memberTopUp(StClientMemberTopUpDTO query); + + /** + * 撤销充值用户vo + * + * @param memberId + * @return + */ + StClientMemberBackOutUserVO selectBackOutUser(@Param("memberId") String memberId, + @Param("eaId") String eaId); + + /** + * 撤销充值的交易流水查询 + * + * @param memberId + * @param status 撤销状态码 + * @return + */ + List selectRunningTabAsBackOut(@Param("memberId") String memberId, + @Param("list") List list, + @Param("status") Integer status, + @Param("canteenId") String canteenId); + + /** + * 查询最新的一条数据 + * + * @param memberId + * @param list status状态集合 + * @return + */ + StClientMemberBalance selectLastMsgByTime(@Param("memberId") String memberId, + @Param("list") List list); + + StClientMemberBalance selectLastMsgByPayTime(@Param("memberId") String memberId, + @Param("status") Integer status); + + /** + * 撤销充值 + * + * @param query + * @return + */ + int backOutTopUp(StClientMemberBackTopUpDto query); + + /** + * 会员消费 + * + * @param param + * @return + */ + int memberSpend(StClientMemberSpendMapperDTO param); + + /** + * @param userId 用户唯一标识 + * @return 会员消费记录 + */ + List memberTranRecordMapper(Page page, @Param("userId") String userId, @Param("status") String status); + + StMiniMineBalanceVo getBalanceInfo(@Param("userId") String userId); + + SysUserSub getOneByUserId(@Param("userId") String userId); + + SysUserSub getOneByCardCode(@Param("cardCode") String cardCode); + + /** + * 批量根据身份证号查询用户钱包(用户表和钱包都有数据才能查询到记录) + * @param list + * @return + */ + List getSubUserListByCardNoList(@Param("list") List list); + + StMiniMineBalanceVo getBalanceByUserId(@Param("userId") String userId); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StClientMemberLevelDeductRuleMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StClientMemberLevelDeductRuleMapper.java new file mode 100644 index 0000000..63a5b94 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StClientMemberLevelDeductRuleMapper.java @@ -0,0 +1,27 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import org.jeecg.payment.entity.StClientMemberLevelDeductRule; +import org.jeecg.payment.entity.StClientMemberLevelDemotionTime; +import org.jeecg.payment.vo.StClientMemberLevelDeductRuleVo; +import org.jeecg.payment.vo.StClientMemberLevelDemotionTimeVO; + +import java.util.List; + +/** + * 扣费规则Mapper 接口 + */ +public interface StClientMemberLevelDeductRuleMapper extends BaseMapper { + + /** + * 自定义分页 + * @return + */ + List selectStClientMemberLevelDeductRulePage(Page page, @Param("vo") StClientMemberLevelDemotionTimeVO stClientMemberLevelDeductRule); + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StClientMemberLevelDemotionTimeMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StClientMemberLevelDemotionTimeMapper.java new file mode 100644 index 0000000..2c2d01f --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StClientMemberLevelDemotionTimeMapper.java @@ -0,0 +1,27 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import org.jeecg.payment.entity.StClientMemberLevelDemotionTime; +import org.jeecg.payment.vo.StClientMemberLevelDemotionTimeVO; + +import java.util.List; + +/** + * 储值规则Mapper 接口 + */ +public interface StClientMemberLevelDemotionTimeMapper extends BaseMapper { + + /** + * 自定义分页 + * + * @param stClientMemberLevelDemotionTime + * @return + */ + List selectSwClientMemberLevelDemotionTimePage(Page page,@Param("vo") StClientMemberLevelDemotionTimeVO stClientMemberLevelDemotionTime); + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StClientMemberTransactionRecordMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StClientMemberTransactionRecordMapper.java new file mode 100644 index 0000000..723f8f6 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/StClientMemberTransactionRecordMapper.java @@ -0,0 +1,38 @@ +package org.jeecg.payment.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import org.jeecg.payment.dto.StClientMemberTransactionRecordDTO; +import org.jeecg.payment.entity.StClientMemberTransactionRecord; +import org.jeecg.payment.vo.InOutRecordInfoVo; +import org.jeecg.payment.vo.StClientMemberTransactionRecordVO; + +import java.util.List; + + +/** + 交易记录 + */ + +public interface StClientMemberTransactionRecordMapper extends BaseMapper { + /** + * 列表查询 + * @return + */ + List pageList(Page page, @Param("vo") StClientMemberTransactionRecordDTO query); + + /** + * 列表查询 + * @return + */ + List pageList(@Param("vo") StClientMemberTransactionRecordDTO query); + + /** + * 列表查询 + * @return + */ + List pageBalanceRefundList(Page page, @Param("vo") StClientMemberTransactionRecordDTO query); + + InOutRecordInfoVo getOneInOutInfo(@Param("recordId") String recordId); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StBbzxOrderInfoMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StBbzxOrderInfoMapper.xml new file mode 100644 index 0000000..36524dd --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StBbzxOrderInfoMapper.xml @@ -0,0 +1,383 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StBbzxOrderInfoPayMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StBbzxOrderInfoPayMapper.xml new file mode 100644 index 0000000..d922a4a --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StBbzxOrderInfoPayMapper.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StClientMemberBalanceMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StClientMemberBalanceMapper.xml new file mode 100644 index 0000000..7ba24fb --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StClientMemberBalanceMapper.xml @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + + + + + + + id, member_id, top_up_balance, reward_balance, integral_balance, consumption_count, + consumption_total, last_consumption_time, first_top_up_time, create_user_no, create_time, + update_user_no, update_time, is_del, canteen_id + + + + + + UPDATE st_client_member_balance + + + + + + + top_up_balance = top_up_balance + #{topUpBalance,jdbcType=DECIMAL}, + top_up_total = top_up_total + #{topUpBalance,jdbcType=DECIMAL} + + + reward_balance = reward_balance + #{rewardBalance,jdbcType=DECIMAL} + + + + integral_balance = integral_balance + #{integralBalance,jdbcType=DECIMAL} + + + + + + top_up_balance = top_up_balance - #{topUpBalance,jdbcType=DECIMAL} + + + reward_balance = reward_balance - #{rewardBalance,jdbcType=DECIMAL} + + + + integral_balance = integral_balance - #{integralBalance,jdbcType=DECIMAL} + + + + + + WHERE id = #{id,jdbcType=INTEGER} + + + + AND top_up_balance >= #{topUpBalance,jdbcType=DECIMAL} + + + AND reward_balance >= #{rewardBalance,jdbcType=DECIMAL} + + + AND integral_balance >= #{integralBalance,jdbcType=DECIMAL} + + + + + + + UPDATE st_client_member_balance + + + top_up_balance = top_up_balance + #{topUpBalance,jdbcType=DECIMAL}, + top_up_total = top_up_total + #{topUpBalance,jdbcType=DECIMAL}, + + + reward_balance = reward_balance + #{rewardBalance,jdbcType=DECIMAL}, + + + integral_balance = integral_balance + #{integralBalance,jdbcType=DECIMAL}, + + + invoice_total = invoice_total + #{invoiceBalance,jdbcType=DECIMAL}, + + is_sync = 0 + + where id = #{id} + + + + + + + + + + + + + + + + UPDATE st_client_member_balance SET + top_up_balance = top_up_balance - #{topUpSubtractBalance,jdbcType=DECIMAL}, + top_up_total = top_up_total - #{topUpSubtractBalance,jdbcType=DECIMAL}, + reward_balance = reward_balance - #{rewardSubtractBalance,jdbcType=DECIMAL}, + integral_balance = integral_balance - #{integralSubtractBalance,jdbcType=INTEGER}, + is_sync = 0 + WHERE id = #{id,jdbcType=INTEGER} + AND top_up_balance = #{oldTopUpBalance,jdbcType=DECIMAL} + AND reward_balance = #{oldRewardBalance,jdbcType=DECIMAL} + + + + + update st_client_member_balance set + + top_up_balance = top_up_balance - #{topUpBalance,jdbcType=DECIMAL}, + + + reward_balance = reward_balance - #{rewardBalance,jdbcType=DECIMAL}, + + consumption_count = consumption_count + 1, + consumption_total = consumption_total + #{consumptionTotal,jdbcType=DECIMAL}, + is_sync = 0, + last_consumption_time = now() + where id = #{id,jdbcType=INTEGER} + + + + + + + + + + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StClientMemberLevelDeductRuleMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StClientMemberLevelDeductRuleMapper.xml new file mode 100644 index 0000000..a6aedd3 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StClientMemberLevelDeductRuleMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StClientMemberLevelDemotionTimeMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StClientMemberLevelDemotionTimeMapper.xml new file mode 100644 index 0000000..9ea1d31 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StClientMemberLevelDemotionTimeMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StClientMemberTransactionRecordMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StClientMemberTransactionRecordMapper.xml new file mode 100644 index 0000000..5a4208b --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/mapper/xml/StClientMemberTransactionRecordMapper.xml @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, member_id, serial_no, order_no, `status`, transaction_amount, payment_method, + stored_balance, reward_balance, integral_balance, pay_time, update_status, create_by, + create_time, update_by, update_time, is_del, canteen_id + + + + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStBbzxOrderInfoPayService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStBbzxOrderInfoPayService.java new file mode 100644 index 0000000..7356157 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStBbzxOrderInfoPayService.java @@ -0,0 +1,16 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.service; + + +import org.jeecg.common.system.base.service.JeecgService; +import org.jeecg.payment.entity.StBbzxOrderInfoPay; + +/** + * 支付信息 服务类 + */ +public interface IStBbzxOrderInfoPayService extends JeecgService { + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStBbzxOrderInfoService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStBbzxOrderInfoService.java new file mode 100644 index 0000000..4cb1870 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStBbzxOrderInfoService.java @@ -0,0 +1,217 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.jeecg.common.system.base.service.JeecgService; +import org.jeecg.payment.vo.*; +import org.jeecg.payment.OrderInfo; +import org.jeecg.payment.entity.StBbzxOrderInfo; +import org.jeecg.restaurant.vo.StatisticsBusinessVo; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + + +/** + * 订单 服务类 + */ +public interface IStBbzxOrderInfoService extends JeecgService { + + + +// /*** +// * 微信小程序充值 +// * @param orderInfo +// * @return +// */ +// JSONObject saveXCXorderInfoTopUp(OrderInfo orderInfo); + + //获取余额信息+充值规则模板 + StMiniMineBalanceVo mineBalanceInfo(String userId, String canteenId); + + /** + * 用户支出/充值记录列表 + * @param userId 用户标识 + */ + IPage getSpendingOrRechargeInfoListV2(String userId, Integer pageNum, Integer pageSize); + + /** + * 根据订单号 获取订单信息 + * + * @param orderCode + * @return + */ + StBbzxOrderInfo getSwBbzxOrderInfo(String orderCode); + + /** + * 根据payId查询支付订单 + * + * @param payId + * @return + */ + StBbzxOrderInfo getSwBbzxOrderInfoByPayId(String payId); + +// /** +// * 退单 +// * +// * @param orderNo +// * @return +// */ +// boolean returnOrder(String orderNo); +// +// /** +// * 线上消费退款 +// * +// * @param payDto +// * @return +// */ +// Result returnPayInfo(RefundDTO payDto); +// +// +// /** +// * 线上充值退款 +// * +// * @param payDto +// * @return +// */ +// Result returnPayInfoTopUpOnline(RefundDTO payDto); + + + void updateMemberIdByOpenId(String openId,String memberId,String memberName); + + + /** + * + * @param orderId 订单主表id + * @param orderFrom 订单来源(1平板端 、2收银端、3自助点餐机、4小程序、5自助餐) + * @param payCode 小程序 支付编码 + * @param payType 支付类型: 1是支付宝 2微信 3现金 4会员 5混合 + * @param orderFromSign 支付用户id(微信支付 = openid、支付宝支付 = 支付宝id) + * @param bh 混合支付时,会员支付的金额 + * @param tradeOrderNo 渠道支付返回的三方交易订单号 + * @param tarnsCode 混合支付时 经过随机数3位拼接的自定义余额支付订单号 + */ + void updateOrderFeign(String orderId, int orderFrom, String payCode, Integer payType, String orderFromSign, + StBbzxOrderInfo orderInfo, BigDecimal bh, String tradeOrderNo, String tarnsCode); + + /** + * 消费金额统计 + * @param canteenId 食堂id + * @param startDate 开始时间 + * @param endDate 结束时间 + * @param pageNo 页数 + * @param pageSize 条数 + * @return 消费金额统计 + */ + IPage getStatisticsTheAmountSpentList(String canteenId, String startDate, String endDate, Integer pageNo, Integer pageSize); + + + /** + * 消费金额统计 + * @param canteenId 食堂id + * @param startDate 开始时间 + * @param endDate 结束时间 + * @return 消费金额统计 + */ + List getStatisticsTheAmountSpentList(String canteenId, String startDate, String endDate); + /** + * 个人消费记录统计 + * @param startDate 开始时间 + * @param endDate 结束时间 + * @param secondDepartId 二级单位id + * @param thirdDepartId 三级部门id + * @param pageNo 页数 + * @param pageSize 条数 + * @return 个人消费记录统计 + */ + IPage statisticsTheAmountSpentEveryMember(String canteenId,String name,String payName,String startDate, String endDate, String secondDepartId,String thirdDepartId,Integer pageNo, Integer pageSize); + + + /** + * 个人消费记录统计导出 + * @param startDate 开始时间 + * @param endDate 结束时间 + * @param secondDepartId 二级单位id + * @param thirdDepartId 三级部门id + * @return 个人消费记录统计 + */ + List statisticsTheAmountSpentEveryMember(String canteenId,String name,String payName,String startDate, String endDate, String secondDepartId,String thirdDepartId); + + + /** + * 食堂营业查询报表 + * @param startDate 开始时间 + * @param endDate 结束时间 + * @param pageNo 页数 + * @param pageSize 条数 + * @return 食堂营业查询报表 + */ + IPage statisticsCanteenBusiness(String canteenId,String startDate, String endDate, String secondDepartId,String thirdDepartId,Integer pageNo, Integer pageSize); + + /** + * 食堂营业查询报表-导出 + * @param startDate 开始时间 + * @param endDate 结束时间 + * @return 食堂营业查询报表 + */ + List statisticsCanteenBusiness(String canteenId, String startDate, String endDate, String secondDepartId, String thirdDepartId); + + /** + * 统一生成订单 + * @param restId + * @param userId + * @param plateNumber + * @return + */ + StBbzxOrderInfo crateOrderInfo(Date dateTime, String restId, String userId, String plateNumber, String timesName,StRfidVo stRfidVo); + + StBbzxOrderInfo buildOrderInfoNew(Date dateTime, String restId, String dinnerType, String plateNumber, String userId, String memberName, + String memberPhone, Integer deductionType, StClientMemberLevelDeductRuleVo levelDeductRuleVo, String remark,StRfidVo stRfidVo); + + /** + * 离线订单 + * @param dateTime + * @param restId + * @param dinnerType + * @param plateNumber + * @param userId + * @param memberName + * @param memberPhone + * @param deductionType + * @param levelDeductRuleVo + * @param remark + * @param stRfidVo + * @param miniMineBalanceVo + * @return + */ + StBbzxOrderInfo buildOrderInfoNewOffLine(Date dateTime, String restId, String dinnerType, String plateNumber, String userId, String memberName, + String memberPhone, Integer deductionType, StClientMemberLevelDeductRuleVo levelDeductRuleVo, String remark,StRfidVo stRfidVo,StMiniMineBalanceVo miniMineBalanceVo); + + /** + * 根据userId + 日期 + 餐次 + 订单状态,获取订单信息 + * @param userId + * @param eatDay + * @param dinnerType + * @param orderStatus + * @param businessModel 经营模式、扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费) + * @return + */ + StBbzxOrderInfo getSwBbzxOrderInfoByUserId(String userId,String eatDay,String dinnerType,Integer orderStatus,String restId, + Integer businessModel ); + + /** + * 定时任务 根据userId + 日期 + 订单状态,获取未支付的订单信息 + * @param userId + * @param eatDay + * @param orderStatus + * @param businessModel 经营模式、扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费) + * @return + */ + List getSwBbzxOrderInfoListByUserId(String userId, String eatDay, String dinnerType, Integer orderStatus, String restId, + Integer businessModel); + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStClientMemberBalanceService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStClientMemberBalanceService.java new file mode 100644 index 0000000..dec37fc --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStClientMemberBalanceService.java @@ -0,0 +1,196 @@ +package org.jeecg.payment.service; + + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.base.service.JeecgService; +import org.jeecg.payment.dto.StClientMemberChangeBalanceDTO; +import org.jeecg.payment.dto.StClientMemberTopUpDTO; +import org.jeecg.payment.dto.StClientMemberTransactionRecordDTO; +import org.jeecg.payment.dto.StClientUserInfoShopPageDTO; +import org.jeecg.payment.entity.StBbzxOrderInfo; +import org.jeecg.payment.entity.StClientMemberBalance; +import org.jeecg.payment.entity.StClientMemberTransactionRecord; +import org.jeecg.payment.vo.*; +import org.springframework.web.multipart.MultipartFile; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +/** + * @author Ls. + * @date 2021/6/16. + */ + +public interface IStClientMemberBalanceService extends JeecgService { + + + /** + * 后台用户充值列表 + * @param + * @return + */ + IPage memberClientMemberBalanceService(Page page, StClientMemberTransactionRecordDTO stClientUserInfoShopPageDTO); + + + List getSpendingOrRechargeInfoListExport(StClientMemberTransactionRecordDTO stClientUserInfoShopPageDTO); + + /** + * 小程序 会员卡充值 + * @param memberId memberId + * @param topUpBalance 交易金额 + * @param paymentMethod 支付方式 + * @return 赠送金额+赠送积分 + */ + Map memberTopUpXcx(String memberId, + BigDecimal topUpBalance, + int paymentMethod,String orderNo); + + + + /** + * 会员交易记录 + * @param userId + * @return 会员交易记录 + */ + IPage memberTranRecordService(Page page,String userId,String status); + + + /** + * 会员充值 + * + * @param canteenId + * @param userId + * @param value + * @param paymentMethod + * @return + */ + Map memberTopUp_new_yc(String canteenId, String userId, BigDecimal value, int paymentMethod); + + /** + * 主键id + * 撤销充值列表查询 + */ + StClientMemberBackOutVO backOutTopUpList(StClientMemberBackOutTopUpDTO query); + + /** + * 撤销充值 + * + * @return + */ + Result backOutTopUpOffLine(StClientMemberBackTopUpQueryDto queryDto); + + + /** + * 根据订单号获取 充值订单记录信息 + * + * @param payOrderNo 小程序充值成功后微信订单号 + * @return 订单记录信息 + */ + StClientMemberTransactionRecord getRecordInfoAboutRechargeByPayOrderNo(String payOrderNo, String orderNo, List integerList); + + /** + * 充值退款后逻辑 + * 通过充值记录 生成退款记录 , 计算balance表数据 + * @param czRecord 充值记录 + */ + void backOrder( StClientMemberTransactionRecord czRecord); + + + /** + * 会员消费 + * + * @param query + */ + StClientMemberSpendVO memberSpend(StClientMemberSpendDTO query) throws Exception; + + + /** + * 会员消费 + * + * @param query + */ + StClientMemberSpendVO memberSpendV2(StClientMemberSpendDTO query) throws Exception; + + + /** + * 用户余额消费统一扣费方法 + * query.id 必填:用户id + * query.orderNo 必填:订单号 + * + * @return StClientMemberSpendVO + * @throws Exception + */ + StClientMemberSpendVO memberSpendV3(StClientMemberSpendDTO query); + + /** + * 用户余额消费统一扣费方法 + * query.id 必填:用户id + * query.orderNo 必填:订单号 + * + * @return StClientMemberSpendVO + * @throws Exception + */ + StClientMemberSpendVO memberSpendOffline(StClientMemberSpendDTO query, StBbzxOrderInfo stBbzxOrderInfo); + + + /** + * 补偿人脸扣费闸机离线扣费方法(只考虑透支消费版本) + * StClientMemberSpendDTO 支付信息 + * + * @return StClientMemberSpendVO + * @throws Exception + */ + List memberSpendOfflineCompensate(List orderIds); + /** + * 积分/余额调整 + * + * @param query + * @return + */ + Result changeBalanceAndIntegral(StClientMemberChangeBalanceDTO query); + + /** + * 后台员卡充值 + * + * @return + */ + boolean memberTopUp(StClientMemberTopUpDTO query); + + /** + * 充值明细导入 + * @param snList + */ + Map> importRechargeData(List snList); + + /** + * 充值明细导入(全量校验通过后才执行真实充值) + * + * @param orgList 待导入的充值明细列表 + */ + Map> importRechargeDataV2(List orgList); + + public List parseExcel(MultipartFile file, String canteenId); + /** + * 计算本次扣费金额(一次扣费模式返回实际扣费金额,按量扣费直接返回0) + * @param levelDeductRuleVo 扣费规则 + * @param dinnerType 餐次 + * @param totalDeduction 费用总计(一次扣费模式=固定/固定+补贴,按量扣费模式=实际金额) + * @param spendMoney 消费金额 + * @param rewardDeduction 补贴金额 + * @param userInnerType 用户类型(内部用户=0,外部用户=1) + */ + StClientMemberSpendDataVO fixedFee(StClientMemberLevelDeductRuleVo levelDeductRuleVo, String dinnerType, + BigDecimal totalDeduction, BigDecimal spendMoney, BigDecimal rewardDeduction, Integer userInnerType); + + + Result getUserBalance(String userId); + /** + * 根据卡号查询用户钱包 + * @param cardCode + * @return + */ + Result getUserBalanceByCardCode(String cardCode); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStClientMemberLevelDeductRuleService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStClientMemberLevelDeductRuleService.java new file mode 100644 index 0000000..f227428 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStClientMemberLevelDeductRuleService.java @@ -0,0 +1,66 @@ + +package org.jeecg.payment.service; + + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.base.service.JeecgService; +import org.jeecg.payment.entity.StClientMemberLevelDeductRule; +import org.jeecg.payment.entity.StClientMemberLevelDemotionTime; +import org.jeecg.payment.vo.*; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.Date; +import java.util.List; +import java.util.Map; + + +/** + * 扣费规则 服务类 + */ +public interface IStClientMemberLevelDeductRuleService extends JeecgService { + + /** + * 列表查询 + * + * @param query + * @return + */ + IPage selectStClientMemberLevelDemotionTimePage(StClientMemberLevelDemotionTimeVO query); + + Boolean saveOrUpdateService(StClientMemberLevelDeductRuleVo vo); + + StClientMemberLevelDeductRuleVo getOneService(StClientMemberLevelDeductRuleVo stClientMemberLevelDeductRuleVo); + + + StClientMemberLevelDeductRuleVo getDeductRuleByCanteenId(String canteenId); + + /** + * 组装扣费规则vo + * + * @param vo + * @param memberLevel + * @return + */ + StClientMemberLevelDeductRuleVo bulidStClientMemberLevelDeductRule(StClientMemberLevelDeductRuleVo vo, StClientMemberLevelDeductRule memberLevel); + + + Result bindUserOfflineCardCode(@RequestBody StRfidoffLineVo stRfidoffLineVo); + + + /** + * 异步生成订单和进行实际扣费流程 + * @param dateTime 订单时间 + * @param restId + * @param userId + * @param stRfidVo + * @param timesName + */ + public String syncMemberSpendOffLine(Date dateTime, String restId, String userId, StRfidVo stRfidVo, String timesName, StMiniMineBalanceVo miniMineBalanceVo, StClientMemberLevelDeductRuleVo levelDeductRuleVo); + + + public Map> groupByMeal(List rfidVos, String restId); + + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStClientMemberLevelDemotionTimeService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStClientMemberLevelDemotionTimeService.java new file mode 100644 index 0000000..251b2b0 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStClientMemberLevelDemotionTimeService.java @@ -0,0 +1,52 @@ + +package org.jeecg.payment.service; + + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.base.service.JeecgService; +import org.jeecg.payment.dto.StClientMemberTransactionRecordDTO; +import org.jeecg.payment.entity.StClientMemberLevelDemotionTime; +import org.jeecg.payment.vo.StClientMemberLevelDemotionTimeVO; +import org.jeecg.payment.vo.StClientMemberTransactionRecordVO; +import org.jeecg.payment.vo.StMemberLevelStoredVO; + +import java.util.List; +import java.util.Map; + + +/** + * 储值规则 服务类 + */ +public interface IStClientMemberLevelDemotionTimeService extends JeecgService { + + + /** + * 会员充值奖励后台接口 + * levelId:关系表id + * topUpPrice:充值金额 + */ + Map topUpAwardService(Integer levelId, Float topUpPrice,String canteenId); + /** + * 列表查询 + * + * @param query + * @return + */ + IPage selectStClientMemberLevelDemotionTimePage(StClientMemberLevelDemotionTimeVO query); + + Boolean saveOrUpdateService(StClientMemberLevelDemotionTimeVO swClientMemberLevelDemotionTime); + + StClientMemberLevelDemotionTimeVO getOneService(StClientMemberLevelDemotionTime swClientMemberLevelDemotionTime); + + /** + * + * @return 店铺会员卡权益规则信息 + */ + List appMemberLevelStoredService(); + + Result memberLevelDemotionTimeByCanteenId(String canteenId); + + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStClientMemberTransactionRecordService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStClientMemberTransactionRecordService.java new file mode 100644 index 0000000..8ff2cc6 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/IStClientMemberTransactionRecordService.java @@ -0,0 +1,39 @@ +package org.jeecg.payment.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.jeecg.common.system.base.service.JeecgService; +import org.jeecg.payment.dto.StClientMemberTransactionRecordDTO; +import org.jeecg.payment.entity.StClientMemberTransactionRecord; +import org.jeecg.payment.vo.InOutRecordInfoVo; +import org.jeecg.payment.vo.StClientMemberTransactionRecordVO; +import org.jeecg.restaurant.vo.SlBaseRestaurantVo; + +import java.util.List; + +/** + * 会员卡交易记录 + */ + +public interface IStClientMemberTransactionRecordService extends JeecgService { + /** + * 列表查询 + * + * @param query + * @return + */ + IPage pageQuery(StClientMemberTransactionRecordDTO query); + + /** + * 列表查询导出 + * + * @param query + * @return + */ + List queryList(StClientMemberTransactionRecordDTO query); + + IPage pageBalanceRefundList(Page page, StClientMemberTransactionRecordDTO query); + + InOutRecordInfoVo getOneInOutInfo(String recordId); + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StBbzxOrderInfoPayServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StBbzxOrderInfoPayServiceImpl.java new file mode 100644 index 0000000..c3b0d6e --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StBbzxOrderInfoPayServiceImpl.java @@ -0,0 +1,20 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.service.impl; + + +import org.jeecg.common.system.base.service.impl.JeecgServiceImpl; +import org.jeecg.payment.entity.StBbzxOrderInfoPay; +import org.jeecg.payment.mapper.StBbzxOrderInfoPayMapper; +import org.jeecg.payment.service.IStBbzxOrderInfoPayService; +import org.springframework.stereotype.Service; + +/** + * 支付信息 服务实现类 + */ +@Service +public class StBbzxOrderInfoPayServiceImpl extends JeecgServiceImpl implements IStBbzxOrderInfoPayService { + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StBbzxOrderInfoServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StBbzxOrderInfoServiceImpl.java new file mode 100644 index 0000000..e6cef60 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StBbzxOrderInfoServiceImpl.java @@ -0,0 +1,954 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.service.impl; + +import cn.hutool.core.date.DateUtil; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xkcoding.http.util.StringUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.system.base.service.impl.JeecgServiceImpl; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.system.vo.ScreenVo; +import org.jeecg.common.util.DateUtils; +import org.jeecg.payment.entity.StBbzxOrderInfo; +import org.jeecg.payment.entity.StBbzxOrderInfoPay; +import org.jeecg.payment.entity.StClientMemberLevelDemotionTime; +import org.jeecg.payment.entity.StClientMemberTransactionRecord; +import org.jeecg.payment.enums.OrderStatus; +import org.jeecg.payment.enums.UserTypeEnum; +import org.jeecg.payment.listUtils.ListUtil; +import org.jeecg.payment.listUtils.OrderUtils; +import org.jeecg.payment.mapper.StBbzxOrderInfoMapper; +import org.jeecg.payment.mapper.StBbzxOrderInfoPayMapper; +import org.jeecg.payment.mapper.StClientMemberBalanceMapper; +import org.jeecg.payment.service.*; +import org.jeecg.payment.vo.*; +import org.jeecg.restaurant.vo.StatisticsBusinessVo; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.text.MessageFormat; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; + + +/** + * 报表中心-店内订单 服务实现类 + + */ +@Slf4j +@Service +public class StBbzxOrderInfoServiceImpl extends JeecgServiceImpl implements IStBbzxOrderInfoService { + +// +// @Resource +// private IPayInfo payInfo; + + @Resource + private IStClientMemberBalanceService stClientMemberBalanceService; + @Resource + private IStClientMemberLevelDeductRuleService iStClientMemberLevelDeductRuleService; + @Resource + private StClientMemberBalanceMapper stClientMemberBalanceMapper; + @Resource + private IStClientMemberLevelDemotionTimeService swClientMemberLevelDemotionTimeService; + + + @Resource + private IStBbzxOrderInfoPayService swBbzxOrderInfoPayService; + @Resource + private RedisTemplate redisTemplate; +// /*** +// * 微信小程序充值 +// * @param orderInfo +// * @return +// */ +// @Override +// public JSONObject saveXCXorderInfoTopUp(OrderInfo orderInfo) { +// String userId = JwtUtil.getUserId(); +// String openId = JwtUtil.getOpenId(); +// String orderNoTopUp = userId+"-"+OrderUtils.getOrderCodeV2(); +// //拉起微信支付 +// BigDecimal b = BigDecimal.valueOf(orderInfo.getOriginalPrice()).setScale(2, BigDecimal.ROUND_HALF_UP); +// Result r = payInfo.wxXcxDoPay(orderNoTopUp, "", "1", "小程序充值" + orderNoTopUp, b, openId,userId); +// String result = r.getResult().toString(); +// JSONObject orderCode = JSONObject.parseObject(result); +// orderCode.put("orderCode", orderNoTopUp); +// return orderCode; +// } + + + /** + * + * 获取余额信息+充值规则模板 + * @param userId 小程序标识 + */ + @Override + public StMiniMineBalanceVo mineBalanceInfo(String userId, String canteenId){ + //获取 + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper(); + lambdaQueryWrapper.eq(StClientMemberLevelDemotionTime::getIsEnable, 1); + if(StringUtils.isNotBlank(canteenId)){ + lambdaQueryWrapper.eq(StClientMemberLevelDemotionTime::getCanteenId, canteenId); + } + StClientMemberLevelDemotionTime memberLevel = swClientMemberLevelDemotionTimeService.getOne(lambdaQueryWrapper); + + StMiniMineBalanceVo balanceInfo = stClientMemberBalanceMapper.getBalanceInfo(userId); + List storedVOS = new ArrayList<>(); + if(memberLevel != null){ + if(StringUtil.isNotEmpty(memberLevel.getStoredRule())){ + storedVOS = com.alibaba.fastjson.JSON.parseArray(memberLevel.getStoredRule(), StMemberLevelStoredVO.class); + } + } + balanceInfo.setStoredVOS(storedVOS); + return balanceInfo; + } + + + /** + * 用户支出/充值记录列表 + * @param userId 用户标识 + */ + @Override + public IPage getSpendingOrRechargeInfoListV2(String userId,Integer pageNum, Integer pageSize) { + Page page = new Page<>(pageNum, pageSize); + Page recordListVoPage = new Page<>(pageNum, pageSize); + IPage memberTransactionRecord = stClientMemberBalanceService.memberTranRecordService(recordListVoPage,userId,null); + List list = memberTransactionRecord.getRecords(); + if(CollectionUtils.isEmpty(list)){ + return new Page<>(pageNum, pageSize, 0); + } + List infos = new ArrayList<>(); + list.forEach(v->{ + String statusName = ""; + //根据获取类型 + if(102 == v.getStatus()){ + statusName = "充值"; + }else if (103 == v.getStatus()){ + statusName = "撤销充值"; + }else { + statusName = "支出"; + } + StRecordListInfo info = new StRecordListInfo(); + info.setName(statusName); + info.setStatus(v.getStatus()); + info.setOrderNo(v.getOrderNo()); + info.setRewardBalance(v.getRewardBalance()); + info.setAvailableBalance(v.getAvailableBalance()); + info.setTransactionAmount(v.getTransactionAmount()); + info.setPayTime(DateUtil.format(v.getPayTime(),"yyyy-MM-dd HH:mm")); + infos.add(info); + }); + page.setTotal(recordListVoPage.getTotal()); + page.setPages(recordListVoPage.getPages()); + page.setSize(recordListVoPage.getSize()); + page.setCurrent(recordListVoPage.getCurrent()); + return page.setRecords(infos); + } + + @Override + public StBbzxOrderInfo getSwBbzxOrderInfo(String orderCode) { + LambdaQueryWrapper wrapperorder = new LambdaQueryWrapper<>(); + wrapperorder.eq(StBbzxOrderInfo::getOrderNo, orderCode); + StBbzxOrderInfo orderInfo1 = this.getBaseMapper().selectOne(wrapperorder); + return orderInfo1; + } + + /** + * 根据payId查询支付订单 + * @param payId + * @return + */ + @Override + public StBbzxOrderInfo getSwBbzxOrderInfoByPayId(String payId) { + LambdaQueryWrapper wrapperorder = new LambdaQueryWrapper<>(); + wrapperorder.eq(StBbzxOrderInfo::getPayId, payId); + StBbzxOrderInfo orderInfo1 = this.getBaseMapper().selectOne(wrapperorder); + return orderInfo1; + } +// +// @Override +// public boolean returnOrder(String orderNo) { +// StBbzxOrderInfo orderInfo = this.getSwBbzxOrderInfo(orderNo); +// //支付方式 +// LambdaQueryWrapper queryDayWrapper = new LambdaQueryWrapper<>(); +// queryDayWrapper.in(StBbzxOrderInfoPay::getOrderId, orderInfo.getId()); +// queryDayWrapper.eq(StBbzxOrderInfoPay::getIsDel, 0); +// List payInfos = swBbzxOrderInfoPayService.getBaseMapper().selectList(queryDayWrapper); +// boolean r = true; +// try { +// if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(payInfos)) { +// for (StBbzxOrderInfoPay pay : payInfos) { +// RefundDTO refundDTO = new RefundDTO(); +// refundDTO.setTotalFee(pay.getPayPrice()); +// refundDTO.setPayOrderNo(pay.getPayCode()); +// if (pay.getPayType() == 1) {//支付宝 +// +// payInfo.refund(refundDTO); +// } else if (pay.getPayType() == 2) {//微信 +// +// payInfo.refund(refundDTO); +// } else if (pay.getPayType() == 4) {//vip +// payInfo.refund(refundDTO); +// } +// } +// } +// +// } catch (Exception e) { +// e.printStackTrace(); +// r = false; +// } +// +// return r; +// } + +// /** +// * 线上消费退款 +// * @param refundDTO +// * @return +// */ +// @Override +// public Result returnPayInfo(RefundDTO refundDTO) { +// return payInfo.refund(refundDTO); +// } +// +// /** +// * 线上充值退款 +// * @param refundDTO +// * @return +// */ +// @Override +// public Result returnPayInfoTopUpOnline(RefundDTO refundDTO) { +// StClientMemberTransactionRecord recordInfoAboutRechargeByPayOrderNo = new StClientMemberTransactionRecord(); +//// if(null == refundDTO.getTotalFee() || refundDTO.getTotalFee().compareTo(BigDecimal.ZERO)<=0){ +// // 微信充值退款之前调用he获取微信退款实际金额接口 +// recordInfoAboutRechargeByPayOrderNo = stClientMemberBalanceService.getRecordInfoAboutRechargeByPayOrderNo(refundDTO.getPayOrderNo(),null,null); +// if(recordInfoAboutRechargeByPayOrderNo == null){ +// return Result.error("查询不到此订单信息!"); +// } +// BigDecimal money = recordInfoAboutRechargeByPayOrderNo.getStoredBalance(); +// if(money.compareTo(BigDecimal.ZERO)<=0){ +// return Result.error("此订单无可退金额!"); +// } +// StClientMemberTransactionRecord stClientMemberTransactionRecord = stClientMemberBalanceService.getRecordInfoAboutRechargeByPayOrderNo(null,recordInfoAboutRechargeByPayOrderNo.getOrderNo(),Arrays.asList(MemberTransactionTypeEnum.BACK_PAY.getCode())); +// if(null != stClientMemberTransactionRecord){ +// return Result.error("此订单已退款"); +// } +// refundDTO.setTotalFee(money); +//// } +// Result r = payInfo.refund(refundDTO); +// if(null != r && r.getCode() == HttpServletResponse.SC_OK && null!=r.getResult()){ +// if(r.getResult().getIsSuccess()){ +// recordInfoAboutRechargeByPayOrderNo.setRemark(refundDTO.getRemark()); +// recordInfoAboutRechargeByPayOrderNo.setPaymentMethod(refundDTO.getPaymentMethod()); +// // 微信充值退款成功之后调用he处理会员余额和退款记录相关逻辑接口 +// stClientMemberBalanceService.backOrder(recordInfoAboutRechargeByPayOrderNo); +// return Result.OK("退款成功"); +// } else { +// return Result.error(r.getResult().getMsg()); +// } +// } else { +// return Result.error("退款失败"); +// } +// } + + + @Override + public void updateMemberIdByOpenId(String openId,String memberId,String memberName){ + List allByOpenId = baseMapper.getAllByOpenId(openId); + if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(allByOpenId)){ + for (StBbzxOrderInfo orderInfo : allByOpenId) { + orderInfo.setMemberName(memberName); + orderInfo.setMemberId(memberId); + } + this.saveOrUpdateBatch(allByOpenId); + } + } + + /** + * + * @param orderId 订单主表id + * @param orderFrom 订单来源(1平板端 、2收银端、3自助点餐机、4小程序、5自助餐) + * @param payCode 小程序 支付编码 + * @param payType 支付类型: 1是支付宝 2微信 3现金 4会员 5混合 + * @param orderFromSign 支付用户id(微信支付 = openid、支付宝支付 = 支付宝id) + * @param bh 混合支付时,会员支付的金额 + * @param tradeOrderNo 渠道支付返回的三方交易订单号 + * @param tarnsCode 混合支付时 经过随机数3位拼接的自定义余额支付订单号 + */ + @Override + public void updateOrderFeign(String orderId,int orderFrom,String payCode,Integer payType,String orderFromSign,StBbzxOrderInfo order, + BigDecimal bh,String tradeOrderNo,String tarnsCode) { + boolean mixedPayment = false ;//自助餐,混合支付时渠道的支付方式 + if(null == bh){ + bh = new BigDecimal("0"); + } else if(bh.compareTo(BigDecimal.ZERO)>0){//余额支付大于0,表示混合支付 + mixedPayment = true; + } + String unionId = null; +// if(!StringUtil.isEmpty(tradeOrderNo)){ +// unionId = dbyWechatExtService.getUnionId(orderFromSign,tradeOrderNo); +// } + LambdaUpdateWrapper orderLambdaUpdateWrapper = new LambdaUpdateWrapper<>(); + orderLambdaUpdateWrapper + .eq(StBbzxOrderInfo::getId, orderId) + .set(StBbzxOrderInfo::getStatus, OrderStatus.ORDER_STATUS_HAS_CHECK_OUT.getCode()) + .set(StBbzxOrderInfo::getPayOrderDate, new Date()) + .set(StBbzxOrderInfo::getPayType, mixedPayment ? 5 : payType)//对应订单 1是支付宝 2微信 3现金 4会员 5混合 + .set(StBbzxOrderInfo::getOrderFromSign, (null != order && null != order.getMemberId()) ? order.getMemberId() : orderFromSign) + .set(StBbzxOrderInfo::getUnionId, unionId) + .set(StBbzxOrderInfo::getMemberId, (null != order && null != order.getMemberId()) ? order.getMemberId() : null) + .set(StBbzxOrderInfo::getMemberName, null != order && StringUtils.isNotBlank(order.getMemberName()) ? order.getMemberName() : null) + .set(StBbzxOrderInfo::getMemberPhone, null != order && StringUtils.isNotBlank(order.getMemberPhone()) ? order.getMemberPhone() : null) + .set(StBbzxOrderInfo::getIsSync, 0); + boolean flag = this.update(null, orderLambdaUpdateWrapper); +// if(flag){ +// //支付完成后,如果是自助餐订单,置空plate_number餐盘编号,更新userId字段(userId,memberId,openId,支付宝id) 和 userIdType +// StBbzxOrderInfo swBbzxOrderInfo = this.getById(orderId); +// if(null != swBbzxOrderInfo && swBbzxOrderInfo.getOrderType() == 3){ //1:桌台点餐 2预点餐 3.自助餐 +// LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); +// lambdaQueryWrapper.eq(StUserFoodInfo::getOrderId,orderId); +// List stUserFoodInfoList = stUserFoodInfoMapper.selectList(lambdaQueryWrapper); +// stUserFoodInfoList.forEach(stUserFoodInfo -> { +// stUserFoodInfo.setPlateNumber("-"); +// if(swBbzxOrderInfo.getPayType() == 1){//结账方式 * /对应订单 1是支付宝 2微信 3现金 4会员 5混合 +// stUserFoodInfo.setUserId(swBbzxOrderInfo.getOrderFromSign()); +// stUserFoodInfo.setUserIdType("4");//用户id类型(4:支付宝id) +// } else if(swBbzxOrderInfo.getPayType() == 2){ +// stUserFoodInfo.setUserId(swBbzxOrderInfo.getOrderFromSign()); +// stUserFoodInfo.setUserIdType("3");//用户id类型(3:openId) +// } else if(swBbzxOrderInfo.getPayType() == 3 && null != swBbzxOrderInfo.getMemberId()){//现金支付-现金支付绑定会员 +// stUserFoodInfo.setUserId(swBbzxOrderInfo.getMemberId()+""); +// stUserFoodInfo.setUserIdType("2");//用户id类型(2:memberIdd) +// }else if(swBbzxOrderInfo.getPayType() == 4 || swBbzxOrderInfo.getPayType() == 5){ +// stUserFoodInfo.setUserId(swBbzxOrderInfo.getMemberId()+""); +// stUserFoodInfo.setUserIdType("2");//用户id类型(2:memberId) +// } +// stUserFoodInfoMapper.updateById(stUserFoodInfo); +// }); +// //纯会员支付 :自助餐推送给收银台设备支付消息(此处只推送纯会员支付消息) +// if(payType == 4){ +// // 微信支付宝的混合支付在com.shuwei.zhct.controller.SwBbzxOrderInfoController.updateOrderPayStatus 中进行消息推送 +// //开启线程发送推送信息 +// Map mapPush = JSONObject.parseObject(JSONObject.toJSONString(swBbzxOrderInfo)); +// mapPush.put("mqType", "1");//支付 +// mapPush.put("payStatus", 1);//支付状态-1系统异常,请重试 0 支付失败 1支付成功 2退款成功 3订单不存在 4支付失败,请撤销订单 5 用户支付中 6交易创建,等待买家付款 +//// Thread t = new Thread(() -> { +//// //发送推送信息 +//// String tp = "sw_test"; +//// Topic t1 = () -> tp; +//// producer.sendMessage(t1, JSON.toJSONString(mapPush)); +//// }); +//// t.start(); +// //推送消息给收银台进行订单同步信息 +//// this.sendCashierDeskOrderMsg(swBbzxOrderInfo); +// } else {// 支付类型: 1是支付宝 / 2微信 / mixedPayment = true 混合 +// // 1:sw_pay_trans_details + sw_bbzx_order_info_pay 保存记录,混合渠道和会员支付各保存一条金额分开 +// if(mixedPayment){//混合支付 +// //渠道的一笔 +// StBbzxOrderInfoPay infoPay = new StBbzxOrderInfoPay(); +// infoPay.setOrderId(swBbzxOrderInfo.getId()); +// if(swBbzxOrderInfo.getMemberId() != null){//混合支付 渠道的金额 = 订单总金额 - 会员支付金额 +// infoPay.setPayIncome(swBbzxOrderInfo.getCustomerPrice().subtract(bh).setScale(2, BigDecimal.ROUND_HALF_UP)); +// infoPay.setPayPrice(swBbzxOrderInfo.getCustomerPrice().subtract(bh).setScale(2, BigDecimal.ROUND_HALF_UP)); +// } else { +// infoPay.setPayIncome(swBbzxOrderInfo.getOrderPrice().subtract(bh).setScale(2, BigDecimal.ROUND_HALF_UP)); +// infoPay.setPayPrice(swBbzxOrderInfo.getOrderPrice().subtract(bh).setScale(2, BigDecimal.ROUND_HALF_UP)); +// } +// infoPay.setPayType(payType); +// infoPay.setStatus(1); +// // infoPay.setPayDiscount(p.getPayCode()); +// infoPay.setPayCode(tradeOrderNo); +// infoPay.setRemark("混合渠道支付成功"); +// infoPay.setIsDel(0); +// infoPay.setCreateBy("0"); +// infoPay.setCreateTime(new Date()); +// infoPay.setCanteenId(order.getCanteenId()); +// infoPay.setIsSync(0); +// stBbzxOrderInfoPayMapper.insert(infoPay); +// +// //会员的一笔 +// StBbzxOrderInfoPay stBbzxOrderInfoPay = new StBbzxOrderInfoPay(); +// stBbzxOrderInfoPay.setOrderId(swBbzxOrderInfo.getId()); +// //ALI_PAY = "1" WX_PAY = "2" ASH_PAY = "3"; +// stBbzxOrderInfoPay.setPayType(4); +// stBbzxOrderInfoPay.setPayIncome(bh); +// stBbzxOrderInfoPay.setPayPrice(bh); +// stBbzxOrderInfoPay.setStatus(1); +// // infoPay.setPayDiscount(p.getPayCode()); +// stBbzxOrderInfoPay.setPayCode(tarnsCode); +// stBbzxOrderInfoPay.setRemark("混合余额支付成功"); +// stBbzxOrderInfoPay.setCreateBy("0"); +// stBbzxOrderInfoPay.setCreateTime(new Date()); +// stBbzxOrderInfoPay.setCanteenId(order.getCanteenId()); +// stBbzxOrderInfoPay.setIsSync(0); +// swBbzxOrderInfoPayService.saveOrUpdate(stBbzxOrderInfoPay); +// } else {//单渠道支付 : 1是支付宝 2微信 +// //渠道的一笔 +// StBbzxOrderInfoPay infoPay = new StBbzxOrderInfoPay(); +// infoPay.setOrderId(swBbzxOrderInfo.getId()); +// if(swBbzxOrderInfo.getMemberId() != null){ +// infoPay.setPayIncome(swBbzxOrderInfo.getCustomerPrice().setScale(2, BigDecimal.ROUND_HALF_UP)); +// infoPay.setPayPrice(swBbzxOrderInfo.getCustomerPrice().setScale(2, BigDecimal.ROUND_HALF_UP)); +// } else { +// infoPay.setPayIncome(swBbzxOrderInfo.getOrderPrice().setScale(2, BigDecimal.ROUND_HALF_UP)); +// infoPay.setPayPrice(swBbzxOrderInfo.getOrderPrice().setScale(2, BigDecimal.ROUND_HALF_UP)); +// } +// infoPay.setPayType(payType); +// infoPay.setStatus(1); +// // infoPay.setPayDiscount(p.getPayCode()); +// infoPay.setPayCode(tradeOrderNo); +// infoPay.setRemark(""); +// infoPay.setIsDel(0); +// infoPay.setCreateBy("0"); +// infoPay.setCreateTime(new Date()); +// infoPay.setCanteenId(order.getCanteenId()); +// infoPay.setIsSync(0); +// stBbzxOrderInfoPayMapper.insert(infoPay); +// } +// } +// +// } +// } + + if (orderFrom == 4) {//小程序 + LambdaUpdateWrapper payLambdaUpdateWrapper = new LambdaUpdateWrapper<>(); + payLambdaUpdateWrapper + .eq(StBbzxOrderInfoPay::getOrderId, orderId) + .eq(StBbzxOrderInfoPay::getPayType, 2) + .set(StBbzxOrderInfoPay::getPayCode, payCode) + .set(StBbzxOrderInfoPay::getIsSync, 0); + swBbzxOrderInfoPayService.update(null, payLambdaUpdateWrapper); + } + + } + + @Override + public IPage getStatisticsTheAmountSpentList(String canteenId, String startDate, String endDate, Integer pageNo, Integer pageSize){ + if(StringUtils.isEmpty(startDate)){ + startDate = DateUtil.format(new Date(),"yyyy-MM-dd"); + endDate = DateUtil.format(new Date(),"yyyy-MM-dd"); + } + + //权限校验 + + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + List canteenIds = new ArrayList<>(); + if(StringUtils.isEmpty(canteenId)){ + if(sysUser != null){ + canteenIds = sysUser.getCanteenIdList().stream().map(ScreenVo::getId).collect(Collectors.toList()); + } + }else { + canteenIds = Collections.singletonList(canteenId); + } + //获取数据列表 + List statisticsTheAmountSpentVos = baseMapper.statisticsTheAmountSpent(canteenIds, startDate, endDate); + if(CollectionUtils.isEmpty(statisticsTheAmountSpentVos)){ + return new Page<>(); + } + statisticsTheAmountSpentVos.forEach(vo->{ + vo.setTotalCount(vo.getBreakfastPerson().add(vo.getLunchPerson().add(vo.getDinnerPerson()).add(vo.getOtherPerson()))); + vo.setCorrectionValue(BigDecimal.ZERO); + vo.setCorrectionCount(BigDecimal.ZERO); + }); + if(startDate.equals(endDate)){ + return ListUtil.listToPage(statisticsTheAmountSpentVos,pageNo,pageSize); + }else { + //如果是时间返回之内的数据 , 直接求每天的和 + //根据食堂分组 + Map> canteenListMaps = statisticsTheAmountSpentVos.stream().collect(Collectors.groupingBy(StatisticsTheAmountSpentVo::getCanteenId)); + List statisticsTheAmountSpentVoList = new ArrayList<>(); + for (Map.Entry> canteenListMap : canteenListMaps.entrySet()) { + StatisticsTheAmountSpentVo spentVo = new StatisticsTheAmountSpentVo(); + spentVo.setEatDay(startDate+"~"+endDate); + spentVo.setCanteenId(canteenListMap.getKey()); + spentVo.setRestName(canteenListMap.getValue().get(0).getRestName()); + spentVo.setTotalNum(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getTotalNum).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setTotalCount(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getTotalCount).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setCustomerPrice(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getCustomerPrice).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setCorrectionCount(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getCorrectionCount).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setCorrectionValue(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getCorrectionValue).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setBreakfastAmount(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getBreakfastAmount).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setBreakfastPerson(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getBreakfastPerson).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setLunchAmount(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getLunchAmount).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setLunchPerson(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getLunchPerson).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setDinnerAmount(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getDinnerAmount).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setDinnerPerson(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getDinnerPerson).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setOtherAmount(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getOtherAmount).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setOtherPerson(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getOtherPerson).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setOrderPrice(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getOrderPrice).reduce(BigDecimal.ZERO,BigDecimal::add)); + statisticsTheAmountSpentVoList.add(spentVo); + } + return ListUtil.listToPage(statisticsTheAmountSpentVoList,pageNo,pageSize); + } + } + + @Override + public List getStatisticsTheAmountSpentList(String canteenId, String startDate, String endDate){ + if(StringUtils.isEmpty(startDate)){ + startDate = DateUtil.format(new Date(),"yyyy-MM-dd"); + endDate = DateUtil.format(new Date(),"yyyy-MM-dd"); + } + + //权限校验 + + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + List canteenIds = new ArrayList<>(); + if(StringUtils.isEmpty(canteenId)){ + if(sysUser != null){ + canteenIds = sysUser.getCanteenIdList().stream().map(ScreenVo::getId).collect(Collectors.toList()); + } + }else { + canteenIds = Collections.singletonList(canteenId); + } + //获取数据列表 + List statisticsTheAmountSpentVos = baseMapper.statisticsTheAmountSpent(canteenIds, startDate, endDate); + if(CollectionUtils.isEmpty(statisticsTheAmountSpentVos)){ + return new ArrayList<>(); + } + statisticsTheAmountSpentVos.forEach(vo->{ + vo.setTotalCount(vo.getBreakfastPerson().add(vo.getLunchPerson().add(vo.getDinnerPerson()).add(vo.getOtherPerson()))); + vo.setCorrectionValue(BigDecimal.ZERO); + vo.setCorrectionCount(BigDecimal.ZERO); + }); + if(startDate.equals(endDate)){ + return statisticsTheAmountSpentVos; + }else { + //如果是时间返回之内的数据 , 直接求每天的和 + //根据食堂分组 + Map> canteenListMaps = statisticsTheAmountSpentVos.stream().collect(Collectors.groupingBy(StatisticsTheAmountSpentVo::getCanteenId)); + List statisticsTheAmountSpentVoList = new ArrayList<>(); + for (Map.Entry> canteenListMap : canteenListMaps.entrySet()) { + StatisticsTheAmountSpentVo spentVo = new StatisticsTheAmountSpentVo(); + spentVo.setEatDay(startDate+"~"+endDate); + spentVo.setCanteenId(canteenListMap.getKey()); + spentVo.setRestName(canteenListMap.getValue().get(0).getRestName()); + spentVo.setTotalNum(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getTotalNum).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setTotalCount(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getTotalCount).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setCustomerPrice(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getCustomerPrice).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setCorrectionCount(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getCorrectionCount).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setCorrectionValue(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getCorrectionValue).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setBreakfastAmount(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getBreakfastAmount).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setBreakfastPerson(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getBreakfastPerson).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setLunchAmount(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getLunchAmount).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setLunchPerson(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getLunchPerson).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setDinnerAmount(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getDinnerAmount).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setDinnerPerson(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getDinnerPerson).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setOtherAmount(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getOtherAmount).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setOtherPerson(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getOtherPerson).reduce(BigDecimal.ZERO,BigDecimal::add)); + spentVo.setOrderPrice(canteenListMap.getValue().stream().map(StatisticsTheAmountSpentVo::getOrderPrice).reduce(BigDecimal.ZERO,BigDecimal::add)); + statisticsTheAmountSpentVoList.add(spentVo); + } + return statisticsTheAmountSpentVoList; + } + } + + /** + * 个人消费记录统计 + * @param startDate 开始时间 + * @param endDate 结束时间 + * @param secondDepartId 二级单位id + * @param thirdDepartId 三级部门id + * @param pageNo 页数 + * @param pageSize 条数 + * @return 个人消费记录统计 + */ + @Override + public IPage statisticsTheAmountSpentEveryMember(String canteenId,String name,String payName,String startDate, String endDate, String secondDepartId,String thirdDepartId,Integer pageNo, Integer pageSize){ + String eatDay = ""; + if(StringUtils.isEmpty(startDate)){ + startDate = DateUtil.format(new Date(),"yyyy-MM-dd"); + endDate = DateUtil.format(new Date(),"yyyy-MM-dd"); + eatDay = startDate; + }else { + eatDay = startDate + "~" + endDate; + } + //权限校验 + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + List canteenIds = new ArrayList<>(); + if(StringUtils.isEmpty(canteenId)){ + if(sysUser != null){ + canteenIds = sysUser.getCanteenIdList().stream().map(ScreenVo::getId).collect(Collectors.toList()); + } + }else { + canteenIds = Collections.singletonList(canteenId); + } + List statisticsTheAmountSpentVos = baseMapper.statisticsTheAmountSpentEveryMember(canteenIds,name,payName,startDate, endDate, secondDepartId, thirdDepartId); + if(CollectionUtils.isEmpty(statisticsTheAmountSpentVos)){ + return new Page<>(); + } + + String finalEatDay = eatDay; + statisticsTheAmountSpentVos.forEach(vo->{ + //计算消费次数 + vo.setTotalCount(vo.getBreakfastPerson().add(vo.getLunchPerson().add(vo.getDinnerPerson()).add(vo.getOtherPerson()))); + vo.setEatDay(finalEatDay); + }); + return ListUtil.listToPage(statisticsTheAmountSpentVos,pageNo,pageSize); + } + + + /** + * 个人消费记录统计导出 + * @param startDate 开始时间 + * @param endDate 结束时间 + * @param secondDepartId 二级单位id + * @param thirdDepartId 三级部门id + * @return 个人消费记录统计导出 + */ + @Override + public List statisticsTheAmountSpentEveryMember(String canteenId,String name,String payName,String startDate, String endDate, String secondDepartId,String thirdDepartId){ + String eatDay = ""; + if(StringUtils.isEmpty(startDate)){ + startDate = DateUtil.format(new Date(),"yyyy-MM-dd"); + endDate = DateUtil.format(new Date(),"yyyy-MM-dd"); + eatDay = startDate; + }else { + eatDay = startDate + "~" + endDate; + } + //权限校验 + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + List canteenIds = new ArrayList<>(); + if(StringUtils.isEmpty(canteenId)){ + if(sysUser != null){ + canteenIds = sysUser.getCanteenIdList().stream().map(ScreenVo::getId).collect(Collectors.toList()); + } + }else { + canteenIds = Collections.singletonList(canteenId); + } + List statisticsTheAmountSpentVos = baseMapper.statisticsTheAmountSpentEveryMember(canteenIds,name,payName,startDate, endDate, secondDepartId, thirdDepartId); + if(CollectionUtils.isEmpty(statisticsTheAmountSpentVos)){ + return Collections.emptyList(); + } + + String finalEatDay = eatDay; + statisticsTheAmountSpentVos.forEach(vo->{ + //计算消费次数 + vo.setTotalCount(vo.getBreakfastPerson().add(vo.getLunchPerson().add(vo.getDinnerPerson()).add(vo.getOtherPerson()))); + vo.setEatDay(finalEatDay); + }); + return statisticsTheAmountSpentVos; + } + + + + /** + * 食堂营业查询报表 + * @param startDate 开始时间 + * @param endDate 结束时间 + * @param secondDepartId 二级单位id + * @param thirdDepartId 三级部门id + * @param pageNo 页数 + * @param pageSize 条数 + * @return 食堂营业查询报表 + */ + @Override + public IPage statisticsCanteenBusiness(String canteenId,String startDate, String endDate, String secondDepartId,String thirdDepartId,Integer pageNo, Integer pageSize){ + List canteenIdList = new ArrayList<>(); + if(StringUtils.isNotBlank(canteenId)){ + ScreenVo screenVo = new ScreenVo(); + screenVo.setId(canteenId); + canteenIdList.add(screenVo); + } else { + //权限校验 + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + List canteenIds = new ArrayList<>(); + if(sysUser != null){ + canteenIds = sysUser.getCanteenIdList().stream().map(ScreenVo::getId).collect(Collectors.toList()); + canteenIds.forEach(id->{ + ScreenVo screenVo = new ScreenVo(); + screenVo.setId(id); + canteenIdList.add(screenVo); + }); + } + } + List statisticsTheAmountSpentVos = baseMapper.statisticsCanteenBusiness(canteenIdList,startDate, endDate); + if(CollectionUtils.isEmpty(statisticsTheAmountSpentVos)){ + return new Page<>(); + } + return ListUtil.listToPage(statisticsTheAmountSpentVos,pageNo,pageSize); + } + + + /** + * 食堂营业查询报表 + * @param startDate 开始时间 + * @param endDate 结束时间 + * @param secondDepartId 二级单位id + * @param thirdDepartId 三级部门id + * @return 食堂营业查询报表 + */ + @Override + public List statisticsCanteenBusiness(String canteenId,String startDate, String endDate, String secondDepartId,String thirdDepartId){ + List canteenIdList = new ArrayList<>(); + if(StringUtils.isNotBlank(canteenId)){ + ScreenVo screenVo = new ScreenVo(); + screenVo.setId(canteenId); + canteenIdList.add(screenVo); + } else { + //权限校验 + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + List canteenIds = new ArrayList<>(); + if(sysUser != null){ + canteenIds = sysUser.getCanteenIdList().stream().map(ScreenVo::getId).collect(Collectors.toList()); + canteenIds.forEach(id->{ + ScreenVo screenVo = new ScreenVo(); + screenVo.setId(id); + canteenIdList.add(screenVo); + }); + } + } + List statisticsTheAmountSpentVos = baseMapper.statisticsCanteenBusiness(canteenIdList,startDate, endDate); + if(CollectionUtils.isEmpty(statisticsTheAmountSpentVos)){ + return new ArrayList<>(); + } + return statisticsTheAmountSpentVos; + } + + /** + * 统一生成订单 + * @param restId + * @param userId + * @param plateNumber + * @return + */ + @Override + public StBbzxOrderInfo crateOrderInfo(Date dateTime, String restId, String userId, String plateNumber, String timesName, StRfidVo stRfidVo){ + //扣费规则(1:固定扣费,2:固定+补贴扣费,3:按量扣费) + StClientMemberLevelDeductRuleVo levelDeductRuleVo = iStClientMemberLevelDeductRuleService.getDeductRuleByCanteenId(restId); + if(null == levelDeductRuleVo){ + return null; + } + // 生成新订单 + return buildOrderInfoNew(dateTime,restId, timesName, plateNumber, userId, "", "", levelDeductRuleVo.getDeductionType(),levelDeductRuleVo,null, stRfidVo); + + } + + /** + * 生成新订单 + * @param restId + * @param dinnerType + * @param plateNumber + * @param userId + * @param memberName + * @param memberPhone + * @param deductionType + * @return + */ + @Override + public StBbzxOrderInfo buildOrderInfoNew(Date dateTime,String restId,String dinnerType,String plateNumber,String userId,String memberName,String memberPhone,Integer deductionType,StClientMemberLevelDeductRuleVo levelDeductRuleVo , String remark,StRfidVo stRfidVo){ + StBbzxOrderInfo stBbzxOrderInfo = new StBbzxOrderInfo(); + stBbzxOrderInfo.setOrderType(3); + String orderNo = OrderUtils.getOrderCode(10); + stBbzxOrderInfo.setOrderNo(orderNo); + stBbzxOrderInfo.setSerialNo(stRfidVo.getCardCode()); + stBbzxOrderInfo.setDinnerType(dinnerType); + stBbzxOrderInfo.setMemberId(userId); + stBbzxOrderInfo.setMemberName(memberName); + stBbzxOrderInfo.setMemberPhone(memberPhone); + stBbzxOrderInfo.setPlateNumber(plateNumber); + stBbzxOrderInfo.setOrderFrom(5); + stBbzxOrderInfo.setDiningWay(2); + stBbzxOrderInfo.setEatDay(DateUtil.today()); + stBbzxOrderInfo.setBusinessDate(new Date()); + stBbzxOrderInfo.setOrderDate(new Date()); + if(null != dateTime){ + stBbzxOrderInfo.setBusinessDate(dateTime); + stBbzxOrderInfo.setOrderDate(dateTime); + String eatDay = DateUtils.formatDate(dateTime,"yyyy-MM-dd"); + stBbzxOrderInfo.setEatDay(eatDay); + } + if (null != stRfidVo && null != stRfidVo.getSpendMoney()) { + stBbzxOrderInfo.setOrderPrice(stRfidVo.getSpendMoney()); + stBbzxOrderInfo.setCustomerPrice(stRfidVo.getSpendMoney()); + } + stBbzxOrderInfo.setStatus(OrderStatus.ORDER_STATUS_WAIT_CHECK_OUT.getCode());//待结账 + stBbzxOrderInfo.setCanteenId(restId); + stBbzxOrderInfo.setBusinessModel(deductionType); + stBbzxOrderInfo.setIsSync(0); + stBbzxOrderInfo.setIsDel(0); + if(4 == deductionType){//自定义扣费 + StClientMemberLevelDeductRuleVo stClientMemberLevelDeductRuleVoCustom = new StClientMemberLevelDeductRuleVo(); + stClientMemberLevelDeductRuleVoCustom.setRuleName("自定义扣费"); + stClientMemberLevelDeductRuleVoCustom.setDeductionType(4); + stBbzxOrderInfo.setOrderText(JSONObject.toJSONString(stClientMemberLevelDeductRuleVoCustom)); + } else { + if(null != levelDeductRuleVo){ + stBbzxOrderInfo.setOrderText(JSONObject.toJSONString(levelDeductRuleVo)); + } + } + stBbzxOrderInfo.setRemark(remark); + stBbzxOrderInfo.setPayId(stRfidVo.getPayId()); + this.save(stBbzxOrderInfo); + return stBbzxOrderInfo; + } + + /** + * 生成新订单 + * @param restId + * @param dinnerType + * @param plateNumber + * @param userId + * @param memberName + * @param memberPhone + * @param deductionType + * @return + */ + @Override + public StBbzxOrderInfo buildOrderInfoNewOffLine(Date dateTime,String restId,String dinnerType,String plateNumber,String userId,String memberName, + String memberPhone,Integer deductionType,StClientMemberLevelDeductRuleVo levelDeductRuleVo , + String remark,StRfidVo stRfidVo,StMiniMineBalanceVo miniMineBalanceVo){ + StBbzxOrderInfo stBbzxOrderInfo = new StBbzxOrderInfo(); + stBbzxOrderInfo.setOrderType(3); + String orderNo = OrderUtils.getOrderCode(10); + stBbzxOrderInfo.setOrderNo(orderNo); + stBbzxOrderInfo.setSerialNo(miniMineBalanceVo.getCardCode()); + stBbzxOrderInfo.setDinnerType(dinnerType); + stBbzxOrderInfo.setMemberId(userId); + stBbzxOrderInfo.setMemberName(memberName); + stBbzxOrderInfo.setMemberPhone(memberPhone); + stBbzxOrderInfo.setPlateNumber(plateNumber); + stBbzxOrderInfo.setOrderFrom(5); + stBbzxOrderInfo.setDiningWay(2); + stBbzxOrderInfo.setEatDay(DateUtil.today()); + stBbzxOrderInfo.setBusinessDate(new Date()); + stBbzxOrderInfo.setOrderDate(new Date()); + if(null != dateTime){ + stBbzxOrderInfo.setBusinessDate(dateTime); + stBbzxOrderInfo.setOrderDate(dateTime); + String eatDay = DateUtils.formatDate(dateTime,"yyyy-MM-dd"); + stBbzxOrderInfo.setEatDay(eatDay); + } + stBbzxOrderInfo.setStatus(OrderStatus.ORDER_STATUS_WAIT_CHECK_OUT.getCode());//待结账 + stBbzxOrderInfo.setCanteenId(restId); + stBbzxOrderInfo.setBusinessModel(deductionType); + stBbzxOrderInfo.setIsSync(0); + stBbzxOrderInfo.setIsDel(0); + stBbzxOrderInfo.setOrderText(JSONObject.toJSONString(levelDeductRuleVo)); + String result = MessageFormat.format("【识别用户名称:{6},识别用户当前类型:{0},识别用户当前删除状态:{1},识别用户当前停用状态:{2}," + + "支付用户名称:{7},支付用户当前类型:{3},支付用户当前删除状态:{4},支付用户当前停用状态:{5}】", + UserTypeEnum.getByCode(miniMineBalanceVo.getUserType()).getDesc(), + miniMineBalanceVo.getDelFlag() == 1 ? "删除" : "正常", miniMineBalanceVo.getStatus() == 1 ? "正常" : "禁用", + miniMineBalanceVo.getUserInnerType() == 1 ? "外部用户" : "内部用户", + miniMineBalanceVo.getPayDelFlag() == 1 ? "删除" : "正常", + miniMineBalanceVo.getUserStatus()== 1 ? "正常" : "禁用", + miniMineBalanceVo.getRealname(),miniMineBalanceVo.getPayRealname()); + stBbzxOrderInfo.setRemark(result+remark); + stBbzxOrderInfo.setPayId(stRfidVo.getPayId()); + stBbzxOrderInfo.setOrderPrice(miniMineBalanceVo.getAmountThis()); + stBbzxOrderInfo.setCustomerPrice(miniMineBalanceVo.getAmountThis()); + this.save(stBbzxOrderInfo); + return stBbzxOrderInfo; + } + /** + * 生成订单流水号 + */ + public String generateSerialNumber_tool(String eaId) { + String container = "ORDER_SERIAL_NUMBER:" + eaId + ":"; + LocalDateTime sameDay = LocalDateTime.now(); + String day = sameDay.format(DateTimeFormatter.ofPattern("yyyyMMdd")); + //获取缓存日期 + HashOperations hashOperations = redisTemplate.opsForHash(); + String redisDay = (String) hashOperations.get(container.concat(day), "SERIAL_NUMBER"); + String result = ""; + if (!org.springframework.util.StringUtils.isEmpty(redisDay)) { + int newSerialNumber = Integer.valueOf(redisDay) + 1; + switch (String.valueOf(newSerialNumber).length()) { + case 1: + result = "000" + newSerialNumber; + break; + case 2: + result = "00" + newSerialNumber; + break; + case 3: + result = "0" + newSerialNumber; + break; + case 4: + result = newSerialNumber + ""; + break; + } + } else { + result = "0001"; + } + //重新写入 + hashOperations.put(container.concat(day), "SERIAL_NUMBER", result); + return result; + } + + /** + * 根据userId + 日期 + 餐次 + 订单状态,获取订单信息 + * @param userId + * @param eatDay + * @param dinnerType + * @param orderStatus + * @return + */ + @Override + public StBbzxOrderInfo getSwBbzxOrderInfoByUserId(String userId,String eatDay,String dinnerType,Integer orderStatus,String restId, + Integer businessModel ){ + LambdaQueryWrapper wrapperorder = new LambdaQueryWrapper<>(); + wrapperorder.eq(StBbzxOrderInfo::getMemberId, userId); + if(StringUtils.isNotBlank(eatDay)){ + wrapperorder.eq(StBbzxOrderInfo::getEatDay, eatDay); + } + if(StringUtils.isNotBlank(dinnerType)){ + wrapperorder.eq(StBbzxOrderInfo::getDinnerType, dinnerType); + } + if(null != orderStatus){ + wrapperorder.eq(StBbzxOrderInfo::getStatus, orderStatus); + } + if(null != businessModel){ + wrapperorder.eq(StBbzxOrderInfo::getBusinessModel, businessModel); + } + wrapperorder.eq(StBbzxOrderInfo::getCanteenId, restId); + wrapperorder.eq(StBbzxOrderInfo::getIsDel, 0); + StBbzxOrderInfo orderInfo1 = this.getBaseMapper().selectOne(wrapperorder); + return orderInfo1; + } + + /** + * 根据userId + 日期 + 餐次 + 订单状态,获取订单信息 + * @param userId + * @param eatDay + * @param orderStatus + * @return + */ + @Override + public List getSwBbzxOrderInfoListByUserId(String userId,String eatDay,String dinnerType,Integer orderStatus,String restId, + Integer businessModel ){ + LambdaQueryWrapper wrapperorder = new LambdaQueryWrapper<>(); + wrapperorder.eq(StBbzxOrderInfo::getMemberId, userId); + if(StringUtils.isNotBlank(eatDay)){ + wrapperorder.eq(StBbzxOrderInfo::getEatDay, eatDay); + } + if(StringUtils.isNotBlank(dinnerType)){ + wrapperorder.eq(StBbzxOrderInfo::getDinnerType, dinnerType); + } + if(null != orderStatus){ + wrapperorder.eq(StBbzxOrderInfo::getStatus, orderStatus); + } + if(null != businessModel){ + wrapperorder.eq(StBbzxOrderInfo::getBusinessModel, businessModel); + } + wrapperorder.eq(StBbzxOrderInfo::getCanteenId, restId); + wrapperorder.eq(StBbzxOrderInfo::getIsDel, 0); + List orderInfo1 = this.getBaseMapper().selectList(wrapperorder); + return orderInfo1; + } + + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StClientMemberBalanceServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StClientMemberBalanceServiceImpl.java new file mode 100644 index 0000000..d028c0a --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StClientMemberBalanceServiceImpl.java @@ -0,0 +1,2050 @@ +package org.jeecg.payment.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.system.base.service.impl.JeecgServiceImpl; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.util.RedisUtil; +import org.jeecg.common.util.StMemberUtil; +import org.jeecg.payment.dto.*; +import org.jeecg.payment.entity.*; +import org.jeecg.payment.enums.MemberEnum; +import org.jeecg.payment.enums.MemberLevelEnum; +import org.jeecg.payment.enums.MemberTransactionTypeEnum; +import org.jeecg.payment.enums.OrderStatus; +import org.jeecg.payment.listUtils.MapUtil; +import org.jeecg.payment.mapper.StClientMemberBalanceMapper; +import org.jeecg.payment.mapper.StClientMemberTransactionRecordMapper; +import org.jeecg.payment.service.*; +import org.jeecg.payment.utils.MealGroupingUtil; +import org.jeecg.payment.vo.*; +import org.jeecg.restaurant.entity.SlBaseDinnerTime; +import org.jeecg.restaurant.service.ISlBaseDinnerTimeService; +import org.jeecg.restaurant.service.ISlBaseRestaurantService; +import org.jeecg.restaurant.entity.SlBaseRestaurant; +import org.jeecg.system.service.ISysPayDepartService; +import org.jeecg.user.entity.SysUserSub; +import org.jeecg.user.vo.SysUserSubInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +/** + 用户钱包 + */ + +@Service +@Slf4j +public class StClientMemberBalanceServiceImpl extends JeecgServiceImpl implements IStClientMemberBalanceService { + + @Resource + private StClientMemberBalanceMapper clientMemberBalanceMapper; + + @Resource + private IStClientMemberTransactionRecordService iStClientMemberTransactionRecordService; + + @Resource + private IStClientMemberLevelDemotionTimeService iStClientMemberLevelDemotionTimeService; + @Resource + private IStClientMemberLevelDeductRuleService iStClientMemberLevelDeductRuleService; + + @Resource + private ISlBaseRestaurantService iSlBaseRestaurantService; + @Resource + private IStBbzxOrderInfoService iStBbzxOrderInfoService; + @Resource + private IStBbzxOrderInfoPayService iStBbzxOrderInfoPayService; + @Autowired + private RedisUtil redisUtil; + @Resource + private ISysPayDepartService sysPayDepartService; + @Resource + private StClientMemberTransactionRecordMapper stClientMemberTransactionRecordMapper; + @Resource + private ISlBaseDinnerTimeService slBaseDinnerTimeService; + + + /** + * 会员交易记录 + * @param userId + * @return 会员交易记录 + */ + @Override + public IPage memberTranRecordService(Page page, String userId, String status) { + List records = clientMemberBalanceMapper.memberTranRecordMapper(page,userId,status); + if (CollectionUtils.isEmpty(records)) { + records = new ArrayList<>(); + } + return page.setRecords(records); + + } + + + @Override + public IPage memberClientMemberBalanceService(Page page, StClientMemberTransactionRecordDTO stClientUserInfoShopPageDTO){ + if(StringUtils.isNotBlank(stClientUserInfoShopPageDTO.getDepartId())){ + List allCascadeDeptIds = sysPayDepartService.getAllCascadeDeptIds(stClientUserInfoShopPageDTO.getDepartId()); + if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(allCascadeDeptIds)){ + stClientUserInfoShopPageDTO.setList(allCascadeDeptIds); + } + } + List records = clientMemberBalanceMapper.pageList(page,stClientUserInfoShopPageDTO); + if (CollectionUtils.isEmpty(records)) { + records = new ArrayList<>(); + } + return page.setRecords(records); + + } + + + @Override + public List getSpendingOrRechargeInfoListExport(StClientMemberTransactionRecordDTO stClientUserInfoShopPageDTO){ + if(StringUtils.isNotBlank(stClientUserInfoShopPageDTO.getDepartId())){ + List allCascadeDeptIds = sysPayDepartService.getAllCascadeDeptIds(stClientUserInfoShopPageDTO.getDepartId()); + if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(allCascadeDeptIds)){ + stClientUserInfoShopPageDTO.setList(allCascadeDeptIds); + } + } + List records = clientMemberBalanceMapper.pageList(stClientUserInfoShopPageDTO); + if (CollectionUtils.isEmpty(records)) { + records = new ArrayList<>(); + } + return records; + + } + + /** + * 小程序 会员卡充值 + * @param memberId memberId + * @param topUpBalance 交易金额 + * @param paymentMethod 支付方式 + * @param orderNo 微信充值的商户外部订单号,退款时根据这个号退 + * @return 赠送金额+赠送积分 + */ + @Override + public Map memberTopUpXcx(String memberId, BigDecimal topUpBalance, int paymentMethod,String orderNo) { + + //根据openId 获取用户信息 + Map m = new HashMap(); + + //获取会员余额信息 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(StClientMemberBalance::getMemberId, memberId); + StClientMemberBalance swClientMemberBalance = baseMapper.selectOne(wrapper); + if (swClientMemberBalance != null) { + //获取会员对应 + Map map = iStClientMemberLevelDemotionTimeService.topUpAwardService(null, topUpBalance.floatValue(),swClientMemberBalance.getCanteenId()); + BigDecimal amount = BigDecimal.ZERO; + String integral = "0"; + if(CollectionUtil.isNotEmpty(map)){ + //赠送的金额 + amount = new BigDecimal(map.get("amount").toString()); + //赠送的积分 + integral = map.get("integral").toString(); + m.put("amount",amount); + m.put("integral",integral); + } + StClientMemberTopUpDTO dto = new StClientMemberTopUpDTO(); + dto.setId(swClientMemberBalance.getId()); + dto.setIntegralBalance(Integer.parseInt(integral)); + dto.setRewardBalance(amount); + dto.setTopUpBalance(topUpBalance); + + //1.充值 更新余额表数据 + baseMapper.memberTopUp(dto); + //更新过后的余额信息 + swClientMemberBalance = baseMapper.selectById(swClientMemberBalance.getId()); + + //2.添加充值记录 + StClientMemberTransactionRecord model = new StClientMemberTransactionRecord(); + + model.setMemberId(memberId); + //充值 + model.setStatus(MemberTransactionTypeEnum.PAY.getCode()); + //交易金额 + model.setTransactionAmount(topUpBalance); + //充值方式 + //1支付宝2微信 + if(paymentMethod == 1){ + //0卡余额1现金2微信3支付宝4前台码 + model.setPaymentMethod(MemberEnum.PAYMENT_METHOD_BY_ALI.getCode()); + }else { + model.setPaymentMethod(MemberEnum.PAYMENT_METHOD_BY_WX.getCode()); + } + model.setPayTime(new Date()); + //操作端 小程序 + model.setUpdateStatus(1); + model.setOrderNo(StMemberUtil.getOrderNo()); + model.setSerialNo(StMemberUtil.getSerialNo()); + model.setIntegralBalance(Integer.parseInt(integral)); + model.setStoredBalance(topUpBalance); + model.setRewardBalance(amount); + if(amount.doubleValue() > 0){ + model.setRemark("充值赠送"+amount+"元"); + } + model.setAvailableBalance(swClientMemberBalance.getTopUpBalance().add(swClientMemberBalance.getRewardBalance())); + + model.setCanteenId(swClientMemberBalance.getCanteenId()); + model.setCreateBy(memberId + ""); + model.setPayOrderNo(orderNo); + model.setIsSync(0); + iStClientMemberTransactionRecordService.saveOrUpdate(model); + } + return m; + } + + + //收银端会员充值API(现金) + @Override + public Map memberTopUp_new_yc(String canteenId, String userId, BigDecimal value, int paymentMethod) { + int flag = 0; + Map m=new HashMap(); + StClientMemberTransactionRecord model = new StClientMemberTransactionRecord(); + LambdaQueryWrapper clientMemberWrapper = new LambdaQueryWrapper<>(); + clientMemberWrapper.eq(StClientMemberBalance::getMemberId, userId); + StClientMemberBalance balance = baseMapper.selectOne(clientMemberWrapper); + StClientMemberTopUpDTO query = new StClientMemberTopUpDTO(); + query.setId(balance.getId()); + query.setPaymentMethod(paymentMethod); + query.setTopUpBalance(value); + flag = baseMapper.memberTopUp(query); + if (flag == 1) { + if (balance != null) { + /*判断是否修改了赠送金额以及积分*/ + try { + Map map = iStClientMemberLevelDemotionTimeService.topUpAwardService(null, query.getTopUpBalance().floatValue(),canteenId); + if (!map.isEmpty()) { + String remark = ""; + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + //赠送的金额 + BigDecimal amount = new BigDecimal(map.get("amount").toString()); + //赠送的积分 + String integral = map.get("integral").toString(); + m.put("amount",amount); + m.put("integral",integral); + if (!query.getRewardBalance().equals(amount) || !query.getIntegralBalance().equals(integral)) { + if (!query.getRewardBalance().equals(amount) && !query.getIntegralBalance().equals(integral)) { + remark = "{}把赠送金额{}元修改成了{}元,把赠送积分{}个改成了{}个"; + remark = StrUtil.format(remark, + sysUser.getUsername(), + amount.toString(), + query.getRewardBalance().toString(), + integral, + query.getIntegralBalance().toString()); + } else if (!query.getRewardBalance().equals(amount)) { + remark = "{}把赠送金额{}元修改成了{}元"; + remark = StrUtil.format(remark, + sysUser.getUsername(), + amount.toString(), + query.getRewardBalance().toString()); + } else if (!query.getIntegralBalance().equals(integral)) { + remark = "{}把赠送积分{}个改成了{}个"; + remark = StrUtil.format(remark, + sysUser.getUsername(), + integral, + query.getIntegralBalance().toString()); + } + model.setRemark(remark); + } + } + } catch (Exception e) { + log.error("swClientMemberLevelService.topUpAwardService接口请求出错"); + } + } + /*录入流水数据*/ + String orderNo = StMemberUtil.getOrderNo(); + model.setMemberId(userId); + model.setStatus(MemberTransactionTypeEnum.PAY.getCode()); + model.setTransactionAmount(query.getTopUpBalance()); + model.setPaymentMethod(query.getPaymentMethod()); + model.setPayTime(new Date()); + model.setUpdateStatus(0); + model.setOrderNo(orderNo); + model.setSerialNo(StMemberUtil.getSerialNo()); + model.setRewardUserNo(query.getRewardUserNo()); + model.setIntegralBalance(query.getIntegralBalance()); + model.setStoredBalance(query.getTopUpBalance()); + model.setRewardBalance(query.getRewardBalance()); + model.setAvailableBalance(balance.getTopUpBalance().add(balance.getRewardBalance())); + model.setCanteenId(canteenId); + model.setCreateBy(query.getUserId() + ""); + + iStClientMemberTransactionRecordService.saveOrUpdate(model); + + } + return m; + } + + + @Override + public boolean memberTopUp(StClientMemberTopUpDTO query) { + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + + // 1. 参数校验失败直接抛异常(触发回滚) + if (query.getId() == null || query.getPaymentMethod() == null || query.getTopUpBalance() == null) { + throw new IllegalArgumentException("充值参数不完整:id、支付方式、充值金额不能为空"); + } + + if (!query.isInvoiceTotal()) { + query.setInvoiceBalance(query.getTopUpBalance()); + } + StClientMemberTransactionRecord model = new StClientMemberTransactionRecord(); + + // 2. 执行余额更新(数据库操作) + int flag = baseMapper.memberTopUp(query); + if (flag != 1) { + // 更新失败抛异常(触发回滚) + throw new RuntimeException("余额更新失败:用户钱包ID=" + query.getId()); + } + + // 3. 查询更新后的余额并处理首次充值时间 + StClientMemberBalance swClientMemberBalance = baseMapper.selectById(query.getId()); + if (swClientMemberBalance == null) { + throw new RuntimeException("未查询到用户钱包信息:ID=" + query.getId()); + } + if (swClientMemberBalance.getFirstTopUpTime() == null) { + swClientMemberBalance.setFirstTopUpTime(new Date()); + int updateFlag = baseMapper.updateById(swClientMemberBalance); + if (updateFlag != 1) { + throw new RuntimeException("更新首次充值时间失败:用户钱包ID=" + query.getId()); + } + } + + // 4. 记录充值流水 + String orderNo = StMemberUtil.getOrderNo(); + model.setMemberId(swClientMemberBalance.getMemberId()); + model.setStatus(MemberTransactionTypeEnum.PAY.getCode()); + model.setTransactionAmount(query.getTopUpBalance()); + model.setPaymentMethod(query.getPaymentMethod()); + model.setPayTime(new Date()); + model.setUpdateStatus(0); + model.setOrderNo(orderNo); + model.setSerialNo(StringUtils.isNotBlank(query.getSerialNo()) ? query.getSerialNo() : StMemberUtil.getSerialNo()); + model.setRewardUserNo(query.getRewardUserNo()); + model.setIntegralBalance(query.getIntegralBalance()); + model.setStoredBalance(query.getTopUpBalance()); + model.setRewardBalance(query.getRewardBalance()); + model.setAvailableBalance(swClientMemberBalance.getTopUpBalance().add(swClientMemberBalance.getRewardBalance())); + model.setCanteenId(query.getCanteenId()); + model.setCreateBy(sysUser.getId() + ""); + model.setIsSync(0); + + boolean saveFlag = iStClientMemberTransactionRecordService.saveOrUpdate(model); + if (!saveFlag) { + throw new RuntimeException("充值流水记录失败:用户ID=" + swClientMemberBalance.getMemberId()); + } + + // 5. 所有操作成功才返回true + return true; + } + + /** + * 充值明细导入 + * @param orgList + */ + @Override + @Transactional(rollbackFor = Exception.class) // 类级别事务 + public Map> importRechargeData(List orgList){ + Map> map = new HashMap<>(); + // 分批处理(避免一次性查询大量数据) + int batchSize = 500; + List newListSuccess = new ArrayList<>();//充值成功 + List newListFailure = new ArrayList<>();//充值失败 + List userListMissingFailure = new ArrayList<>();//根据身份证号查询用户或钱包信息缺失失败 + List userListMoreFailure = new ArrayList<>();//根据身份证号查询用户或钱包信息查询多个 + List oldList = new ArrayList<>();//根据流水号历史已导入 + for (int i = 0; i < orgList.size(); i += batchSize) { + List batch = orgList.subList(i, Math.min(i + batchSize, orgList.size())); + // 查询已导入的 流水号 和 未导入的流水号 + Map> resultMap = querySerialNoStatus(batch); + // 获取已导入的集合 + List existingList = resultMap.get(true); + // 获取未导入的集合 + List nonExistingList = resultMap.get(false); + if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(nonExistingList)){ + List cardNoList = nonExistingList.stream().map(StClientMemberTopUpDTO::getCardNo).distinct().collect(Collectors.toList()); + List subUserListByCardNoList = clientMemberBalanceMapper.getSubUserListByCardNoList(cardNoList); + for (StClientMemberTopUpDTO stClientMemberTopUpDTO : nonExistingList) { + + List sysUserSubInfos = subUserListByCardNoList.stream().filter(cardNo -> cardNo.getCardNo().equals(stClientMemberTopUpDTO.getCardNo())).collect(Collectors.toList()); + if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(sysUserSubInfos)){ + if(sysUserSubInfos.size()==1){ + SysUserSubInfo sysUserSubInfo = sysUserSubInfos.get(0); + stClientMemberTopUpDTO.setId(sysUserSubInfo.getBalanceId());//设置会员钱包id + //进行充值操作 + boolean b = this.memberTopUp(stClientMemberTopUpDTO); + if(b){ + //充值成功 + newListSuccess.add(stClientMemberTopUpDTO); + } else { + //充值失败 + newListFailure.add(stClientMemberTopUpDTO); + } + } else { + //根据身份证号查询用户或钱包信息查询多个 + userListMoreFailure.add(stClientMemberTopUpDTO); + } + } else { + //根据身份证号查询用户或钱包信息缺失失败 + userListMissingFailure.add(stClientMemberTopUpDTO); + } + } + } + // 已导入记录已存在 + if (!existingList.isEmpty()) { + oldList.addAll(existingList); + } + } + map.put("newListSuccess",newListSuccess); + map.put("newListFailure",newListFailure); + map.put("oldList",oldList); + map.put("userListMoreFailure",userListMoreFailure); + map.put("userListMissingFailure",userListMissingFailure); + return map; + } + + + /** + * 充值明细导入(全量校验通过后才执行真实充值) + * @param orgList 待导入的充值明细列表 + */ + @Override + @Transactional(rollbackFor = Exception.class) // 类级别事务 + public Map> importRechargeDataV2(List orgList) { + Map> resultMap = new HashMap<>(); + // 初始化各类结果集合 + List newListSuccess = new ArrayList<>();// 最终充值成功 + List newListFailure = new ArrayList<>();// 充值执行失败(校验通过后才可能出现) + List userListMissingFailure = new ArrayList<>();// 校验:用户/钱包信息缺失 + List userListMoreFailure = new ArrayList<>();// 校验:用户/钱包信息重复 + List oldList = new ArrayList<>();// 校验:历史已导入 + // 临时存储校验通过的待充值数据(用于后续批量执行充值) + List validList = new ArrayList<>(); + + // 第一步:全量校验所有数据(不执行实际充值) + int batchSize = 500; + for (int i = 0; i < orgList.size(); i += batchSize) { + List batch = orgList.subList(i, Math.min(i + batchSize, orgList.size())); + // 校验1:查询已导入的流水号(区分已存在和未存在) + Map> serialNoStatusMap = querySerialNoStatus(batch); + List existingList = serialNoStatusMap.get(true); + List nonExistingList = serialNoStatusMap.get(false); + + // 收集已导入的历史数据(校验不通过) + if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(existingList)) { + oldList.addAll(existingList); + } + + // 校验2:未导入的数据继续校验用户/钱包信息 + if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(nonExistingList)) { + // 批量查询当前批次涉及的用户/钱包信息(按卡号去重) + List cardNoList = nonExistingList.stream() + .map(StClientMemberTopUpDTO::getCardNo) + .distinct() + .collect(Collectors.toList()); + List subUserListByCardNoList = clientMemberBalanceMapper.getSubUserListByCardNoList(cardNoList); + + for (StClientMemberTopUpDTO dto : nonExistingList) { + // 筛选当前卡号对应的用户/钱包信息 + List matchedUsers = subUserListByCardNoList.stream() + .filter(user -> user.getCardNo().equals(dto.getCardNo())) + .collect(Collectors.toList()); + + if (org.apache.commons.collections4.CollectionUtils.isEmpty(matchedUsers)) { + // 校验失败:用户/钱包信息缺失 + userListMissingFailure.add(dto); + } else if (matchedUsers.size() > 1) { + // 校验失败:用户/钱包信息重复 + userListMoreFailure.add(dto); + } else { + // 校验通过:记录用户钱包ID,暂存到待充值列表 + SysUserSubInfo validUser = matchedUsers.get(0); + dto.setId(validUser.getBalanceId());// 设置会员钱包ID + validList.add(dto); + } + } + } + } + + // 第二步:判断是否所有数据都校验通过(无任何失败项) + boolean allValid = org.apache.commons.collections4.CollectionUtils.isEmpty(oldList) + && org.apache.commons.collections4.CollectionUtils.isEmpty(userListMissingFailure) + && org.apache.commons.collections4.CollectionUtils.isEmpty(userListMoreFailure) + && validList.size() == orgList.size();// 确保校验通过的数量与原始数量一致 + + if (allValid) {//无任何失败项 + // 所有数据校验通过,执行真实充值操作 + for (StClientMemberTopUpDTO dto : validList) { + boolean rechargeSuccess = this.memberTopUp(dto); + if (rechargeSuccess) { + newListSuccess.add(dto); + } else { + // 任何一次充值失败,抛出异常触发事务回滚 + throw new RuntimeException("充值失败:用户ID=" + dto.getUserId() + ",卡号=" + dto.getCardNo()); + } + } + // 全部导入成功标记 + resultMap.put("allSuccess", newListSuccess); + } else { + //校验失败的情况,把部分成功的数据设置到成功列表,导出错误表格时进行标记区分 + newListSuccess.addAll(validList); + } + // 组装返回结果 + resultMap.put("newListSuccess", newListSuccess); + resultMap.put("newListFailure", newListFailure); + resultMap.put("oldList", oldList); + resultMap.put("userListMoreFailure", userListMoreFailure); + resultMap.put("userListMissingFailure", userListMissingFailure); + return resultMap; + } + + @Override + public List parseExcel(MultipartFile file, String canteenId) { + List snList = new ArrayList<>(); + try { + InputStream inputStream = file.getInputStream(); + String fileName = file.getOriginalFilename(); + Workbook workbook; + if (fileName != null && fileName.endsWith(".xls")) { + // 如果是 .xls 格式,使用 HSSFWorkbook + workbook = new HSSFWorkbook(inputStream); + } else { + // 如果是 .xlsx 格式,使用 XSSFWorkbook + workbook = new XSSFWorkbook(inputStream); + } + Sheet sheet = workbook.getSheetAt(0); + for (int i = 1; i <= sheet.getLastRowNum(); i++) { + Row row = sheet.getRow(i); + if (row != null) { + Cell cell = row.getCell(0); + StClientMemberTopUpDTO query = new StClientMemberTopUpDTO(); + query.setRewardBalance(BigDecimal.ZERO); + query.setIntegralBalance(0); + query.setPaymentMethod(5); + if (cell != null) { + cell.setCellType(CellType.STRING); + String value = cell.getStringCellValue();//缴费单号 + if (StringUtils.isNotBlank(value)) { + query.setSerialNo(value); + } + } + Cell cell1 = row.getCell(3); + if (cell1 != null) { + cell1.setCellType(CellType.STRING); + String value = cell1.getStringCellValue();//身份证号 + if (StringUtils.isNotBlank(value)) { + query.setCardNo(value); + } + } + Cell cell2 = row.getCell(6); + if (cell2 != null) { + cell2.setCellType(CellType.STRING); + String value = cell2.getStringCellValue();//交易金额 + if (StringUtils.isNotBlank(value)) { + query.setTopUpBalance(new BigDecimal(value)); + } + } + query.setCanteenId(canteenId); + snList.add(query); + } + } + workbook.close(); + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException("文件中存在错误数据,请检查文件内容"); + } + snList = snList.stream() + .filter(data -> + // 校验卡号和流水号不为空(字符串非空判断) + StringUtils.isNotBlank(data.getCardNo()) && + StringUtils.isNotBlank(data.getSerialNo()) && + // 校验BigDecimal类型的充值金额:非null且有效(如大于0,根据业务需求调整) + data.getTopUpBalance() != null && + data.getTopUpBalance().compareTo(BigDecimal.ZERO) > 0 + ) + .collect(Collectors.toList()); + return snList; + } + + + /** + * 计算本次扣费金额(一次扣费模式返回实际扣费金额,按量扣费直接返回0) + * @param levelDeductRuleVo 扣费规则 + * @param dinnerType 餐次 + * @param totalDeduction 费用总计(一次扣费模式=固定/固定+补贴,按量扣费模式=实际金额) + * @param spendMoney 消费金额 + * @param rewardDeduction 补贴金额 + * @param userInnerType 用户类型(内部用户=0,外部用户=1) + */ + @Override + public StClientMemberSpendDataVO fixedFee(StClientMemberLevelDeductRuleVo levelDeductRuleVo, String dinnerType, + BigDecimal totalDeduction,BigDecimal spendMoney, BigDecimal rewardDeduction, Integer userInnerType){ + StClientMemberSpendDataVO stClientMemberSpendVO = new StClientMemberSpendDataVO(); + // 1:先判断扣费规则(1:固定扣费,2:固定+补贴扣费,3:按量扣费) + StMemberDeductRuleVO voStMemberDeductRuleVO = levelDeductRuleVo.getStMemberDeductRuleVO(); + if(levelDeductRuleVo.getDeductionType() == 1){//1:固定扣费 + if(userInnerType== 0){//用户类型:内部用户=0 + if("早餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getBreakfast().getInner().getFixedDeduction()); + totalDeduction = spendMoney; + } + if("午餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getLunch().getInner().getFixedDeduction()); + totalDeduction = spendMoney; + } + if("晚餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getDinner().getInner().getFixedDeduction()); + totalDeduction = spendMoney; + } + } else {//用户类型:外部用户=1 + if("早餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getBreakfast().getExternal().getFixedDeduction()); + totalDeduction = spendMoney; + } + if("午餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getLunch().getExternal().getFixedDeduction()); + totalDeduction = spendMoney; + } + if("晚餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getDinner().getExternal().getFixedDeduction()); + totalDeduction = spendMoney; + } + } + } else if(levelDeductRuleVo.getDeductionType() == 2){//2:固定+补贴扣费 + if(userInnerType == 0){//用户类型:内部用户=0 + if("早餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getBreakfast().getInner().getFixedDeduction()); + rewardDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getBreakfast().getInner().getRewardDeduction()); + totalDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getBreakfast().getInner().getTotalDeduction()); + } + if("午餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getLunch().getInner().getFixedDeduction()); + rewardDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getLunch().getInner().getRewardDeduction()); + totalDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getLunch().getInner().getTotalDeduction()); + } + if("晚餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getDinner().getInner().getFixedDeduction()); + rewardDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getDinner().getInner().getRewardDeduction()); + totalDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getDinner().getInner().getTotalDeduction()); + } + } else {//用户类型:外部用户=1 + if("早餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getBreakfast().getExternal().getFixedDeduction()); + rewardDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getBreakfast().getExternal().getRewardDeduction()); + totalDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getBreakfast().getExternal().getTotalDeduction()); + } + if("午餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getLunch().getExternal().getFixedDeduction()); + rewardDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getLunch().getExternal().getRewardDeduction()); + totalDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getLunch().getExternal().getTotalDeduction()); + } + if("晚餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getDinner().getExternal().getFixedDeduction()); + rewardDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getDinner().getExternal().getRewardDeduction()); + totalDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getDinner().getExternal().getTotalDeduction()); + } + } + } + stClientMemberSpendVO.setSpendMoney(spendMoney); + stClientMemberSpendVO.setRewardDeduction(rewardDeduction); + stClientMemberSpendVO.setTotalDeduction(totalDeduction); + return stClientMemberSpendVO; + } + + + @Override + public StClientMemberBackOutVO backOutTopUpList(StClientMemberBackOutTopUpDTO query) { + String id = query.getId(); + String canteenId = query.getCanteenId(); + StClientMemberBackOutVO model = new StClientMemberBackOutVO(); + StClientMemberBalance swClientMemberBalance = baseMapper.selectById(id); + + if (swClientMemberBalance != null) { + model.setUserVO(baseMapper.selectBackOutUser(swClientMemberBalance.getMemberId(), null)); + + ArrayList list = new ArrayList<>(); + list.add(MemberTransactionTypeEnum.BACK_PAY.getCode()); + list.add(MemberTransactionTypeEnum.PAY.getCode()); + List swClientMemberBackOutPayVOS + = baseMapper.selectRunningTabAsBackOut(swClientMemberBalance.getMemberId(), + list, + MemberTransactionTypeEnum.BACK_PAY.getCode(), + canteenId); + if (!swClientMemberBackOutPayVOS.isEmpty()) { + for (StClientMemberBackOutPayVO s : swClientMemberBackOutPayVOS) { + s.setStatusName(MemberTransactionTypeEnum.getName(s.getStatus())); + s.setPaymentMethodName(MemberEnum.getXcxName(s.getPaymentMethod())); + if(MemberTransactionTypeEnum.PAY.getCode().equals(s.getStatus())){ + s.setRemark(null); + } + } + } + model.setPayVO(swClientMemberBackOutPayVOS); + } + return model; + } + + /** + * 线下充值退款方法 + * @param queryDto + * @return + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Result backOutTopUpOffLine(StClientMemberBackTopUpQueryDto queryDto) { + if (queryDto.getId() == null || queryDto.getPaymentMethod() == null || StrUtil.isBlank(queryDto.getRemark())) { + return Result.error("参数异常"); + } + String id = queryDto.getId(); + int flag = 0; + StClientMemberTransactionRecord transactionRecord = iStClientMemberTransactionRecordService.getById(id); + if (transactionRecord != null) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(StClientMemberTransactionRecord::getOrderNo, transactionRecord.getOrderNo()); + queryWrapper.eq(StClientMemberTransactionRecord::getStatus, MemberTransactionTypeEnum.BACK_PAY.getCode()); + StClientMemberTransactionRecord one = iStClientMemberTransactionRecordService.getOne(queryWrapper); + if (one != null) { + return Result.error("该订单已撤销,请勿重复提交"); + } + + String memberId = transactionRecord.getMemberId(); + //最近一次的充值 + StClientMemberBalance mb = baseMapper.selectLastMsgByPayTime(memberId, MemberTransactionTypeEnum.PAY.getCode()); + //如果时间小于要撤销的数据时间 + if (mb != null && mb.getCreateTime().after(transactionRecord.getCreateTime())) { + return Result.error("只能撤销最近一次的充值"); + } + //最近一次的消费 + ArrayList list = new ArrayList<>(); + list.add(MemberTransactionTypeEnum.EXPENDITURE.getCode()); + list.add(MemberTransactionTypeEnum.BALANCE_ADD.getCode()); + list.add(MemberTransactionTypeEnum.BALANCE_SUBTRACT.getCode()); + StClientMemberBalance smb = baseMapper.selectLastMsgByTime(memberId, list); + if (smb != null && smb.getCreateTime().after(transactionRecord.getCreateTime())) { + return Result.error("充值后有消费、余额调整记录,不允许撤销充值"); + } + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(StClientMemberBalance::getMemberId, memberId); + StClientMemberBalance swClientMemberBalance = baseMapper.selectOne(wrapper); + if (swClientMemberBalance != null) { + //退款 + StClientMemberBackTopUpDto query = new StClientMemberBackTopUpDto(); + query.setId(swClientMemberBalance.getId()); + query.setOldTopUpBalance(swClientMemberBalance.getTopUpBalance()); + query.setOldRewardBalance(swClientMemberBalance.getRewardBalance()); + query.setTopUpSubtractBalance(transactionRecord.getStoredBalance()); + query.setRewardSubtractBalance(transactionRecord.getRewardBalance()); + query.setIntegralSubtractBalance(transactionRecord.getIntegralBalance()); + flag = baseMapper.backOutTopUp(query); + swClientMemberBalance = baseMapper.selectOne(wrapper); + if (flag == 1) { + //流水表添加记录 + StClientMemberTransactionRecord model = new StClientMemberTransactionRecord(); + model.setMemberId(memberId); + model.setCanteenId(transactionRecord.getCanteenId()); + model.setOrderNo(transactionRecord.getOrderNo()); + model.setStatus(MemberTransactionTypeEnum.BACK_PAY.getCode()); + model.setTransactionAmount(transactionRecord.getStoredBalance()); + model.setPaymentMethod(queryDto.getPaymentMethod()); + model.setStoredBalance(transactionRecord.getStoredBalance()); + model.setRewardBalance(transactionRecord.getRewardBalance()); + model.setIntegralBalance(transactionRecord.getIntegralBalance()); + model.setPayTime(new Date()); + model.setUpdateStatus(0); + model.setRemark(queryDto.getRemark()); + model.setAvailableBalance(swClientMemberBalance.getTopUpBalance().add(swClientMemberBalance.getRewardBalance())); + model.setSerialNo(StMemberUtil.getSerialNo()); + model.setIsSync(0); + iStClientMemberTransactionRecordService.save(model); + } + } + } + return Result.OK("操作成功"); + } + + /** + * 根据订单号获取 充值订单记录信息 + * @param payOrderNo 小程序充值成功后微信订单号 + * @return 订单记录信息 + */ + @Override + public StClientMemberTransactionRecord getRecordInfoAboutRechargeByPayOrderNo(String payOrderNo,String orderNo,List integerList){ + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if(StringUtils.isNotBlank(orderNo)){ + queryWrapper.eq(StClientMemberTransactionRecord::getOrderNo, orderNo); + } + if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(integerList)){ + queryWrapper.in(StClientMemberTransactionRecord::getStatus, integerList); + } + if(StringUtils.isNotBlank(payOrderNo)){ + queryWrapper.eq(StClientMemberTransactionRecord::getPayOrderNo, payOrderNo); + } + queryWrapper.eq(StClientMemberTransactionRecord::getIsDel, 0); + return iStClientMemberTransactionRecordService.getOne(queryWrapper); + } + + /** + * 充值退款后逻辑 + * 通过充值记录 生成退款记录 , 计算balance表数据 + * @param czRecord 充值记录 + */ + @Override + public void backOrder( StClientMemberTransactionRecord czRecord){ + + //记录数据生成 + StClientMemberBackTopUpQueryDto queryDto = new StClientMemberBackTopUpQueryDto(); + queryDto.setPaymentMethod(czRecord.getPaymentMethod()); + if(StringUtils.isNotBlank(czRecord.getRemark())){ + queryDto.setRemark(czRecord.getRemark()); + }else{ + queryDto.setRemark("小程序充值退款[全额原路退]"); + } + + //获取用户当前余额信息 + LambdaQueryWrapper balance = new LambdaQueryWrapper<>(); + balance.eq(StClientMemberBalance::getMemberId, czRecord.getMemberId()); + StClientMemberBalance swClientMemberBalance = baseMapper.selectOne(balance); + if(swClientMemberBalance != null){ + //退款 + StClientMemberBackTopUpDto query = new StClientMemberBackTopUpDto(); + query.setId(swClientMemberBalance.getId()); + query.setOldTopUpBalance(swClientMemberBalance.getTopUpBalance()); + query.setOldRewardBalance(swClientMemberBalance.getRewardBalance()); + + query.setTopUpSubtractBalance(czRecord.getStoredBalance()); + query.setRewardSubtractBalance(czRecord.getRewardBalance()); + query.setIntegralSubtractBalance(czRecord.getIntegralBalance()); + //余额表变更 + int flag = baseMapper.backOutTopUp(query); + //再次查询用户当前余额信息 + swClientMemberBalance = baseMapper.selectOne(balance); + if (flag == 1) { + //流水表添加记录 + StClientMemberTransactionRecord model = new StClientMemberTransactionRecord(); + model.setMemberId(czRecord.getMemberId()); + model.setCanteenId(czRecord.getCanteenId()); + //生成撤销订单 +// model.setOrderNo(StMemberUtil.getUnOrderNo()); + model.setOrderNo(czRecord.getOrderNo());//此处生成撤销充值订单编号必须和之前充值单号一直,充值列表的operationStatus才能标记为不能重复撤销充值 + model.setStatus(MemberTransactionTypeEnum.BACK_PAY.getCode()); + model.setTransactionAmount(czRecord.getStoredBalance()); + model.setPaymentMethod(queryDto.getPaymentMethod()); + model.setStoredBalance(czRecord.getStoredBalance()); + model.setRewardBalance(czRecord.getRewardBalance()); + model.setIntegralBalance(czRecord.getIntegralBalance()); + model.setPayTime(new Date()); + model.setUpdateStatus(0); + model.setRemark(queryDto.getRemark()); + model.setAvailableBalance(swClientMemberBalance.getTopUpBalance().add(swClientMemberBalance.getRewardBalance())); + model.setSerialNo(StMemberUtil.getSerialNo()); + model.setIsSync(0); + //保存撤销记录 + iStClientMemberTransactionRecordService.save(model); + } + } + } + + + /** + * 用户余额消费统一扣费方法 + * query.id 必填:用户id + * query.orderNo 必填:订单号 + * @return StClientMemberSpendVO + * @throws Exception + */ + @Override + @Transactional(rollbackFor = Exception.class) + public StClientMemberSpendVO memberSpend(StClientMemberSpendDTO query) throws Exception { + if (query.getId() == null || StrUtil.isBlank(query.getOrderNo())) { + throw new Exception("参数异常"); + } + StBbzxOrderInfo stBbzxOrderInfo = iStBbzxOrderInfoService.getSwBbzxOrderInfo(query.getOrderNo()); + if(null == stBbzxOrderInfo){ + throw new Exception("未查询到订单"); + } + String canteenId = null; + if(StringUtils.isNotBlank(query.getCanteenId())){ + canteenId = query.getCanteenId(); + } else { + canteenId = stBbzxOrderInfo.getCanteenId(); + } + StClientMemberSpendVO resultModel = new StClientMemberSpendVO(); + //已有支付订单信息,有原路返回处理重复扣费,未支付成功的正常走流程 + if(stBbzxOrderInfo.getStatus() == OrderStatus.ORDER_STATUS_HAS_CHECK_OUT.getCode()){ + resultModel.setPriceSpread(null); + resultModel.setPreferentialPrice(stBbzxOrderInfo.getPreferentialPrice()); + resultModel.setSpendMoney(stBbzxOrderInfo.getCustomerPrice()); + StMiniMineBalanceVo balanceInfo1 = baseMapper.getBalanceInfo(query.getId()); + resultModel.setBalance(new BigDecimal(balanceInfo1.getBalance())); + resultModel.setSuccess(true); + return resultModel; + } + //获取订单对应餐次 + String dinnerType = stBbzxOrderInfo.getDinnerType(); + + StClientMemberSpendMapperDTO param = new StClientMemberSpendMapperDTO(); + //赠送积分 + Integer integral = 0; + //实际扣减额 + BigDecimal spendMoney = BigDecimal.ZERO;//用户实际扣费金额 + //当前会员的余额信息 + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper(); + lambdaQueryWrapper.eq(StClientMemberBalance::getMemberId, query.getId()); + lambdaQueryWrapper.eq(StClientMemberBalance::getIsDel, 0); +// lambdaQueryWrapper.eq(StClientMemberBalance::getCanteenId, canteenId); + StClientMemberBalance model = baseMapper.selectOne(lambdaQueryWrapper); + if (model == null) { + throw new Exception("用户不存在"); + } + param.setId(model.getId()); + //充值 + 赠送余额 + BigDecimal total = model.getTopUpBalance().add(model.getRewardBalance()); + + Result result = iStClientMemberLevelDemotionTimeService.memberLevelDemotionTimeByCanteenId(canteenId); + if (!result.getCode().equals(CommonConstant.SC_OK_200)) { + throw new Exception(result.getMessage()); + } + StClientMemberLevelDemotionTime memberLevel = (StClientMemberLevelDemotionTime) result.getResult(); + + SlBaseRestaurant slBaseRestaurant = iSlBaseRestaurantService.getById(canteenId); + if (null == slBaseRestaurant) {//是否支持透支消费(0:不支持,1:支持) + throw new Exception("食堂不存在"); + } + // "是否使用支持外部人员就餐(0:不支持,1:支持)" + Integer isExternalDine = slBaseRestaurant.getIsExternalDine(); + //"是否支持透支消费(0:不支持,1:支持)" + Integer isOverdraftDine = slBaseRestaurant.getIsOverdraftDine(); + // "是否开启使用前端结算金额(0:不启用,1:启用)" + Integer isHeadendSettlement = slBaseRestaurant.getIsHeadendSettlement(); + // "前端结算价格浮动元(当开启使用前端结算金额isHeadendSettlement=1时:后端价格和前端价格上下相差不超过此金额,优先采用前端价格,否则采取后台价格)" + BigDecimal headendSettlementFloat = slBaseRestaurant.getHeadendSettlementFloat(); + if(isExternalDine == 0 && model.getUserInnerType() == 1){ + throw new Exception("该食堂不支持外部用户就餐"); + } + // 1:先判断扣费规则(1:固定扣费,2:固定+补贴扣费,3:按量扣费) + StClientMemberLevelDeductRuleVo stClientMemberLevelDeductRuleVo = new StClientMemberLevelDeductRuleVo(); + stClientMemberLevelDeductRuleVo.setCanteenId(canteenId); + StClientMemberLevelDeductRuleVo levelDeductRuleVo = iStClientMemberLevelDeductRuleService.getOneService(stClientMemberLevelDeductRuleVo); + if(null == levelDeductRuleVo){ + throw new Exception("食堂未设置扣费规则"); + } + StMemberDeductRuleVO voStMemberDeductRuleVO = levelDeductRuleVo.getStMemberDeductRuleVO(); + + BigDecimal rewardDeduction = BigDecimal.ZERO;//用户扣费补贴金额 + BigDecimal totalDeduction = BigDecimal.ZERO;//原订单总金额 + if(levelDeductRuleVo.getDeductionType() == 1){//1:固定扣费 + if(model.getUserInnerType() == 0){//用户类型:内部用户=0 + if("早餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getBreakfast().getInner().getFixedDeduction()); + totalDeduction = spendMoney; + } + if("午餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getLunch().getInner().getFixedDeduction()); + totalDeduction = spendMoney; + } + if("晚餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getDinner().getInner().getFixedDeduction()); + totalDeduction = spendMoney; + } + } else {//用户类型:外部用户=1 + if("早餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getBreakfast().getExternal().getFixedDeduction()); + totalDeduction = spendMoney; + } + if("午餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getLunch().getExternal().getFixedDeduction()); + totalDeduction = spendMoney; + } + if("晚餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getDinner().getExternal().getFixedDeduction()); + totalDeduction = spendMoney; + } + } + } else if(levelDeductRuleVo.getDeductionType() == 2){//2:固定+补贴扣费 + if(model.getUserInnerType() == 0){//用户类型:内部用户=0 + if("早餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getBreakfast().getInner().getFixedDeduction()); + rewardDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getBreakfast().getInner().getRewardDeduction()); + totalDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getBreakfast().getInner().getTotalDeduction()); + } + if("午餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getLunch().getInner().getFixedDeduction()); + rewardDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getLunch().getInner().getRewardDeduction()); + totalDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getLunch().getInner().getTotalDeduction()); + } + if("晚餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getDinner().getInner().getFixedDeduction()); + rewardDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getDinner().getInner().getRewardDeduction()); + totalDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getDinner().getInner().getTotalDeduction()); + } + } else {//用户类型:外部用户=1 + if("早餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getBreakfast().getExternal().getFixedDeduction()); + rewardDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getBreakfast().getExternal().getRewardDeduction()); + totalDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getBreakfast().getExternal().getTotalDeduction()); + } + if("午餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getLunch().getExternal().getFixedDeduction()); + rewardDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getLunch().getExternal().getRewardDeduction()); + totalDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getLunch().getExternal().getTotalDeduction()); + } + if("晚餐".equals(dinnerType)){ + spendMoney = BigDecimal.valueOf(voStMemberDeductRuleVO.getDinner().getExternal().getFixedDeduction()); + rewardDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getDinner().getExternal().getRewardDeduction()); + totalDeduction = BigDecimal.valueOf(voStMemberDeductRuleVO.getDinner().getExternal().getTotalDeduction()); + } + } + } else {//3:按量扣费 + // 2:如果是按量扣费模式,再判断是否使用前端金额(0:不启用,1:启用) + if(isHeadendSettlement == 1){//开启使用前端金额 + //前端金额大于等于后端金额,用前端金额 + if(stBbzxOrderInfo.getCustomerHeadendPrice().compareTo(stBbzxOrderInfo.getCustomerBackendPrice()) >= 0){ + spendMoney = stBbzxOrderInfo.getCustomerHeadendPrice(); + } else {//前端金额小于后端金额 + BigDecimal subtract = stBbzxOrderInfo.getCustomerBackendPrice().subtract(stBbzxOrderInfo.getCustomerHeadendPrice()); + //超出浮动范围,使用后端金额(浮动金额设置10元,后端金额20 - 前端金额5 = 15 >= 10,使用后端金额) + if(subtract.compareTo(headendSettlementFloat) >= 0){ + spendMoney = stBbzxOrderInfo.getCustomerBackendPrice(); + } else { + //在浮动范围内用前端金额(浮动金额设置10元,后端金额20 - 前端金额18 = 2 < 10,使用前端金额) + spendMoney = stBbzxOrderInfo.getCustomerHeadendPrice(); + } + } + } else {//不开启使用前端金额,前端 和 后端 金额对比,谁大取谁 + if(stBbzxOrderInfo.getCustomerHeadendPrice().compareTo(stBbzxOrderInfo.getCustomerBackendPrice()) >= 0){ + spendMoney = stBbzxOrderInfo.getCustomerHeadendPrice(); + } else{ + spendMoney = stBbzxOrderInfo.getCustomerBackendPrice(); + } + } + } + // 3:判断食堂是否支持透支消费 + if (spendMoney.compareTo(total) > -1 && isOverdraftDine == 0) { + if (total.equals(new BigDecimal("0"))) { + throw new Exception("会员暂无可用余额"); + } + //消费额大于等于总余额,直接扣减总额,剩余差额return + throw new Exception("食堂不支持透支消费!"); + } else if (spendMoney.compareTo(total) > -1 && isOverdraftDine == 1) { + //"应补差额" + BigDecimal priceSpread = spendMoney.subtract(total); + param.setConsumptionTotal(total); + param.setTopUpBalance(spendMoney);//直接将实付金额传入,余额更新为负值 + param.setRewardBalance(model.getRewardBalance());//赠送金额清零 + resultModel.setPriceSpread(priceSpread); + } else { + // 4:最后判断扣费顺序(1储值和赠送余额等比例扣减,2先储值余额,后赠送余额,3先赠送余额,后储值余额) + //消费额小于总余额,三种扣费方式 + param.setConsumptionTotal(spendMoney); + //默认等比例扣减 + MemberLevelEnum type = MemberLevelEnum.getEnumCode(null != memberLevel?memberLevel.getPaymentOrder():null); + //应扣储值额 + BigDecimal topUpBalance = new BigDecimal("0"); + //应扣赠送额 + BigDecimal rewardBalance = new BigDecimal("0"); + switch (type) { + //储值和赠送余额等比例扣减 + case PAY_SEQUENCE_BY_RATIO: + //比例 + BigDecimal bigDecimal = model.getTopUpBalance().divide(total, 2, BigDecimal.ROUND_HALF_UP); + //应减储值额 + topUpBalance = spendMoney.multiply(bigDecimal); + rewardBalance = spendMoney.subtract(topUpBalance); + break; + //先储值余额,后赠送余额 + case PAY_SEQUENCE_BY_TOP_UP: + //如果储值余额小于应扣额,拆分 + if (model.getTopUpBalance().compareTo(spendMoney) == -1) { + topUpBalance = model.getTopUpBalance(); + rewardBalance = spendMoney.subtract(topUpBalance); + } else { + topUpBalance = spendMoney; + } + break; + //先赠送余额,后储值余额 + case PAY_SEQUENCE_BY_REWARD: + //如果赠送余额小于应扣额,拆分 + if (model.getRewardBalance().compareTo(spendMoney) == -1) { + rewardBalance = model.getRewardBalance(); + topUpBalance = spendMoney.subtract(rewardBalance); + } else { + rewardBalance = spendMoney; + } + break; + } + param.setTopUpBalance(topUpBalance); + param.setRewardBalance(rewardBalance); + + resultModel.setPriceSpread(new BigDecimal("0")); + } + //进行扣费操作 + boolean resultStatus = baseMapper.memberSpend(param) == 1; + if(!resultStatus){ + throw new Exception("余额更新失败"); + } + resultModel.setSuccess(resultStatus); + + resultModel.setPreferentialPrice(rewardDeduction); + resultModel.setSpendMoney(spendMoney); + StMiniMineBalanceVo balanceInfo = baseMapper.getBalanceInfo(query.getId()); + resultModel.setBalance(new BigDecimal(balanceInfo.getBalance())); + + //更新订单状态和最终支付金额 + stBbzxOrderInfo.setStatus(OrderStatus.ORDER_STATUS_HAS_CHECK_OUT.getCode()); +// stBbzxOrderInfo.setOrderDiscount(); +// stBbzxOrderInfo.setOrderDiscountReason(); +// stBbzxOrderInfo.setOrderDerate(); + stBbzxOrderInfo.setOrderPrice(totalDeduction); + stBbzxOrderInfo.setCustomerPrice(spendMoney); +// stBbzxOrderInfo.setPayTotal(); + stBbzxOrderInfo.setPreferentialPrice(rewardDeduction); + iStBbzxOrderInfoService.updateById(stBbzxOrderInfo); + + LambdaQueryWrapper lambdaQueryWrapper1 = new LambdaQueryWrapper(); + lambdaQueryWrapper1.eq(StBbzxOrderInfoPay::getOrderNo,query.getOrderNo()); + StBbzxOrderInfoPay stBbzxOrderInfoPay = iStBbzxOrderInfoPayService.getOne(lambdaQueryWrapper1); + stBbzxOrderInfoPay.setStatus(1); + stBbzxOrderInfoPay.setPayType(4); + stBbzxOrderInfoPay.setPayDiscount(rewardDeduction); + stBbzxOrderInfoPay.setPayIncome(spendMoney); + iStBbzxOrderInfoPayService.updateById(stBbzxOrderInfoPay); + //消费送积分 +// if (!MemberLevelEnum.COMMON_LEVEL_MEMBER.getCode().equals(swClientMember.getMemberLevelId())) { +// StClientMemberLevelRule levelRule = swClientMemberLevelRuleService.getOne(new LambdaQueryWrapper() +// .eq(SwClientMemberLevelRule::getMemberLevelId, swClientMember.getMemberLevelId())); +// if (levelRule != null && StrUtil.isNotBlank(levelRule.getIntegralRule())) { +// JSON parse = JSONUtil.parse(levelRule.getIntegralRule()); +// //消费额 +// BigDecimal expenditure = new BigDecimal(String.valueOf(parse.getByPath("expenditure"))); +// //奖励积分 +// String awardIntegral = String.valueOf(parse.getByPath("awardIntegral")); +// //取商 +// BigDecimal number = deductionAmount.divide(expenditure, 0, BigDecimal.ROUND_DOWN); +// //计算赠送积分 +// integral = number.multiply(new BigDecimal(awardIntegral)).intValue(); +// +// //会员余额表叠加积分 +// StClientMemberTopUpDTO swClientMemberTopUpDTO = new StClientMemberTopUpDTO(); +// swClientMemberTopUpDTO.setId(model.getId()); +// swClientMemberTopUpDTO.setIntegralBalance(integral); +// if (query.getInvoice() != null && !query.getInvoice()) { +// swClientMemberTopUpDTO.setInvoiceBalance(deductionAmount); +// } +// +// baseMapper.memberTopUp(swClientMemberTopUpDTO); +// } +// } + //消费记录数据添加 + StClientMemberTransactionRecord mtr = new StClientMemberTransactionRecord(); + mtr.setMemberId(query.getId()); + mtr.setSerialNo(RandomUtil.randomNumbers(19)); + mtr.setOrderNo(query.getOrderNo()); + mtr.setStatus(MemberTransactionTypeEnum.EXPENDITURE.getCode()); + mtr.setTransactionAmount(spendMoney.subtract(resultModel.getPriceSpread())); + mtr.setPaymentMethod(MemberEnum.PAYMENT_METHOD_BY_CARD.getCode()); + mtr.setStoredBalance(param.getTopUpBalance()); + mtr.setRewardBalance(param.getRewardBalance()); + mtr.setIntegralBalance(integral); + //可用余额 + mtr.setAvailableBalance( + model.getTopUpBalance() + .add(model.getRewardBalance()) + .subtract(param.getTopUpBalance()) + .subtract(param.getRewardBalance()) + ); + mtr.setPayTime(new Date()); + mtr.setUpdateStatus(0); + mtr.setRemark(query.getRemark()); + mtr.setCreateBy("0"); + mtr.setCanteenId(canteenId); + iStClientMemberTransactionRecordService.save(mtr); + return resultModel; + } + + + + /** + * 用户余额消费统一扣费方法 + * query.id 必填:用户id + * query.orderNo 必填:订单号 + * @return StClientMemberSpendVO + * @throws Exception + */ + @Override + @Transactional(rollbackFor = Exception.class) + public StClientMemberSpendVO memberSpendV2(StClientMemberSpendDTO query) throws Exception { + if (query.getId() == null || StrUtil.isBlank(query.getOrderNo())) { + throw new Exception("参数异常"); + } + StBbzxOrderInfo stBbzxOrderInfo = iStBbzxOrderInfoService.getSwBbzxOrderInfo(query.getOrderNo()); + if(null == stBbzxOrderInfo){ + throw new Exception("未查询到订单"); + } + String canteenId = null; + if(StringUtils.isNotBlank(query.getCanteenId())){ + canteenId = query.getCanteenId(); + } else { + canteenId = stBbzxOrderInfo.getCanteenId(); + } + StClientMemberSpendVO resultModel = new StClientMemberSpendVO(); + //已有支付订单信息,有原路返回处理重复扣费,未支付成功的正常走流程 +// if(stBbzxOrderInfo.getStatus() == OrderStatus.ORDER_STATUS_HAS_CHECK_OUT.getCode()){ +// resultModel.setPriceSpread(null); +// resultModel.setPreferentialPrice(stBbzxOrderInfo.getPreferentialPrice()); +// resultModel.setSpendMoney(stBbzxOrderInfo.getCustomerPrice()); +// StMiniMineBalanceVo balanceInfo1 = baseMapper.getBalanceInfo(query.getId()); +// resultModel.setBalance(new BigDecimal(balanceInfo1.getBalance())); +// resultModel.setSuccess(true); +// return resultModel; +// } + + StMiniMineBalanceVo miniMineBalanceVo = query.getMiniMineBalanceVo(); + + StClientMemberSpendMapperDTO param = new StClientMemberSpendMapperDTO(); + //赠送积分 + Integer integral = 0; + //实际扣减额 + BigDecimal spendMoney = miniMineBalanceVo.getAmountThis();//用户实际扣费金额-本次消费金额 + //当前会员的余额信息 + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper(); + lambdaQueryWrapper.eq(StClientMemberBalance::getMemberId, query.getId()); + lambdaQueryWrapper.eq(StClientMemberBalance::getIsDel, 0); + StClientMemberBalance model = baseMapper.selectOne(lambdaQueryWrapper); + if (model == null) { + throw new Exception("用户不存在"); + } + param.setId(model.getId()); + //充值 + 赠送余额 + BigDecimal total = model.getTopUpBalance().add(model.getRewardBalance()); + + Result result = iStClientMemberLevelDemotionTimeService.memberLevelDemotionTimeByCanteenId(canteenId); + if (!result.getCode().equals(CommonConstant.SC_OK_200)) { + throw new Exception(result.getMessage()); + } + StClientMemberLevelDemotionTime memberLevel = (StClientMemberLevelDemotionTime) result.getResult(); + + SlBaseRestaurant slBaseRestaurant = iSlBaseRestaurantService.getById(canteenId); + if (null == slBaseRestaurant) {//是否支持透支消费(0:不支持,1:支持) + throw new Exception("食堂不存在"); + } + //"是否支持透支消费(0:不支持,1:支持)" + Integer isOverdraftDine = slBaseRestaurant.getIsOverdraftDine(); + BigDecimal rewardDeduction = BigDecimal.ZERO;//用户扣费补贴金额 + BigDecimal totalDeduction = spendMoney;//原订单总金额 + // 3:判断食堂是否支持透支消费 + if (spendMoney.compareTo(total) > -1 && isOverdraftDine == 0) { + if (total.equals(new BigDecimal("0"))) { + throw new Exception("会员暂无可用余额"); + } + //消费额大于等于总余额,直接扣减总额,剩余差额return + throw new Exception("食堂不支持透支消费!"); + } else if (spendMoney.compareTo(total) > -1 && isOverdraftDine == 1) { + //"应补差额" + BigDecimal priceSpread = spendMoney.subtract(total); + param.setConsumptionTotal(total); + param.setTopUpBalance(spendMoney);//直接将实付金额传入,余额更新为负值 + param.setRewardBalance(model.getRewardBalance());//赠送金额清零 + resultModel.setPriceSpread(priceSpread); + } else { + // 4:最后判断扣费顺序(1储值和赠送余额等比例扣减,2先储值余额,后赠送余额,3先赠送余额,后储值余额) + //消费额小于总余额,三种扣费方式 + param.setConsumptionTotal(spendMoney); + //默认等比例扣减 + MemberLevelEnum type = MemberLevelEnum.getEnumCode(null != memberLevel?memberLevel.getPaymentOrder():null); + //应扣储值额 + BigDecimal topUpBalance = new BigDecimal("0"); + //应扣赠送额 + BigDecimal rewardBalance = new BigDecimal("0"); + switch (type) { + //储值和赠送余额等比例扣减 + case PAY_SEQUENCE_BY_RATIO: + //比例 + BigDecimal bigDecimal = model.getTopUpBalance().divide(total, 2, BigDecimal.ROUND_HALF_UP); + //应减储值额 + topUpBalance = spendMoney.multiply(bigDecimal); + rewardBalance = spendMoney.subtract(topUpBalance); + break; + //先储值余额,后赠送余额 + case PAY_SEQUENCE_BY_TOP_UP: + //如果储值余额小于应扣额,拆分 + if (model.getTopUpBalance().compareTo(spendMoney) == -1) { + topUpBalance = model.getTopUpBalance(); + rewardBalance = spendMoney.subtract(topUpBalance); + } else { + topUpBalance = spendMoney; + } + break; + //先赠送余额,后储值余额 + case PAY_SEQUENCE_BY_REWARD: + //如果赠送余额小于应扣额,拆分 + if (model.getRewardBalance().compareTo(spendMoney) == -1) { + rewardBalance = model.getRewardBalance(); + topUpBalance = spendMoney.subtract(rewardBalance); + } else { + rewardBalance = spendMoney; + } + break; + } + param.setTopUpBalance(topUpBalance); + param.setRewardBalance(rewardBalance); + + resultModel.setPriceSpread(new BigDecimal("0")); + } + //进行扣费操作 + boolean resultStatus = baseMapper.memberSpend(param) == 1; + if(!resultStatus){ + throw new Exception("余额更新失败"); + } + resultModel.setSuccess(resultStatus); + + resultModel.setPreferentialPrice(rewardDeduction); + resultModel.setSpendMoney(spendMoney); + StMiniMineBalanceVo balanceInfo = baseMapper.getBalanceInfo(query.getId()); + resultModel.setBalance(new BigDecimal(balanceInfo.getBalance())); + + //更新订单状态和最终支付金额 + stBbzxOrderInfo.setStatus(OrderStatus.ORDER_STATUS_HAS_CHECK_OUT.getCode()); +// stBbzxOrderInfo.setOrderDiscount(); +// stBbzxOrderInfo.setOrderDiscountReason(); +// stBbzxOrderInfo.setOrderDerate(); + stBbzxOrderInfo.setOrderPrice(totalDeduction); + stBbzxOrderInfo.setCustomerPrice(spendMoney); +// stBbzxOrderInfo.setPayTotal(); + stBbzxOrderInfo.setPreferentialPrice(rewardDeduction); + stBbzxOrderInfo.setPayMemberId(query.getId()); + iStBbzxOrderInfoService.updateById(stBbzxOrderInfo); + + LambdaQueryWrapper lambdaQueryWrapper1 = new LambdaQueryWrapper(); + lambdaQueryWrapper1.eq(StBbzxOrderInfoPay::getOrderNo,query.getOrderNo()); + StBbzxOrderInfoPay stBbzxOrderInfoPay = iStBbzxOrderInfoPayService.getOne(lambdaQueryWrapper1); + if(null == stBbzxOrderInfoPay){ + stBbzxOrderInfoPay = new StBbzxOrderInfoPay(); + stBbzxOrderInfoPay.setOrderId(stBbzxOrderInfo.getId()); + stBbzxOrderInfoPay.setOrderNo(stBbzxOrderInfo.getOrderNo()); + } + stBbzxOrderInfoPay.setStatus(1); + stBbzxOrderInfoPay.setPayType(4); + stBbzxOrderInfoPay.setPayDiscount(rewardDeduction); + stBbzxOrderInfoPay.setPayIncome(spendMoney); + stBbzxOrderInfoPay.setPayPrice(spendMoney); + iStBbzxOrderInfoPayService.saveOrUpdate(stBbzxOrderInfoPay); + //消费送积分 +// if (!MemberLevelEnum.COMMON_LEVEL_MEMBER.getCode().equals(swClientMember.getMemberLevelId())) { +// StClientMemberLevelRule levelRule = swClientMemberLevelRuleService.getOne(new LambdaQueryWrapper() +// .eq(SwClientMemberLevelRule::getMemberLevelId, swClientMember.getMemberLevelId())); +// if (levelRule != null && StrUtil.isNotBlank(levelRule.getIntegralRule())) { +// JSON parse = JSONUtil.parse(levelRule.getIntegralRule()); +// //消费额 +// BigDecimal expenditure = new BigDecimal(String.valueOf(parse.getByPath("expenditure"))); +// //奖励积分 +// String awardIntegral = String.valueOf(parse.getByPath("awardIntegral")); +// //取商 +// BigDecimal number = deductionAmount.divide(expenditure, 0, BigDecimal.ROUND_DOWN); +// //计算赠送积分 +// integral = number.multiply(new BigDecimal(awardIntegral)).intValue(); +// +// //会员余额表叠加积分 +// StClientMemberTopUpDTO swClientMemberTopUpDTO = new StClientMemberTopUpDTO(); +// swClientMemberTopUpDTO.setId(model.getId()); +// swClientMemberTopUpDTO.setIntegralBalance(integral); +// if (query.getInvoice() != null && !query.getInvoice()) { +// swClientMemberTopUpDTO.setInvoiceBalance(deductionAmount); +// } +// +// baseMapper.memberTopUp(swClientMemberTopUpDTO); +// } +// } + //消费记录数据添加 + StClientMemberTransactionRecord mtr = new StClientMemberTransactionRecord(); + mtr.setMemberId(stBbzxOrderInfo.getMemberId()); + mtr.setPayMemberId(query.getId()); + mtr.setSerialNo(RandomUtil.randomNumbers(19)); + mtr.setOrderNo(query.getOrderNo()); + mtr.setStatus(MemberTransactionTypeEnum.EXPENDITURE.getCode()); + mtr.setTransactionAmount(spendMoney.subtract(resultModel.getPriceSpread())); + mtr.setPaymentMethod(MemberEnum.PAYMENT_METHOD_BY_CARD.getCode()); + mtr.setStoredBalance(param.getTopUpBalance()); + mtr.setRewardBalance(param.getRewardBalance()); + mtr.setIntegralBalance(integral); + //可用余额 + mtr.setAvailableBalance( + model.getTopUpBalance() + .add(model.getRewardBalance()) + .subtract(param.getTopUpBalance()) + .subtract(param.getRewardBalance()) + ); + mtr.setTransactionAmount(param.getTopUpBalance()); + mtr.setPayTime(new Date()); + mtr.setUpdateStatus(0); + mtr.setRemark(query.getRemark()); + mtr.setCreateBy("0"); + mtr.setCanteenId(canteenId); + iStClientMemberTransactionRecordService.save(mtr); + return resultModel; + } + + + + /** + * 用户余额消费统一扣费方法 + * query.id 必填:用户id + * query.orderNo 必填:订单号 + * @return StClientMemberSpendVO + * @throws Exception + */ + @Override + @Transactional(rollbackFor = Exception.class) + public StClientMemberSpendVO memberSpendV3(StClientMemberSpendDTO query) { + StClientMemberSpendVO resultModel = new StClientMemberSpendVO(); + if (query.getId() == null || StrUtil.isBlank(query.getOrderNo())) { + resultModel.setMsg("支付用户ID为空,订单号为空,参数异常"); + resultModel.setSuccess(false); + return resultModel; + } + StBbzxOrderInfo stBbzxOrderInfo = iStBbzxOrderInfoService.getSwBbzxOrderInfo(query.getOrderNo()); + if(null == stBbzxOrderInfo){ + resultModel.setMsg("未查询到订单:"+query.getOrderNo()); + resultModel.setSuccess(false); + return resultModel; + } + String canteenId = null; + if(StringUtils.isNotBlank(query.getCanteenId())){ + canteenId = query.getCanteenId(); + } else { + canteenId = stBbzxOrderInfo.getCanteenId(); + } + + StMiniMineBalanceVo miniMineBalanceVo = query.getMiniMineBalanceVo(); + + StClientMemberSpendMapperDTO param = new StClientMemberSpendMapperDTO(); + //赠送积分 + Integer integral = 0; + //实际扣减额 + BigDecimal spendMoney = miniMineBalanceVo.getAmountThis();//用户实际扣费金额-本次消费金额 + //当前会员的余额信息 + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper(); + lambdaQueryWrapper.eq(StClientMemberBalance::getMemberId, query.getId()); + lambdaQueryWrapper.eq(StClientMemberBalance::getIsDel, 0); + StClientMemberBalance model = baseMapper.selectOne(lambdaQueryWrapper); + if (model == null) { + resultModel.setMsg("支付用户不存在:"+query.getId()); + resultModel.setSuccess(false); + return resultModel; + } + param.setId(model.getId()); + //充值 + 赠送余额 + BigDecimal total = model.getTopUpBalance().add(model.getRewardBalance()); + + SlBaseRestaurant slBaseRestaurant = iSlBaseRestaurantService.getById(canteenId); + if (null == slBaseRestaurant) {//是否支持透支消费(0:不支持,1:支持) + resultModel.setMsg("食堂不存在:"+canteenId); + resultModel.setSuccess(false); + return resultModel; + } + //"是否支持透支消费(0:不支持,1:支持)" + Integer isOverdraftDine = slBaseRestaurant.getIsOverdraftDine(); + BigDecimal rewardDeduction = BigDecimal.ZERO;//用户扣费补贴金额 + BigDecimal totalDeduction = spendMoney;//原订单总金额 + // 当余额不足时判断食堂是否支持透支消费 + if (spendMoney.compareTo(total) > -1 && isOverdraftDine == 0) { + if (total.equals(new BigDecimal("0"))) { + resultModel.setMsg("不支持透支消费会员暂无可用余额"); + resultModel.setSuccess(false); + return resultModel; + } + resultModel.setMsg("不支持透支消费会员可用余额不足"); + resultModel.setSuccess(false); + return resultModel; + } + param.setConsumptionTotal(total); + param.setTopUpBalance(spendMoney);//直接将实付金额传入,如果余额不足将更新为负值 + param.setRewardBalance(model.getRewardBalance()); + resultModel.setSpendMoney(spendMoney); + //余额充足 + if (spendMoney.compareTo(total) <= 0) { + resultModel.setPriceSpread(new BigDecimal("0")); + } else {//余额不足, + BigDecimal priceSpread = spendMoney.subtract(total); + resultModel.setPriceSpread(priceSpread); + } + //进行扣费操作 + boolean resultStatus = baseMapper.memberSpend(param) == 1; + if(!resultStatus){ + resultModel.setMsg("余额更新失败"); + resultModel.setSuccess(false); + return resultModel; + } + + //需求点1:支持单人多次刷人脸,但在第二次刷卡的时候 ,提示是否继续扣费(需点一下确认)。 + //redis保存用户本餐次扣款(第一次扣款模式下扣费节点会根据,根据用户ID+日期+餐次为key,保存用户已扣费标记) + String key = query.getId() + "_" + DateUtil.today() + "_" + stBbzxOrderInfo.getDinnerType(); + redisUtil.hset(MapUtil.USER_ORDER_FLAG, key,stBbzxOrderInfo.getOrderNo(),6 * 60 * 60); + + resultModel.setSuccess(resultStatus); + resultModel.setPreferentialPrice(rewardDeduction); + resultModel.setSpendMoney(spendMoney); + StMiniMineBalanceVo balanceInfo = baseMapper.getBalanceInfo(query.getId()); + resultModel.setBalance(new BigDecimal(balanceInfo.getBalance())); + + //更新订单状态和最终支付金额 + stBbzxOrderInfo.setStatus(OrderStatus.ORDER_STATUS_HAS_CHECK_OUT.getCode()); + stBbzxOrderInfo.setOrderPrice(totalDeduction); + stBbzxOrderInfo.setCustomerPrice(spendMoney); + stBbzxOrderInfo.setPreferentialPrice(rewardDeduction); + stBbzxOrderInfo.setPayMemberId(query.getId()); + iStBbzxOrderInfoService.updateById(stBbzxOrderInfo); + + LambdaQueryWrapper lambdaQueryWrapper1 = new LambdaQueryWrapper(); + lambdaQueryWrapper1.eq(StBbzxOrderInfoPay::getOrderNo,query.getOrderNo()); + StBbzxOrderInfoPay stBbzxOrderInfoPay = iStBbzxOrderInfoPayService.getOne(lambdaQueryWrapper1); + if(null == stBbzxOrderInfoPay){ + stBbzxOrderInfoPay = new StBbzxOrderInfoPay(); + stBbzxOrderInfoPay.setOrderId(stBbzxOrderInfo.getId()); + stBbzxOrderInfoPay.setOrderNo(stBbzxOrderInfo.getOrderNo()); + } + stBbzxOrderInfoPay.setStatus(1); + stBbzxOrderInfoPay.setPayType(4); + stBbzxOrderInfoPay.setPayDiscount(rewardDeduction); + stBbzxOrderInfoPay.setPayIncome(spendMoney); + stBbzxOrderInfoPay.setPayPrice(spendMoney); + stBbzxOrderInfoPay.setUserId(query.getId()); + iStBbzxOrderInfoPayService.saveOrUpdate(stBbzxOrderInfoPay); + //消费记录数据添加 + StClientMemberTransactionRecord mtr = new StClientMemberTransactionRecord(); + mtr.setMemberId(stBbzxOrderInfo.getMemberId()); + mtr.setPayMemberId(query.getId()); + mtr.setSerialNo(RandomUtil.randomNumbers(19)); + mtr.setOrderNo(query.getOrderNo()); + mtr.setStatus(MemberTransactionTypeEnum.EXPENDITURE.getCode()); + mtr.setTransactionAmount(spendMoney.subtract(resultModel.getPriceSpread())); + mtr.setPaymentMethod(MemberEnum.PAYMENT_METHOD_BY_CARD.getCode()); + mtr.setStoredBalance(param.getTopUpBalance()); + mtr.setRewardBalance(param.getRewardBalance()); + mtr.setIntegralBalance(integral); + //可用余额 + mtr.setAvailableBalance( + model.getTopUpBalance() + .add(model.getRewardBalance()) + .subtract(param.getTopUpBalance()) + .subtract(param.getRewardBalance()) + ); + mtr.setTransactionAmount(param.getTopUpBalance()); + mtr.setPayTime(new Date()); + if (null != stBbzxOrderInfo.getOrderDate()) { + mtr.setCreateTime(stBbzxOrderInfo.getOrderDate()); + } + mtr.setUpdateStatus(0); + mtr.setRemark(query.getRemark()); + mtr.setCreateBy("0"); + mtr.setCanteenId(canteenId); + iStClientMemberTransactionRecordService.save(mtr); + return resultModel; + } + + + /** + * 人脸扣费闸机离线扣费方法(只考虑透支消费版本) + * StClientMemberSpendDTO 支付信息 + * @return StClientMemberSpendVO + * @throws Exception + */ + @Override + @Transactional(rollbackFor = Exception.class) + public StClientMemberSpendVO memberSpendOffline(StClientMemberSpendDTO query,StBbzxOrderInfo stBbzxOrderInfo) { + StClientMemberSpendVO resultModel = new StClientMemberSpendVO(); + String canteenId = null; + if(StringUtils.isNotBlank(query.getCanteenId())){ + canteenId = query.getCanteenId(); + } else { + canteenId = stBbzxOrderInfo.getCanteenId(); + } + //用户钱包数据 和本次消费金额 + StMiniMineBalanceVo miniMineBalanceVo = query.getMiniMineBalanceVo(); + //赠送积分 + Integer integral = 0; + //用户扣费补贴金额 + BigDecimal rewardDeduction = BigDecimal.ZERO; + //本次消费金额 + BigDecimal spendMoney = miniMineBalanceVo.getAmountThis(); + //用户余额 + BigDecimal total = new BigDecimal(miniMineBalanceVo.getBalance()); + + StClientMemberSpendMapperDTO param = new StClientMemberSpendMapperDTO(); + param.setId(miniMineBalanceVo.getId()); + param.setConsumptionTotal(total); + param.setTopUpBalance(spendMoney);//直接将实付金额传入,如果余额不足将更新为负值 + param.setRewardBalance(BigDecimal.ZERO); + resultModel.setSpendMoney(spendMoney); + //余额充足 + if (spendMoney.compareTo(total) <= 0) { + resultModel.setPriceSpread(new BigDecimal("0")); + } else {//余额不足, + BigDecimal priceSpread = spendMoney.subtract(total); + resultModel.setPriceSpread(priceSpread); + } + //进行扣费操作 + boolean resultStatus = baseMapper.memberSpend(param) == 1; + if(!resultStatus){ + resultModel.setMsg("余额更新失败"); + resultModel.setSuccess(false); + return resultModel; + } + resultModel.setSuccess(resultStatus); + resultModel.setPreferentialPrice(rewardDeduction); + resultModel.setSpendMoney(spendMoney); + + //更新订单状态和最终支付金额 + stBbzxOrderInfo.setStatus(OrderStatus.ORDER_STATUS_HAS_CHECK_OUT.getCode()); + stBbzxOrderInfo.setOrderPrice(spendMoney); + stBbzxOrderInfo.setCustomerPrice(spendMoney); + stBbzxOrderInfo.setPreferentialPrice(rewardDeduction); + stBbzxOrderInfo.setPayMemberId(query.getId()); + stBbzxOrderInfo.setPayOrderDate(new Date()); + iStBbzxOrderInfoService.updateById(stBbzxOrderInfo); + + StBbzxOrderInfoPay stBbzxOrderInfoPay = new StBbzxOrderInfoPay(); + stBbzxOrderInfoPay.setOrderId(stBbzxOrderInfo.getId()); + stBbzxOrderInfoPay.setOrderNo(stBbzxOrderInfo.getOrderNo()); + stBbzxOrderInfoPay.setStatus(1); + stBbzxOrderInfoPay.setPayType(4); + stBbzxOrderInfoPay.setPayDiscount(rewardDeduction); + stBbzxOrderInfoPay.setPayIncome(spendMoney); + stBbzxOrderInfoPay.setPayPrice(spendMoney); + stBbzxOrderInfoPay.setUserId(query.getId()); + iStBbzxOrderInfoPayService.save(stBbzxOrderInfoPay); + //消费记录数据添加 + StClientMemberTransactionRecord mtr = new StClientMemberTransactionRecord(); + mtr.setMemberId(stBbzxOrderInfo.getMemberId()); + mtr.setPayMemberId(query.getId()); + mtr.setSerialNo(RandomUtil.randomNumbers(19)); + mtr.setOrderNo(query.getOrderNo()); + mtr.setStatus(MemberTransactionTypeEnum.EXPENDITURE.getCode()); + mtr.setTransactionAmount(spendMoney.subtract(resultModel.getPriceSpread())); + mtr.setPaymentMethod(MemberEnum.PAYMENT_METHOD_BY_CARD.getCode()); + mtr.setStoredBalance(param.getTopUpBalance()); + mtr.setRewardBalance(param.getRewardBalance()); + mtr.setIntegralBalance(integral); + //可用余额 + mtr.setAvailableBalance( + new BigDecimal(miniMineBalanceVo.getBalance()) + .add(BigDecimal.ZERO) + .subtract(param.getTopUpBalance()) + .subtract(param.getRewardBalance()) + ); + mtr.setTransactionAmount(param.getTopUpBalance()); + mtr.setPayTime(new Date()); + if (null != stBbzxOrderInfo.getOrderDate()) { + mtr.setCreateTime(stBbzxOrderInfo.getOrderDate()); + } + mtr.setUpdateStatus(0); + mtr.setRemark(query.getRemark()); + mtr.setCreateBy("0"); + mtr.setCanteenId(canteenId); + iStClientMemberTransactionRecordService.save(mtr); + return resultModel; + } + + + /** + * 补偿人脸扣费闸机离线扣费方法(只考虑透支消费版本) + * StClientMemberSpendDTO 支付信息 + * @return StClientMemberSpendVO + * @throws Exception + */ + @Override + @Transactional(rollbackFor = Exception.class) + public List memberSpendOfflineCompensate(List orderIds) { + + List stClientMemberSpendVOS = new ArrayList<>(); + List stBbzxOrderInfoList = iStBbzxOrderInfoService.listByIds(orderIds); + + if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(stBbzxOrderInfoList)){ + + stBbzxOrderInfoList.forEach(stBbzxOrderInfo->{ + StClientMemberSpendVO resultModel = new StClientMemberSpendVO(); + String restId = stBbzxOrderInfo.getCanteenId(); + //扣费规则(1:固定扣费,2:固定+补贴扣费,3:按量扣费) + StClientMemberLevelDeductRuleVo levelDeductRuleVo = iStClientMemberLevelDeductRuleService.getDeductRuleByCanteenId(restId); + if(null == levelDeductRuleVo){ + resultModel.setMsg("食堂扣费规则未设置"); + resultModel.setSuccess(false); + stClientMemberSpendVOS.add(resultModel); + return; + } + + String timesName = ""; + Date date = stBbzxOrderInfo.getBusinessDate(); + SlBaseDinnerTime dinnerTime = slBaseDinnerTimeService.getCanteenDinnerType(restId, date); + if(null != dinnerTime){ + timesName = dinnerTime.getDinnerType(); + } else { + timesName = MealGroupingUtil.getDefaultMealType(date); + } + if(StringUtils.isBlank(timesName) || "未知餐次".equals(timesName)){ + resultModel.setMsg("食堂餐次设置异常:"+timesName); + resultModel.setSuccess(false); + stClientMemberSpendVOS.add(resultModel); + return; + } + Result userBalanceResult = this.getUserBalance(stBbzxOrderInfo.getPayMemberId()); + StMiniMineBalanceVo stMiniMineBalanceVo = new StMiniMineBalanceVo(); + if(userBalanceResult.isSuccess()){ + stMiniMineBalanceVo = userBalanceResult.getResult(); + } + //实际扣减额 + //计算本次扣费金额(一次扣费模式返回实际扣费金额,按量扣费直接返回0) + StClientMemberSpendDataVO stClientMemberSpendDataVO = this.fixedFee(levelDeductRuleVo,timesName,BigDecimal.ZERO,BigDecimal.ZERO,BigDecimal.ZERO,stMiniMineBalanceVo.getUserInnerType()); + BigDecimal spendMoney = stClientMemberSpendDataVO.getSpendMoney(); + stMiniMineBalanceVo.setAmountThis(spendMoney); + StClientMemberSpendDTO stClientMemberSpendDTO = new StClientMemberSpendDTO(); + stClientMemberSpendDTO.setCanteenId(stBbzxOrderInfo.getCanteenId()); + stClientMemberSpendDTO.setOrderNo(stBbzxOrderInfo.getOrderNo()); + stClientMemberSpendDTO.setId(stMiniMineBalanceVo.getMemberId());//钱包用户id + stClientMemberSpendDTO.setMiniMineBalanceVo(stMiniMineBalanceVo);//钱包用户 + + resultModel.setOrderId(stBbzxOrderInfo.getId()); + String canteenId = null; + if(StringUtils.isNotBlank(stClientMemberSpendDTO.getCanteenId())){ + canteenId = stClientMemberSpendDTO.getCanteenId(); + } else { + canteenId = stBbzxOrderInfo.getCanteenId(); + } + //用户钱包数据 和本次消费金额 + StMiniMineBalanceVo miniMineBalanceVo = stClientMemberSpendDTO.getMiniMineBalanceVo(); + //赠送积分 + Integer integral = 0; + //用户扣费补贴金额 + BigDecimal rewardDeduction = BigDecimal.ZERO; + //用户余额 + BigDecimal total = new BigDecimal(miniMineBalanceVo.getBalance()); + + StClientMemberSpendMapperDTO param = new StClientMemberSpendMapperDTO(); + param.setId(miniMineBalanceVo.getId()); + param.setConsumptionTotal(total); + param.setTopUpBalance(spendMoney);//直接将实付金额传入,如果余额不足将更新为负值 + param.setRewardBalance(BigDecimal.ZERO); + resultModel.setSpendMoney(spendMoney); + //余额充足 + if (spendMoney.compareTo(total) <= 0) { + resultModel.setPriceSpread(new BigDecimal("0")); + } else {//余额不足, + BigDecimal priceSpread = spendMoney.subtract(total); + resultModel.setPriceSpread(priceSpread); + } + //进行扣费操作 + boolean resultStatus = baseMapper.memberSpend(param) == 1; + if(!resultStatus){ + resultModel.setMsg("余额更新失败"); + resultModel.setSuccess(false); + stClientMemberSpendVOS.add(resultModel); + return; + } + resultModel.setSuccess(resultStatus); + resultModel.setPreferentialPrice(rewardDeduction); + resultModel.setSpendMoney(spendMoney); + + //更新订单状态和最终支付金额 + stBbzxOrderInfo.setStatus(OrderStatus.ORDER_STATUS_HAS_CHECK_OUT.getCode()); + stBbzxOrderInfo.setOrderPrice(spendMoney); + stBbzxOrderInfo.setCustomerPrice(spendMoney); + stBbzxOrderInfo.setPreferentialPrice(rewardDeduction); + stBbzxOrderInfo.setPayMemberId(stClientMemberSpendDTO.getId()); + stBbzxOrderInfo.setDinnerType(timesName); + iStBbzxOrderInfoService.updateById(stBbzxOrderInfo); + + LambdaQueryWrapper stBbzxOrderInfoPayLambdaQueryWrapper = new LambdaQueryWrapper<>(); + stBbzxOrderInfoPayLambdaQueryWrapper.eq(StBbzxOrderInfoPay::getOrderId, stBbzxOrderInfo.getId()); + List list = iStBbzxOrderInfoPayService.list(stBbzxOrderInfoPayLambdaQueryWrapper); + if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(list)){ + list.forEach(pay->{ + pay.setOrderId(stBbzxOrderInfo.getId()); + pay.setOrderNo(stBbzxOrderInfo.getOrderNo()); + pay.setStatus(1); + pay.setPayType(4); + pay.setPayDiscount(rewardDeduction); + pay.setPayIncome(spendMoney); + pay.setPayPrice(spendMoney); + pay.setUserId(stClientMemberSpendDTO.getId()); + iStBbzxOrderInfoPayService.updateById(pay); + }); + } else { + StBbzxOrderInfoPay stBbzxOrderInfoPay = new StBbzxOrderInfoPay(); + stBbzxOrderInfoPay.setOrderId(stBbzxOrderInfo.getId()); + stBbzxOrderInfoPay.setOrderNo(stBbzxOrderInfo.getOrderNo()); + stBbzxOrderInfoPay.setStatus(1); + stBbzxOrderInfoPay.setPayType(4); + stBbzxOrderInfoPay.setPayDiscount(rewardDeduction); + stBbzxOrderInfoPay.setPayIncome(spendMoney); + stBbzxOrderInfoPay.setPayPrice(spendMoney); + stBbzxOrderInfoPay.setUserId(stClientMemberSpendDTO.getId()); + iStBbzxOrderInfoPayService.save(stBbzxOrderInfoPay); + } + + LambdaQueryWrapper transactionRecordLambdaQueryWrapper = new LambdaQueryWrapper<>(); + transactionRecordLambdaQueryWrapper.eq(StClientMemberTransactionRecord::getOrderNo, stBbzxOrderInfo.getOrderNo()); + List records = iStClientMemberTransactionRecordService.list(transactionRecordLambdaQueryWrapper); + if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(records)){ + records.forEach(transactionRecord->{ + transactionRecord.setMemberId(stBbzxOrderInfo.getMemberId()); + transactionRecord.setPayMemberId(stClientMemberSpendDTO.getId()); + if(StringUtils.isBlank(transactionRecord.getSerialNo())){ + transactionRecord.setSerialNo(RandomUtil.randomNumbers(19)); + } + transactionRecord.setStatus(MemberTransactionTypeEnum.EXPENDITURE.getCode()); + transactionRecord.setTransactionAmount(spendMoney.subtract(resultModel.getPriceSpread())); + transactionRecord.setPaymentMethod(MemberEnum.PAYMENT_METHOD_BY_CARD.getCode()); + transactionRecord.setStoredBalance(param.getTopUpBalance()); + transactionRecord.setRewardBalance(param.getRewardBalance()); + transactionRecord.setIntegralBalance(integral); + //可用余额 + transactionRecord.setAvailableBalance( + new BigDecimal(miniMineBalanceVo.getBalance()) + .add(BigDecimal.ZERO) + .subtract(param.getTopUpBalance()) + .subtract(param.getRewardBalance()) + ); + transactionRecord.setTransactionAmount(param.getTopUpBalance()); + iStClientMemberTransactionRecordService.updateById(transactionRecord); + }); + } else { + //消费记录数据添加 + StClientMemberTransactionRecord mtr = new StClientMemberTransactionRecord(); + mtr.setMemberId(stBbzxOrderInfo.getMemberId()); + mtr.setPayMemberId(stClientMemberSpendDTO.getId()); + mtr.setSerialNo(RandomUtil.randomNumbers(19)); + mtr.setOrderNo(stClientMemberSpendDTO.getOrderNo()); + mtr.setStatus(MemberTransactionTypeEnum.EXPENDITURE.getCode()); + mtr.setTransactionAmount(spendMoney.subtract(resultModel.getPriceSpread())); + mtr.setPaymentMethod(MemberEnum.PAYMENT_METHOD_BY_CARD.getCode()); + mtr.setStoredBalance(param.getTopUpBalance()); + mtr.setRewardBalance(param.getRewardBalance()); + mtr.setIntegralBalance(integral); + //可用余额 + mtr.setAvailableBalance( + new BigDecimal(miniMineBalanceVo.getBalance()) + .add(BigDecimal.ZERO) + .subtract(param.getTopUpBalance()) + .subtract(param.getRewardBalance()) + ); + mtr.setTransactionAmount(param.getTopUpBalance()); + mtr.setPayTime(new Date()); + if (null != stBbzxOrderInfo.getOrderDate()) { + mtr.setCreateTime(stBbzxOrderInfo.getOrderDate()); + } + mtr.setUpdateStatus(0); + mtr.setRemark(stClientMemberSpendDTO.getRemark()); + mtr.setCreateBy("0"); + mtr.setCanteenId(canteenId); + iStClientMemberTransactionRecordService.save(mtr); + } + stClientMemberSpendVOS.add(resultModel); + }); + } + return stClientMemberSpendVOS; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Result changeBalanceAndIntegral(StClientMemberChangeBalanceDTO query) { + int flag = 0; + if (query.getId() == null || query.getStatus() == null) { + return Result.error("参数异常"); + } + if (query.getRewardBalance() != null && query.getRewardBalance().compareTo(BigDecimal.ZERO) < 0) { + return Result.error("赠送余额不能为空"); + } + if (query.getTopUpBalance() != null && query.getTopUpBalance().compareTo(BigDecimal.ZERO) < 0) { + return Result.error("充值余额不能为空"); + } + if (query.getIntegralBalance() != null && query.getIntegralBalance() < 0) { + return Result.error("积分不能为空"); + } + //积分余额调整 + flag = baseMapper.changeBalanceAndIntegral(query); + //操作成功 + if (flag == 1) { + int status = 0; + //积分 + if (query.getIntegralBalance() != null) { + if (query.getStatus() == 1) { + //补录 + status = MemberTransactionTypeEnum.INTEGRAL_ADD.getCode(); + } else { + status = MemberTransactionTypeEnum.INTEGRAL_SUBTRACT.getCode(); + } + } else { + if (query.getStatus() == 1) { + //补录 + status = MemberTransactionTypeEnum.BALANCE_ADD.getCode(); + } else { + status = MemberTransactionTypeEnum.BALANCE_SUBTRACT.getCode(); + } + } + StClientMemberTransactionRecord model = new StClientMemberTransactionRecord(); + StClientMemberBalance swClientMemberBalance = baseMapper.selectById(query.getId()); + if (swClientMemberBalance != null) { + model.setCanteenId(query.getCanteenId()); + model.setMemberId(swClientMemberBalance.getMemberId()); + model.setMemberId(query.getCanteenId()); + model.setSerialNo(StMemberUtil.getSerialNo()); + model.setStatus(status); + if (query.getIntegralBalance() != null) { + model.setIntegralBalance(query.getIntegralBalance()); + } + BigDecimal v=new BigDecimal("0"); + if (query.getTopUpBalance() != null) { + model.setStoredBalance(query.getTopUpBalance()); + v.add(query.getTopUpBalance()); + } + if (query.getRewardBalance() != null) { + model.setRewardBalance(query.getRewardBalance()); + v.add(query.getRewardBalance()); + } + //操作金额 + model.setTransactionAmount(v); + + model.setAvailableBalance(swClientMemberBalance.getTopUpBalance().add(swClientMemberBalance.getRewardBalance())); + model.setPayTime(new Date()); + model.setUpdateStatus(0); + iStClientMemberTransactionRecordService.save(model); + } + } + return Result.OK("操作成功!"); + } + + @Override + public Result getUserBalance(String userId){ + SysUserSub sysUserSub = baseMapper.getOneByUserId(userId); + if(sysUserSub == null){ + return Result.error("用户不存在"); + } + //2025-05-06下午,余+徐:扣费时人员和钱包状态异常正常扣费保存订单记录等 +// if(sysUserSub.getStatus() != 1){ +// return Result.error("用户已作废"); +// } + StMiniMineBalanceVo balanceByUserId = new StMiniMineBalanceVo();//2025-04-18 正式员工视为"内部员工"计费,否则视为"外部员工"计费 + //判断是否是主用户 + if(StringUtils.isNotEmpty(sysUserSub.getMainUserId())){ + //查询主用户信息 + balanceByUserId = baseMapper.getBalanceByUserId(sysUserSub.getMainUserId()); + }else { + //查询主用户信息 + balanceByUserId = baseMapper.getBalanceByUserId(sysUserSub.getId()); + } + if(balanceByUserId != null){//设置当前用户信息 + balanceByUserId.setStatus(sysUserSub.getStatus()); + balanceByUserId.setDelFlag(sysUserSub.getDelFlag()); + balanceByUserId.setRealname(sysUserSub.getRealname()); + balanceByUserId.setWorkNo(sysUserSub.getWorkNo()); + balanceByUserId.setUserType(sysUserSub.getUserType()); + //2025-05-06下午,余+徐:扣费时人员和钱包状态异常正常扣费保存订单记录等 +// if(balanceByUserId.getUserStatus() != 1){ +// return Result.error("用户已作废"); +// } + if(StringUtils.isEmpty(balanceByUserId.getBalance())){ + balanceByUserId.setBalance("0"); + } + return Result.OK(balanceByUserId); + } + return Result.error("用户不存在"); + } + + /** + * 根据卡号查询用户钱包 + * @param cardCode + * @return + */ + @Override + public Result getUserBalanceByCardCode(String cardCode){ + SysUserSub sysUserSub = baseMapper.getOneByCardCode(cardCode); + if(sysUserSub == null){ + return Result.error("卡号未绑定或已解绑"); + } + //2025-05-06下午,余+徐:扣费时人员和钱包状态异常正常扣费保存订单记录等 +// if(sysUserSub.getStatus() != 1){ +// return Result.error("用户已作废"); +// } + StMiniMineBalanceVo balanceByUserId = new StMiniMineBalanceVo();//2025-04-18 正式员工视为"内部员工"计费,否则视为"外部员工"计费 + //判断是否是主用户 + if(StringUtils.isNotEmpty(sysUserSub.getMainUserId())){ + //查询主用户信息 + balanceByUserId = baseMapper.getBalanceByUserId(sysUserSub.getMainUserId()); + }else { + //查询主用户信息 + balanceByUserId = baseMapper.getBalanceByUserId(sysUserSub.getId()); + } + if(balanceByUserId != null){//设置当前用户信息 + balanceByUserId.setStatus(sysUserSub.getStatus()); + balanceByUserId.setDelFlag(sysUserSub.getDelFlag()); + balanceByUserId.setRealname(sysUserSub.getRealname()); + balanceByUserId.setWorkNo(sysUserSub.getWorkNo()); + balanceByUserId.setUserType(sysUserSub.getUserType()); + balanceByUserId.setCardCode(sysUserSub.getCardCode()); + balanceByUserId.setCardCodeStatus(sysUserSub.getCardCodeStatus()); + //2025-05-06下午,余+徐:扣费时人员和钱包状态异常正常扣费保存订单记录等 +// if(balanceByUserId.getUserStatus() != 1){ +// return Result.error("用户已作废"); +// } + if(StringUtils.isEmpty(balanceByUserId.getBalance())){ + balanceByUserId.setBalance("0"); + } + return Result.OK(balanceByUserId); + } + return Result.error("卡号未绑定或已解绑"); + } + + private Map> querySerialNoStatus(List snList) { + // 提取所有待查询的serialNo + List sns = snList.stream() + .map(StClientMemberTopUpDTO::getSerialNo) + .collect(Collectors.toList()); + + // 如果没有需要查询的serialNo,直接返回空分组 + if (sns.isEmpty()) { + return Collections.emptyMap(); + } + + // 查询数据库中存在的serialNo + List existingRecords = stClientMemberTransactionRecordMapper.selectList( + new LambdaQueryWrapper() + .in(StClientMemberTransactionRecord::getSerialNo, sns) + .select(StClientMemberTransactionRecord::getSerialNo) + ); + + // 提取数据库中存在的serialNo到Set,用于快速判断 + Set existingSerials = existingRecords.stream() + .map(StClientMemberTransactionRecord::getSerialNo) + .collect(Collectors.toSet()); + + // 二分分组:true=已存在,false=不存在 + return snList.stream() + .collect(Collectors.partitioningBy( + dto -> existingSerials.contains(dto.getSerialNo()) + )); + } + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StClientMemberLevelDeductRuleServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StClientMemberLevelDeductRuleServiceImpl.java new file mode 100644 index 0000000..c22232a --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StClientMemberLevelDeductRuleServiceImpl.java @@ -0,0 +1,571 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.service.impl; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.base.service.impl.JeecgServiceImpl; +import org.jeecg.common.util.DateUtils; +import org.jeecg.payment.entity.StBbzxOrderInfo; +import org.jeecg.payment.entity.StClientMemberLevelDeductRule; +import org.jeecg.payment.enums.DeductionTypeEnum; +import org.jeecg.payment.mapper.StClientMemberLevelDeductRuleMapper; +import org.jeecg.payment.service.IStBbzxOrderInfoService; +import org.jeecg.payment.service.IStClientMemberBalanceService; +import org.jeecg.payment.service.IStClientMemberLevelDeductRuleService; +import org.jeecg.payment.utils.MealGroupingUtil; +import org.jeecg.payment.vo.*; +import org.jeecg.restaurant.entity.SlBaseDinnerTime; +import org.jeecg.restaurant.entity.SlBaseRestaurant; +import org.jeecg.restaurant.mapper.SlBaseRestaurantMapper; +import org.jeecg.restaurant.service.ISlBaseDinnerTimeService; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.RequestBody; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + + +/** + * 扣费规则服务实现类 + */ +@Service +@Slf4j +public class StClientMemberLevelDeductRuleServiceImpl extends JeecgServiceImpl implements IStClientMemberLevelDeductRuleService { + + + @Resource + private SlBaseRestaurantMapper slBaseRestaurantMapper; + @Resource + private IStBbzxOrderInfoService iStBbzxOrderInfoService; + @Resource + private IStClientMemberBalanceService iStClientMemberBalanceService; + + @Autowired + private ISlBaseDinnerTimeService stBaseDinnerTimeService; + + + @Override + public IPage selectStClientMemberLevelDemotionTimePage(StClientMemberLevelDemotionTimeVO query){ + Page page = new Page<>(query.getPageNo(), query.getPageSize()); + List pageList = baseMapper.selectStClientMemberLevelDeductRulePage(page,query); + SlBaseRestaurant slBaseRestaurant = slBaseRestaurantMapper.selectById(query.getCanteenId()); + pageList.forEach(p->{ + p.setIsExternalDine(null !=slBaseRestaurant?slBaseRestaurant.getIsExternalDine():0); + }); + return page.setRecords(pageList); + + } + + @Override + public Boolean saveOrUpdateService(StClientMemberLevelDeductRuleVo vo) { + //规则 + StClientMemberLevelDeductRule demotionTime = new StClientMemberLevelDeductRule(); + demotionTime.setId(vo.getId()); + demotionTime.setRuleName(DeductionTypeEnum.getName(vo.getDeductionType())); + StMemberDeductRuleVO resetVO = vo.getStMemberDeductRuleVO(); + //2:固定+补贴扣费 + if(vo.getDeductionType().equals(2)){ + //=============================早餐================================= + StMemberDeductUserTypeRuleVO breakfast = resetVO.getBreakfast(); + StMemberDeductTypeOfAuditorRuleVO externalBreakfast = breakfast.getExternal(); + this.buildMemberDeductTypeOfAuditorRule(externalBreakfast); + breakfast.setExternal(externalBreakfast); + StMemberDeductTypeOfAuditorRuleVO innerBreakfast = breakfast.getInner(); + this.buildMemberDeductTypeOfAuditorRule(innerBreakfast); + breakfast.setInner(innerBreakfast); + resetVO.setBreakfast(breakfast); + //=============================午餐================================= + StMemberDeductUserTypeRuleVO lunch = resetVO.getLunch(); + StMemberDeductTypeOfAuditorRuleVO externalLunch = lunch.getExternal(); + this.buildMemberDeductTypeOfAuditorRule(externalLunch); + lunch.setExternal(externalLunch); + StMemberDeductTypeOfAuditorRuleVO innerLunch = lunch.getInner(); + this.buildMemberDeductTypeOfAuditorRule(innerLunch); + lunch.setInner(innerLunch); + resetVO.setLunch(lunch); + //=============================晚餐================================= + StMemberDeductUserTypeRuleVO dinner = resetVO.getDinner(); + StMemberDeductTypeOfAuditorRuleVO externalDinner= dinner.getExternal(); + this.buildMemberDeductTypeOfAuditorRule(externalDinner); + dinner.setExternal(externalDinner); + StMemberDeductTypeOfAuditorRuleVO innerDinner = dinner.getInner(); + this.buildMemberDeductTypeOfAuditorRule(innerDinner); + dinner.setInner(innerDinner); + resetVO.setDinner(dinner); + } + + String reStr = JSON.toJSONString(resetVO); + demotionTime.setDeductionRule(reStr); + demotionTime.setDeductionType(vo.getDeductionType()); + demotionTime.setCanteenId(vo.getCanteenId()); + demotionTime.setIsSync(0); + return this.saveOrUpdate(demotionTime); + } + + /** + * 扣费类型:固定+补贴扣费,选择按照比例扣减,计算赠送金额和总金额 + * @param typeOfAuditorRule + */ + private void buildMemberDeductTypeOfAuditorRule(StMemberDeductTypeOfAuditorRuleVO typeOfAuditorRule){ + //选择按照比例扣减 + if(typeOfAuditorRule.getRatioDeduction() !=null){ + //四舍五入到两位小数 + BigDecimal rewardDeduction = BigDecimal.valueOf(typeOfAuditorRule.getFixedDeduction()) + .multiply(BigDecimal.valueOf(typeOfAuditorRule.getRatioDeduction() / 100)).setScale(2, RoundingMode.HALF_UP); + typeOfAuditorRule.setRewardDeduction(rewardDeduction.floatValue()); + typeOfAuditorRule.setTotalDeduction(rewardDeduction.add(BigDecimal.valueOf(typeOfAuditorRule.getFixedDeduction())).floatValue()); + } else { + //固定扣除 + typeOfAuditorRule.setTotalDeduction(BigDecimal.valueOf(typeOfAuditorRule.getFixedDeduction()) + .add(BigDecimal.valueOf(typeOfAuditorRule.getRewardDeduction())).setScale(2, RoundingMode.HALF_UP).floatValue()); + } + } + + @Override + public StClientMemberLevelDeductRuleVo getOneService(StClientMemberLevelDeductRuleVo stClientMemberLevelDeductRuleVo) { + StClientMemberLevelDeductRuleVo vo = new StClientMemberLevelDeductRuleVo(); + StClientMemberLevelDeductRule memberLevel = null; + if(StringUtils.isNotBlank(stClientMemberLevelDeductRuleVo.getId())){ + memberLevel = getOne(new LambdaQueryWrapper() + .eq(StClientMemberLevelDeductRule::getId, stClientMemberLevelDeductRuleVo.getId())); + } + + if(StringUtils.isNotBlank(stClientMemberLevelDeductRuleVo.getCanteenId())){ + memberLevel = getOne(new LambdaQueryWrapper() + .eq(StClientMemberLevelDeductRule::getCanteenId, stClientMemberLevelDeductRuleVo.getCanteenId()) + .eq(StClientMemberLevelDeductRule::getIsEnable,1)); + } + if (memberLevel == null) { + return null; + } + BeanUtils.copyProperties(memberLevel, vo); + if (vo.getDeductionType() == null) { + vo.setDeductionType(1); + } + /** + * 规则 + */ + String deductionRule = memberLevel.getDeductionRule(); + StMemberDeductRuleVO stMemberDeductRuleVO; + if (!org.springframework.util.StringUtils.isEmpty(deductionRule)) { + stMemberDeductRuleVO = JSON.parseObject(deductionRule, StMemberDeductRuleVO.class); + } else { + stMemberDeductRuleVO = new StMemberDeductRuleVO(); + } + vo.setStMemberDeductRuleVO(stMemberDeductRuleVO); + return vo; + } + + + + /** + * 根据食堂ID获取商户扣费规则 + * @param canteenId + * @return + */ + @Override + public StClientMemberLevelDeductRuleVo getDeductRuleByCanteenId(String canteenId) { +// Object deductRule = redisUtil.hget(MapUtil.DeductRule, canteenId); +// if (ObjectUtils.isNotEmpty(deductRule)){ +// return (StClientMemberLevelDeductRuleVo)deductRule; +//// return JSONObject.parseObject(deductRule, StClientMemberLevelDeductRuleVo.class); +// } + StClientMemberLevelDeductRuleVo vo = new StClientMemberLevelDeductRuleVo(); + StClientMemberLevelDeductRule memberLevel = null; + if(StringUtils.isNotBlank(canteenId)){ + memberLevel = getOne(new LambdaQueryWrapper() + .eq(StClientMemberLevelDeductRule::getCanteenId, canteenId) + .eq(StClientMemberLevelDeductRule::getIsEnable,1)); + } + if (memberLevel == null) { + return null; + } + vo = bulidStClientMemberLevelDeductRule(vo,memberLevel); +// redisUtil.hset(MapUtil.DeductRule, canteenId, vo); + return vo; + } + + + /** + * 组装扣费规则vo + * @param vo + * @param memberLevel + * @return + */ + @Override + public StClientMemberLevelDeductRuleVo bulidStClientMemberLevelDeductRule(StClientMemberLevelDeductRuleVo vo, StClientMemberLevelDeductRule memberLevel){ + BeanUtils.copyProperties(memberLevel, vo); + if (vo.getDeductionType() == null) { + vo.setDeductionType(1); + } + /** + * 规则 + */ + String deductionRule = memberLevel.getDeductionRule(); + StMemberDeductRuleVO stMemberDeductRuleVO; + if (!org.springframework.util.StringUtils.isEmpty(deductionRule)) { + stMemberDeductRuleVO = JSON.parseObject(deductionRule, StMemberDeductRuleVO.class); + } else { + stMemberDeductRuleVO = new StMemberDeductRuleVO(); + } + vo.setStMemberDeductRuleVO(stMemberDeductRuleVO); + return vo; + } + + + @Override + public Result bindUserOfflineCardCode(@RequestBody StRfidoffLineVo stRfidoffLineVo){ + String deviceCode = stRfidoffLineVo.getDeviceCode(); + String restId = stRfidoffLineVo.getRestId(); + List voListAll = stRfidoffLineVo.getVoList(); + if(CollectionUtils.isEmpty(voListAll)){ + return Result.error("扣费信息不能为空"); + } + //扣费规则(扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费,4:自定义金额)) + StClientMemberLevelDeductRuleVo levelDeductRuleVo = this.getDeductRuleByCanteenId(restId); + if(voListAll.size() > 1){//离线扣费 + //加异步处理 + CompletableFuture.runAsync(() -> { + unifiedDeductionInterface(voListAll,restId,deviceCode,levelDeductRuleVo,false); + }); + } else {//在线扣费 + return unifiedDeductionInterface(voListAll,restId,deviceCode,levelDeductRuleVo,true); + } + return Result.OK("扣费成功"); + } + + + /** + * 统一扣费方法 + * @param voListAll + * @param restId + * @param deviceCode + * @param levelDeductRuleVo + * @return + */ + private Result unifiedDeductionInterface(List voListAll, String restId, String deviceCode, + StClientMemberLevelDeductRuleVo levelDeductRuleVo, Boolean online) { + long start = System.currentTimeMillis(); + //声明在线扣费余额信息 + StClientMemberSpendVO stClientMemberSpendVO = new StClientMemberSpendVO(); + BigDecimal totalOnline = BigDecimal.ZERO; + BigDecimal spendMoneyOnline = BigDecimal.ZERO; + + // 存储错误信息 + String errorMessages = null; + // 将扣费信息按照餐次分组 + Map> stringListMap = groupByMeal(voListAll, restId); + StMiniMineBalanceVo stMiniMineBalanceVoOnline = new StMiniMineBalanceVo(); + + for (Map.Entry> entry : stringListMap.entrySet()) { + List voList = entry.getValue(); + + for (StRfidVo stRfidVo : voList) { // 改用普通for循环,方便控制流程 + stRfidVo.setDeviceCode(deviceCode); + stRfidVo.setRestId(restId); + if(!online){ + log.error("/payUser/offline/v3 离线人脸扣费v3,bindUser={}", JSONObject.toJSONString(stRfidVo)); + } else { + log.error("/payUser/online 在线人脸扣费,bindUser={}", JSONObject.toJSONString(stRfidVo)); + } + // 检查支付ID是否已存在 + if (StringUtils.isNotBlank(stRfidVo.getPayId())) { +// StBbzxOrderInfo stBbzxOrderInfo = iStBbzxOrderInfoService.getSwBbzxOrderInfoByPayId(stRfidVo.getPayId()); + StBbzxOrderInfo stBbzxOrderInfo = iStBbzxOrderInfoService.lambdaQuery() + .select(StBbzxOrderInfo::getOrderNo) + .eq(StBbzxOrderInfo::getPayId, stRfidVo.getPayId()) + .one(); + if (null != stBbzxOrderInfo) { + String errorMsg = "闸机记录的支付唯一id已经有支付信息, payId=" + stRfidVo.getPayId() + + ", 已存在的订单编号=" + stBbzxOrderInfo.getOrderNo(); + log.error(errorMsg); + errorMessages = "重复支付"; // 添加错误信息 + continue; // 跳过当前循环,处理下一个 + } + } + + // 当前餐次 + String timesName = entry.getKey(); + BigDecimal rewardDeduction = BigDecimal.ZERO; + BigDecimal totalDeduction = BigDecimal.ZERO; + Date dateTime = new Date(Long.parseLong(stRfidVo.getDateTime())); + + // 预算本次扣费金额 + BigDecimal spendMoneyBudgeted = BigDecimal.ZERO; + BigDecimal moneyCustom = stRfidVo.getSpendMoney(); + Integer deductionType; + + if (moneyCustom != null && moneyCustom.compareTo(BigDecimal.ZERO) > 0) { + // 自定义金额方式 + spendMoneyBudgeted = moneyCustom; + deductionType = 4; + } else { + deductionType = 1; + if (null == levelDeductRuleVo) { + String errorMsg = "食堂扣费规则未设置, restId=" + restId; + log.error(errorMsg); + errorMessages = "食堂扣费规则未设置"; + if(!online) {//离线 + CompletableFuture.runAsync(()-> iStBbzxOrderInfoService.buildOrderInfoNew(dateTime, stRfidVo.getRestId(), timesName, deviceCode, + stRfidVo.getUserId(), null, null, 1, levelDeductRuleVo, + errorMsg, stRfidVo)); + } + continue; + } + + // 扣费规则模式计算 + try { + StClientMemberSpendDataVO stClientMemberSpendDataVO = + iStClientMemberBalanceService.fixedFee(levelDeductRuleVo, timesName, totalDeduction, + spendMoneyBudgeted, rewardDeduction, 1); + spendMoneyBudgeted = stClientMemberSpendDataVO.getSpendMoney(); + } catch (Exception e) { + String errorMsg = "计算预算扣费金额失败, userId=" + stRfidVo.getUserId() + ", 餐次=" + timesName; + log.error(errorMsg, e); + errorMessages = "计算预算扣费金额失败"; + continue; + } + } + + // 获取用户钱包数据 + String userId = stRfidVo.getUserId(); + StMiniMineBalanceVo stMiniMineBalanceVo = new StMiniMineBalanceVo(); + + if (StringUtils.isNotBlank(userId)) { + // 人脸识别方式 + Result userBalanceResult = iStClientMemberBalanceService.getUserBalance(userId); + if (!userBalanceResult.isSuccess()) { + stRfidVo.setSpendMoney(spendMoneyBudgeted); + String errorMsg = "离线人脸扣费v3,根据userId=" + userId + "查询用户钱包数据失败: " + userBalanceResult.getMessage(); + log.error(errorMsg); + errorMessages = "根据人脸查询用户钱包数据失败: " + userBalanceResult.getMessage(); + if(!online){//离线 + CompletableFuture.runAsync(()->iStBbzxOrderInfoService.buildOrderInfoNew(dateTime, stRfidVo.getRestId(), timesName, deviceCode, + stRfidVo.getUserId(), null, null, deductionType, + levelDeductRuleVo, errorMsg, stRfidVo)); + } + continue; + } + stMiniMineBalanceVo = userBalanceResult.getResult(); + } else { + // 刷卡方式 + if (StringUtils.isBlank(stRfidVo.getCardCode())) { + stRfidVo.setSpendMoney(spendMoneyBudgeted); + String errorMsg = "离线人脸扣费v3,判断为刷卡方式,卡号为空, payId=" + stRfidVo.getPayId(); + log.error(errorMsg); + errorMessages = "刷卡卡号为空"; + if(!online) {//离线 + CompletableFuture.runAsync(()->iStBbzxOrderInfoService.buildOrderInfoNew(dateTime, stRfidVo.getRestId(), timesName, deviceCode, + stRfidVo.getUserId(), null, null, deductionType, + levelDeductRuleVo, errorMsg, stRfidVo)); + } + continue; + } + + String cardCode = stRfidVo.getCardCode(); + Result userBalanceResult = iStClientMemberBalanceService.getUserBalanceByCardCode(cardCode); + if (!userBalanceResult.isSuccess()) { + stRfidVo.setSpendMoney(spendMoneyBudgeted); + String errorMsg = "离线人脸扣费v3,刷卡方式根据卡号=" + cardCode + "查询用户钱包数据失败: " + userBalanceResult.getMessage(); + log.error(errorMsg); + errorMessages = "根据卡号查询用户钱包数据: " + userBalanceResult.getMessage(); + if(!online) {//离线 + CompletableFuture.runAsync(()->iStBbzxOrderInfoService.buildOrderInfoNew(dateTime, stRfidVo.getRestId(), timesName, deviceCode, + stRfidVo.getUserId(), null, null, deductionType, levelDeductRuleVo, + errorMsg, stRfidVo)); + } + continue; + } + + if(StringUtils.isNotBlank(stMiniMineBalanceVo.getCardCode()) && stMiniMineBalanceVo.getCardCodeStatus() != 1){ + errorMessages = "此卡已挂失"; + if(!online) {//离线 + String finalErrorMessages = errorMessages; + StMiniMineBalanceVo finalStMiniMineBalanceVo = stMiniMineBalanceVo; + CompletableFuture.runAsync(()->iStBbzxOrderInfoService.buildOrderInfoNew(dateTime, stRfidVo.getRestId(), timesName, deviceCode, + stRfidVo.getUserId(), null, null, deductionType, levelDeductRuleVo, + finalErrorMessages +",卡号:"+ finalStMiniMineBalanceVo.getCardCode(), stRfidVo)); + } + continue; + } + + stMiniMineBalanceVo = userBalanceResult.getResult(); + stMiniMineBalanceVo.setCardCode(stRfidVo.getCardCode()); + stRfidVo.setUserId(stMiniMineBalanceVo.getMemberId()); + userId = stMiniMineBalanceVo.getMemberId(); + } + //在线场景判断用户和钱包状态 + if(online){ + if(stMiniMineBalanceVo.getUserStatus() != 1){ + errorMessages = "主用户已停用"; + continue; + } + if(stMiniMineBalanceVo.getStatus()!= 1){ + errorMessages = "当前用户已停用"; + continue; + } + } + // 计算本次实际扣费金额并处理 + BigDecimal spendMoney = BigDecimal.ZERO; + BigDecimal money = stRfidVo.getSpendMoney(); + + try { + if (money != null && money.compareTo(BigDecimal.ZERO) > 0) { + // 自定义金额方式 + spendMoney = money; + StClientMemberLevelDeductRuleVo stClientMemberLevelDeductRuleVo = new StClientMemberLevelDeductRuleVo(); + stClientMemberLevelDeductRuleVo.setRuleName("自定义扣费"); + stClientMemberLevelDeductRuleVo.setDeductionType(4); + stMiniMineBalanceVo.setAmountThis(spendMoney); + + String data = syncMemberSpendOffLine(dateTime, restId, userId, stRfidVo, timesName, + stMiniMineBalanceVo, stClientMemberLevelDeductRuleVo); + if (StringUtils.isNotBlank(data)) { + String errorMsg = "自定义金额扣费操作失败, userId=" + userId + ", 错误信息: " + data; + log.error(errorMsg); + errorMessages = "自定义金额扣费操作失败: " + data; + continue; + } + } else { + // 扣费规则模式 + StClientMemberSpendDataVO stClientMemberSpendDataVO = + iStClientMemberBalanceService.fixedFee(levelDeductRuleVo, timesName, totalDeduction, + spendMoney, rewardDeduction, stMiniMineBalanceVo.getUserInnerType()); + spendMoney = stClientMemberSpendDataVO.getSpendMoney(); + stMiniMineBalanceVo.setAmountThis(spendMoney); + + String data = syncMemberSpendOffLine(dateTime, restId, userId, stRfidVo, timesName, + stMiniMineBalanceVo, levelDeductRuleVo); + if (StringUtils.isNotBlank(data)) { + String errorMsg = "规则模式扣费操作失败, userId=" + userId + ", 错误信息: " + data; + log.error(errorMsg); + errorMessages = "规则模式扣费操作失败:" + data; + continue; + } + } + } catch (Exception e) { + String errorMsg = "扣费处理过程中发生异常, userId=" + userId + ", 异常信息: " + e.getMessage(); + log.error(errorMsg, e); + errorMessages = "扣费处理过程中发生异常: " + e.getMessage(); + continue; + } + + if(online){//在线扣费 + stMiniMineBalanceVoOnline = stMiniMineBalanceVo; + spendMoneyOnline = spendMoney; + BigDecimal total = new BigDecimal(stMiniMineBalanceVo.getBalance()); + //扣费后余额 + totalOnline = total.subtract(spendMoney).setScale(2, BigDecimal.ROUND_HALF_UP); + } + } + } + if(online){//在线 + // 根据错误信息返回不同结果 + if (StringUtils.isBlank(errorMessages)) { + BigDecimal rewardDeduction = BigDecimal.ZERO;//用户扣费补贴金额 + stClientMemberSpendVO.setRealname(stMiniMineBalanceVoOnline.getRealname()); + stClientMemberSpendVO.setWorkNo(stMiniMineBalanceVoOnline.getWorkNo()); + stClientMemberSpendVO.setPayRealname(stMiniMineBalanceVoOnline.getPayRealname()); + stClientMemberSpendVO.setPayWorkNo(stMiniMineBalanceVoOnline.getPayWorkNo()); + stClientMemberSpendVO.setSuccess(true); + stClientMemberSpendVO.setPreferentialPrice(rewardDeduction); + stClientMemberSpendVO.setSpendMoney(spendMoneyOnline); + stClientMemberSpendVO.setBalance(totalOnline); + log.info("在线扣费耗时,success:{}",System.currentTimeMillis() - start); + return Result.OK("扣费成功",stClientMemberSpendVO); + } else { + log.info("在线扣费耗时,error:{}",System.currentTimeMillis() - start); + return Result.error(errorMessages); + } + } + return Result.OK("扣费成功"); + } + + /** + * 扣费只有三餐,加餐怎么算?20250916余,加餐不算,后面让马老师那边加设置,不在三餐时间不能刷脸就扣费 + * @param rfidVos + * @param restId + * @return + */ + @Override + public Map> groupByMeal(List rfidVos,String restId) { + List list = stBaseDinnerTimeService.list(new LambdaQueryWrapper() + .eq(SlBaseDinnerTime::getRestId, restId) + .orderByAsc(SlBaseDinnerTime::getEndTime)); + return rfidVos.stream() + .collect(Collectors.groupingBy(rfidVo -> { + long timestamp = Long.parseLong(rfidVo.getDateTime()); + Date date = new Date(timestamp); + SlBaseDinnerTime dinnerTime = getCanteenDinnerType(list, date); + if(null != dinnerTime){ + return dinnerTime.getDinnerType(); + } else { + return MealGroupingUtil.getDefaultMealType(date); + } + })); + } + + private SlBaseDinnerTime getCanteenDinnerType(List list, Date dataTime) { + if (org.apache.commons.collections.CollectionUtils.isNotEmpty(list)) { + for (SlBaseDinnerTime slBaseDinnerTime : list) { + DateTime endTime = DateUtil.parse(DateUtil.today() + " " + slBaseDinnerTime.getEndTime()); + Date date = new Date(); + if(null != dataTime){ + endTime = DateUtil.parse(DateUtils.formatDate(dataTime,"yyyy-MM-dd") + " " + slBaseDinnerTime.getEndTime()); + date = dataTime; + } + if (DateUtil.compare(date, endTime) <= 0) { + return slBaseDinnerTime; + } + } + return list.get(list.size() - 1); + } + return null; + } + + /** + * 异步生成订单和进行实际扣费流程 + * @param dateTime 订单时间 + * @param restId + * @param userId + * @param stRfidVo + * @param timesName + */ + @Override + public String syncMemberSpendOffLine(Date dateTime, String restId, String userId, StRfidVo stRfidVo, String timesName, StMiniMineBalanceVo miniMineBalanceVo, StClientMemberLevelDeductRuleVo levelDeductRuleVo){ + // 生成新订单 + StBbzxOrderInfo stBbzxOrderInfo = iStBbzxOrderInfoService.buildOrderInfoNewOffLine(dateTime,restId, timesName, stRfidVo.getDeviceCode(), userId, + "", "", levelDeductRuleVo.getDeductionType(),levelDeductRuleVo,null, stRfidVo,miniMineBalanceVo); + StClientMemberSpendDTO stClientMemberSpendDTO = new StClientMemberSpendDTO(); + stClientMemberSpendDTO.setCanteenId(stRfidVo.getRestId()); + stClientMemberSpendDTO.setOrderNo(stBbzxOrderInfo.getOrderNo()); + stClientMemberSpendDTO.setId(miniMineBalanceVo.getMemberId());//钱包用户id + stClientMemberSpendDTO.setMiniMineBalanceVo(miniMineBalanceVo);//钱包用户 + StClientMemberSpendVO stClientMemberSpendVO = iStClientMemberBalanceService.memberSpendOffline(stClientMemberSpendDTO,stBbzxOrderInfo); + if(null != stClientMemberSpendVO && !stClientMemberSpendVO.getSuccess()){ + return stClientMemberSpendVO.getMsg(); + } else { + return null; + } + } + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StClientMemberLevelDemotionTimeServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StClientMemberLevelDemotionTimeServiceImpl.java new file mode 100644 index 0000000..403a06c --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StClientMemberLevelDemotionTimeServiceImpl.java @@ -0,0 +1,316 @@ +/** + * use shuwei code generator created + */ +package org.jeecg.payment.service.impl; + +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.base.service.impl.JeecgServiceImpl; +import org.jeecg.common.system.util.JwtUtil; +import org.jeecg.payment.entity.StClientMemberLevelDemotionTime; +import org.jeecg.payment.mapper.StClientMemberLevelDemotionTimeMapper; +import org.jeecg.payment.service.IStClientMemberLevelDemotionTimeService; +import org.jeecg.payment.vo.*; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + + +/** + * 储值规则服务实现类 + */ +@Service +public class StClientMemberLevelDemotionTimeServiceImpl extends JeecgServiceImpl implements IStClientMemberLevelDemotionTimeService { + + + + + + + @Override + public Map topUpAwardService(Integer levelId, Float topUpPrice, String canteenId) { + Map result = new HashMap<>(); + try { + //赠送的金额 + float reChargeAmount = 0; + //赠送的积分 + int reChargePIntegral = 0; + + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper(); + lambdaQueryWrapper.eq(StClientMemberLevelDemotionTime::getIsEnable, 1); + if(StringUtils.isNotBlank(canteenId)){ + lambdaQueryWrapper.eq(StClientMemberLevelDemotionTime::getCanteenId, canteenId); + } + StClientMemberLevelDemotionTime ruleSource = this.getOne(lambdaQueryWrapper); + if (ruleSource == null) { + result.put("amount", reChargeAmount); + result.put("integral", reChargePIntegral); + return result; + } + + //储值赠送方式 + List storedVOS = JSON.parseArray(ruleSource.getStoredRule(), StMemberLevelStoredVO.class); + if (!CollectionUtils.isEmpty(storedVOS)) { + AtomicInteger flag = new AtomicInteger(ruleSource.getStoredRuleFlag()); + if (flag.get() == 1) { //固定规则 + for (StMemberLevelStoredVO s : storedVOS) { + //获得对应的赠送金额 + Float rewardAmount = s.getRewardAmount() == null ? 0f : s.getRewardAmount(); + //获得对应的赠送积分 + Integer rewardIntegral = s.getRewardIntegral() == null ? 0 : s.getRewardIntegral(); + + if (topUpPrice < s.getRechargePresenter()) { + //获取上一区间对应的赠送金额及积分 +// result.put("amount", reChargeAmount); +// result.put("integral", reChargePIntegral); + continue; + + } else { + //获得对应的赠送金额 +// reChargeAmount = (topUpPrice / s.getRechargePresenter()) * rewardAmount; + reChargeAmount = rewardAmount; + //获得对应的赠送积分 + reChargePIntegral = rewardIntegral; + } + } + } else { //比例规则 + + //至多一条记录 + StMemberLevelStoredVO storedVO = storedVOS.get(0); + //赠送积分 + if (topUpPrice < storedVO.getRechargePresenter()) { + reChargePIntegral = 0; + } else { + //赠送比例 + Float rewardAmount = storedVO.getRewardAmount(); + reChargeAmount = topUpPrice * (rewardAmount / 100); + reChargePIntegral = storedVO.getRewardIntegral(); + } + + } + } + + + result.put("amount", reChargeAmount); + result.put("integral", reChargePIntegral); + + return result; + } catch (Exception ex) { + ex.printStackTrace(); + + result.put("amount", 0); + result.put("integral", 0); + + return result; + } + + } + + + @Override + public IPage selectStClientMemberLevelDemotionTimePage(StClientMemberLevelDemotionTimeVO query){ + Page page = new Page<>(query.getPageNo(), query.getPageSize()); + List pageList = baseMapper.selectSwClientMemberLevelDemotionTimePage(page,query); + return page.setRecords(pageList); + + } + + @Override + public Boolean saveOrUpdateService(StClientMemberLevelDemotionTimeVO vo) { + //获得机构对应的配置 + StClientMemberLevelDemotionTime demotionTime = null; + if(StringUtils.isNotBlank(vo.getId())){ + demotionTime = getById(vo.getId()); + } + if (demotionTime == null) { + demotionTime = new StClientMemberLevelDemotionTime(); + demotionTime.setCanteenId(vo.getCanteenId()); + } + demotionTime.setRuleName(vo.getRuleName()); + //储值规则 + demotionTime.setStoredRuleFlag(vo.getStoredRuleFlag()); + //储值扣款顺序 + demotionTime.setPaymentOrder(vo.getPaymentOrder()); + demotionTime.setRewardAmountReset(vo.getRewardAmountReset()); + //降级时间 + if (!org.springframework.util.StringUtils.isEmpty(vo.getHourMinuteStr())) { + demotionTime.setDHour(Integer.parseInt(vo.getHourMinuteStr().split(":")[0])); + demotionTime.setDMinute(Integer.parseInt(vo.getHourMinuteStr().split(":")[1])); + } + //储值规则 + List storedVOS = vo.getStoredVOS(); + if (!CollectionUtils.isEmpty(storedVOS)) { + String storedStr = JSON.toJSONString(storedVOS); + demotionTime.setStoredRule(storedStr); + } + //积分抵现规则 + if (!org.springframework.util.StringUtils.isEmpty(vo.getIntegralVO())) { + StMemberLevelIntegralVO integralVO = vo.getIntegralVO(); + String intStr = JSON.toJSONString(integralVO); + demotionTime.setIntegralRule(intStr); + //新增单笔订单积分抵扣规则 +// if (StringUtils.isBlank(vo.getId()) && integralVO.getStatus() == 1 && integralVO.getDeductionAmount() != null && integralVO.getIntegralCount() != null) { + StMemberLevelSingleVO singleVO = vo.getSingleVO(); + String singleStr = JSON.toJSONString(singleVO); + demotionTime.setSingleMaxIntegralRule(singleStr); +// } + } + //积分清零规则 + if (!org.springframework.util.StringUtils.isEmpty(vo.getResetVO())) { + StMemberLevelResetVO resetVO = vo.getResetVO(); + String reStr = JSON.toJSONString(resetVO); + demotionTime.setIntegralResetRule(reStr); + } + //积分清零提示短信 + if (!org.springframework.util.StringUtils.isEmpty(vo.getResetMessageVO())) { + StMemberLevelResetMessageVO messageVO = vo.getResetMessageVO(); + String messStr = JSON.toJSONString(messageVO); + demotionTime.setIntegralResetRemindMessage(messStr); + } + demotionTime.setIsSync(0); + return saveOrUpdate(demotionTime); + } + + @Override + public StClientMemberLevelDemotionTimeVO getOneService(StClientMemberLevelDemotionTime swClientMemberLevelDemotionTime) { + StClientMemberLevelDemotionTimeVO vo = new StClientMemberLevelDemotionTimeVO(); + StClientMemberLevelDemotionTime memberLevel = getById(swClientMemberLevelDemotionTime.getId()); + if (memberLevel == null) { + return null; + } + BeanUtils.copyProperties(memberLevel, vo); + if (vo.getStoredRuleFlag() == null) { + vo.setStoredRuleFlag(1); + } + if (vo.getPaymentOrder() == null) { + vo.setPaymentOrder(1); + } + /** + * 解析会员卡等级储值规则 + */ + String storedRule = memberLevel.getStoredRule(); + if (!org.springframework.util.StringUtils.isEmpty(storedRule)) { + List storedVOS = JSON.parseArray(storedRule, StMemberLevelStoredVO.class); + vo.setStoredVOS(storedVOS); + } else { + vo.setStoredVOS(new ArrayList()); + } + /** + * 积分抵现规则 + */ + String integralRule = memberLevel.getIntegralRule(); + StMemberLevelIntegralVO integralVO; + if (!org.springframework.util.StringUtils.isEmpty(integralRule)) { + integralVO = JSON.parseObject(integralRule, StMemberLevelIntegralVO.class); + } else { + integralVO = new StMemberLevelIntegralVO(); + integralVO.setStatus(0); + } + vo.setIntegralVO(integralVO); + /** + * 单笔积分抵扣上限规则 + */ + String singleRule = memberLevel.getSingleMaxIntegralRule(); + StMemberLevelSingleVO singleVO; + if (!org.springframework.util.StringUtils.isEmpty(singleRule)) { + singleVO = JSON.parseObject(singleRule, StMemberLevelSingleVO.class); + } else { + singleVO = new StMemberLevelSingleVO(); + singleVO.setStatus(0); + } + vo.setSingleVO(singleVO); + /** + * 积分清零规则 + */ + String resetRule = memberLevel.getIntegralResetRule(); + StMemberLevelResetVO resetVO; + if (!org.springframework.util.StringUtils.isEmpty(resetRule)) { + resetVO = JSON.parseObject(resetRule, StMemberLevelResetVO.class); + } else { + resetVO = new StMemberLevelResetVO(); + resetVO.setStatus(0); + } + vo.setResetVO(resetVO); + /** + * 提示短信 + */ + String messageRule = memberLevel.getIntegralResetRemindMessage(); + StMemberLevelResetMessageVO messageVO; + if (!org.springframework.util.StringUtils.isEmpty(messageRule)) { + messageVO = JSON.parseObject(messageRule, StMemberLevelResetMessageVO.class); + } else { + messageVO = new StMemberLevelResetMessageVO(); + messageVO.setStatus(0); + } + vo.setResetMessageVO(messageVO); + return vo; + } + + @Override + public List appMemberLevelStoredService() { + String canteenId = JwtUtil.getCanteenId(); + + StClientMemberLevelDemotionTime memberLevelDemotionTime = + getOne(new LambdaQueryWrapper() + .eq(StClientMemberLevelDemotionTime::getCanteenId, canteenId)); + if (memberLevelDemotionTime != null) { + String storedRule = memberLevelDemotionTime.getStoredRule(); + if (!org.springframework.util.StringUtils.isEmpty(storedRule)) { + List storedRuleList = JSON.parseArray(storedRule, StMemberLevelStoredVO.class); + Integer storedRuleFlag = memberLevelDemotionTime.getStoredRuleFlag(); + switch (storedRuleFlag) { + case 1: + return storedRuleList; + case 2: + /** + * 处理按比例 + */ + StMemberLevelStoredVO levelStored = storedRuleList.get(0); + //每满金额*赠送比例 + BigDecimal rechargePresenter = + new BigDecimal(levelStored.getRechargePresenter() == null ? 0 : levelStored.getRechargePresenter()); + BigDecimal rewardAmount = + new BigDecimal(levelStored.getRewardAmount() == null ? 0 : levelStored.getRewardAmount()); + float newRewardAmount = rechargePresenter.multiply(rewardAmount).floatValue(); + levelStored.setRewardAmount(newRewardAmount); + return storedRuleList; + } + } else { + return null; + } + } + return null; + } + + @Override + public Result memberLevelDemotionTimeByCanteenId(String canteenId){ + if(StringUtils.isBlank(canteenId)){ + return Result.error("食堂ID不能为空"); + } + try { + LambdaQueryWrapper lambdaQueryWrapper1 = new LambdaQueryWrapper<>(); + lambdaQueryWrapper1.eq(StClientMemberLevelDemotionTime::getCanteenId,canteenId); + lambdaQueryWrapper1.eq(StClientMemberLevelDemotionTime::getIsEnable,1); + StClientMemberLevelDemotionTime memberLevel = this.getOne(lambdaQueryWrapper1); + if(null != memberLevel){ + return Result.OK(memberLevel); + } + } catch (Exception e) { + return Result.error("食堂扣费规则获取异常请检查后台配置是否正常"); + } + return Result.error("食堂扣费顺序未设置"); + } + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StClientMemberTransactionRecordServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StClientMemberTransactionRecordServiceImpl.java new file mode 100644 index 0000000..c1c3d55 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/service/impl/StClientMemberTransactionRecordServiceImpl.java @@ -0,0 +1,101 @@ +package org.jeecg.payment.service.impl; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.system.base.service.impl.JeecgServiceImpl; +import org.jeecg.payment.dto.StClientMemberTransactionRecordDTO; +import org.jeecg.payment.entity.StClientMemberTransactionRecord; +import org.jeecg.payment.enums.MemberEnum; +import org.jeecg.payment.enums.MemberTransactionTypeEnum; +import org.jeecg.payment.mapper.StClientMemberTransactionRecordMapper; +import org.jeecg.payment.service.IStClientMemberTransactionRecordService; +import org.jeecg.payment.vo.InOutRecordInfoVo; +import org.jeecg.payment.vo.StClientMemberTransactionRecordVO; +import org.jeecg.system.service.ISysPayDepartService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 交易记录 + */ + +@Service +public class StClientMemberTransactionRecordServiceImpl extends JeecgServiceImpl implements IStClientMemberTransactionRecordService { + + @Resource + private ISysPayDepartService iSysPayDepartService; + + @Override + public IPage pageQuery(StClientMemberTransactionRecordDTO query) { + Page page = new Page<>(query.getPageNo(), query.getPageSize()); + if(StringUtils.isNotBlank(query.getDepartId())){ + List allCascadeDeptIds = iSysPayDepartService.getAllCascadeDeptIds(query.getDepartId()); + if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(allCascadeDeptIds)){ + query.setList(allCascadeDeptIds); + } + } + List pageList = baseMapper.pageList(page,query); + if (CollectionUtils.isNotEmpty(pageList)) { + for (StClientMemberTransactionRecordVO model : pageList) { + model.setStatusName(MemberTransactionTypeEnum.getName(model.getStatus())); + model.setPaymentMethodName(MemberEnum.getXcxName(model.getPaymentMethod())); + model.setUpdateStatusName(null != model.getUpdateStatus() && 1 == model.getUpdateStatus() ? "前端":"后台"); + if(null != model.getBusinessModel()&& 1 == model.getBusinessModel() ){ + model.setBusinessModelName("定值模式");//扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费,4:自定义金额) + } + if(null != model.getBusinessModel()&& 4 == model.getBusinessModel() ){ + model.setBusinessModelName("金额模式");//扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费,4:自定义金额) + } + } + } + return page.setRecords(pageList); + } + + @Override + public List queryList(StClientMemberTransactionRecordDTO query) { + List pageList = baseMapper.pageList(query); + if (CollectionUtils.isNotEmpty(pageList)) { + for (StClientMemberTransactionRecordVO model : pageList) { + model.setStatusName(MemberTransactionTypeEnum.getName(model.getStatus())); + model.setPaymentMethodName(MemberEnum.getXcxName(model.getPaymentMethod())); + model.setUpdateStatusName(null != model.getUpdateStatus() && 1 == model.getUpdateStatus() ? "前端":"后台"); + if(null != model.getBusinessModel()&& 1 == model.getBusinessModel() ){ + model.setBusinessModelName("定值模式");//扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费,4:自定义金额) + } + if(null != model.getBusinessModel()&& 4 == model.getBusinessModel() ){ + model.setBusinessModelName("金额模式");//扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费,4:自定义金额) + } + } + } + return pageList; + } + + @Override + public IPage pageBalanceRefundList(Page page,StClientMemberTransactionRecordDTO query) { + + List pageList = baseMapper.pageBalanceRefundList(page,query); + if (CollectionUtils.isNotEmpty(pageList)) { + for (StClientMemberTransactionRecordVO model : pageList) { + model.setStatusName(MemberTransactionTypeEnum.getName(model.getStatus())); + } + } + return page.setRecords(pageList); + } + + + + @Override + public InOutRecordInfoVo getOneInOutInfo(String recordId) { + InOutRecordInfoVo oneInOutInfo = baseMapper.getOneInOutInfo(recordId); + if(oneInOutInfo != null){ + String xcxPayMethodName = MemberEnum.getXcxName(Integer.parseInt(oneInOutInfo.getPaymentMethod())); + oneInOutInfo.setPaymentMethod(xcxPayMethodName); + return oneInOutInfo; + } + return null; + } +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/utils/ExcelResultGenerator.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/utils/ExcelResultGenerator.java new file mode 100644 index 0000000..b76e322 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/utils/ExcelResultGenerator.java @@ -0,0 +1,378 @@ +package org.jeecg.payment.utils; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.jeecg.payment.dto.StClientMemberTopUpDTO; +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Slf4j +public class ExcelResultGenerator { + + /** + * 生成带标记和报告的Excel + * @param file 原始Excel文件 + * @param canteenId 食堂ID + * @param idCardSet 需要标记为红色的身份证号集合 + * @param resultMap 导入结果映射 key:身份证号 value:结果描述 + * @return 处理后的Excel输入流 + */ + public InputStream generateResultExcel(MultipartFile file, String canteenId, + Set idCardSet, Map resultMap) { + try { + // 1. 读取原始Excel + InputStream inputStream = file.getInputStream(); + String fileName = file.getOriginalFilename(); + Workbook workbook; + + // 根据文件格式创建对应的Workbook + if (fileName != null && fileName.endsWith(".xls")) { + workbook = new HSSFWorkbook(inputStream); + } else { + workbook = new XSSFWorkbook(inputStream); + } + + // 2. 获取第一个工作表 + Sheet sheet = workbook.getSheetAt(0); + if (sheet == null) { + return null; + } + + // 3. 创建红色背景样式 + CellStyle redBgStyle = workbook.createCellStyle(); + redBgStyle.cloneStyleFrom(sheet.getRow(0).getCell(0).getCellStyle()); // 复制原始样式 + redBgStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); + redBgStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + + // 4. 获取最后一列索引,准备添加结果列 + int lastColNum = 0; + Row firstRow = sheet.getRow(0); + if (firstRow != null) { + lastColNum = firstRow.getLastCellNum(); + } + + // 在第一行添加结果列标题 + Cell resultTitleCell = firstRow.createCell(lastColNum); + resultTitleCell.setCellValue("导入结果"); + CellStyle titleStyle = workbook.createCellStyle(); + titleStyle.cloneStyleFrom(firstRow.getCell(0).getCellStyle()); + titleStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + titleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + resultTitleCell.setCellStyle(titleStyle); + + // 5. 遍历处理每一行数据 + for (int i = 1; i <= sheet.getLastRowNum(); i++) { + Row row = sheet.getRow(i); + if (row == null) { + continue; + } + + // 获取身份证号单元格(第3列,索引为2) + Cell idCardCell = row.getCell(2); + String idCard = ""; + if (idCardCell != null) { + idCardCell.setCellType(CellType.STRING); + idCard = idCardCell.getStringCellValue(); + } + + // 6. 处理行背景色:如果身份证号在指定集合中,设置红色背景 + if (idCardSet.contains(idCard)) { + for (int j = 0; j <= row.getLastCellNum(); j++) { + Cell cell = row.getCell(j); + if (cell == null) { + cell = row.createCell(j); + } + cell.setCellStyle(redBgStyle); + } + } + + // 7. 添加导入结果描述 + Cell resultCell = row.createCell(lastColNum); + String resultDesc = resultMap.getOrDefault(idCard, "未处理"); + resultCell.setCellValue(resultDesc); + + // 保持与该行其他单元格相同的样式 + if (row.getLastCellNum() > 0) { + CellStyle cellStyle = row.getCell(0).getCellStyle(); + resultCell.setCellStyle(cellStyle); + } + } + + // 8. 调整最后一列宽度自适应内容 + sheet.autoSizeColumn(lastColNum); + + // 9. 将处理后的Workbook转换为InputStream + ByteArrayOutputStream out = new ByteArrayOutputStream(); + workbook.write(out); + workbook.close(); + inputStream.close(); + + return new ByteArrayInputStream(out.toByteArray()); + + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + + /** + * 核心工具方法:生成带标记的结果Excel + * 功能:1. 保留原始Excel格式;2. 异常行(重复/缺失用户等)标红;3. 最后一列追加导入结果描述 + * @param originalFile 原始上传文件 + * @param successList 充值成功的DTO列表 + * @param failList 充值失败的DTO列表 + * @param duplicateSerialList 流水号重复的DTO列表 + * @param multiUserList 用户重复的DTO列表 + * @param missingUserList 用户缺失的DTO列表 + * @return 处理后的Excel输入流 + */ + public static InputStream generateResultExcel( + MultipartFile originalFile, + List successList, + List failList, + List duplicateSerialList, + List multiUserList, + List missingUserList) throws IOException { + + // 1. 读取原始Excel,创建Workbook(兼容.xls和.xlsx) + InputStream originalInputStream = originalFile.getInputStream(); + String fileName = originalFile.getOriginalFilename(); + Workbook workbook; + int maxColumnIndex; + if (fileName != null && fileName.endsWith(".xls")) { + workbook = new HSSFWorkbook(originalInputStream); + maxColumnIndex = 255; // Excel 2003最大列索引(A~IV) + } else { + workbook = new XSSFWorkbook(originalInputStream); + maxColumnIndex = 16383; // Excel 2007+最大列索引(A~XFD) + } + Sheet sheet = workbook.getSheetAt(0); + if (sheet == null) { + throw new RuntimeException("原始Excel无工作表,请检查文件格式"); + } + + // 2. 初始化样式(保留原始格式,避免错乱) + Row headerRow = sheet.getRow(0); + CellStyle redBgStyle = workbook.createCellStyle(); + CellStyle resultHeaderStyle = workbook.createCellStyle(); + if (headerRow != null && headerRow.getCell(0) != null) { + // 复制表头样式(字体、边框、对齐方式与原始一致) + redBgStyle.cloneStyleFrom(headerRow.getCell(0).getCellStyle()); + resultHeaderStyle.cloneStyleFrom(headerRow.getCell(0).getCellStyle()); + } + redBgStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); + redBgStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + resultHeaderStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + resultHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + + // 3. 构建「身份证号→导入结果」映射(核心修复:格式统一+优先级处理) + Map idCardToResultMap = new HashMap<>(); + + + // 3.2 按业务优先级添加结果(成功 > 重复 > 多用户 > 缺失 > 失败) + // 优先级1:充值成功(不被后续低优先级结果覆盖) + for (StClientMemberTopUpDTO dto : successList) { + if (dto == null) continue; + String normalizedCardNo = dto.getCardNo(); + if (StringUtils.isNotBlank(normalizedCardNo) && !idCardToResultMap.containsKey(normalizedCardNo)) { + idCardToResultMap.put(normalizedCardNo, "校验正常"); + log.debug("映射添加[成功]:{} → 校验正常", normalizedCardNo); + } + } + + // 优先级2:流水号重复(不覆盖成功结果) + for (StClientMemberTopUpDTO dto : duplicateSerialList) { + if (dto == null) continue; + String normalizedCardNo = dto.getCardNo(); + if (StringUtils.isNotBlank(normalizedCardNo) && !idCardToResultMap.containsKey(normalizedCardNo)) { + idCardToResultMap.put(normalizedCardNo, "流水号已导入(历史重复)"); + log.debug("映射添加[重复]:{} → 流水号已导入(历史重复)", normalizedCardNo); + } + } + + // 优先级3:身份证对应多个用户(不覆盖成功/重复结果) + for (StClientMemberTopUpDTO dto : multiUserList) { + if (dto == null) continue; + String normalizedCardNo = dto.getCardNo(); + if (StringUtils.isNotBlank(normalizedCardNo) && !idCardToResultMap.containsKey(normalizedCardNo)) { + idCardToResultMap.put(normalizedCardNo, "身份证对应多个用户(请核查)"); + log.debug("映射添加[多用户]:{} → 身份证对应多个用户(请核查)", normalizedCardNo); + } + } + + // 优先级4:身份证无对应用户(不覆盖前3类结果) + for (StClientMemberTopUpDTO dto : missingUserList) { + if (dto == null) continue; + String normalizedCardNo = dto.getCardNo(); + if (StringUtils.isNotBlank(normalizedCardNo) && !idCardToResultMap.containsKey(normalizedCardNo)) { + idCardToResultMap.put(normalizedCardNo, "身份证无对应用户/钱包(请先创建)"); + log.debug("映射添加[缺失用户]:{} → 身份证无对应用户/钱包(请先创建)", normalizedCardNo); + } + } + + // 优先级5:通用充值失败(仅覆盖未匹配到前4类的结果) + for (StClientMemberTopUpDTO dto : failList) { + if (dto == null) continue; + String normalizedCardNo = dto.getCardNo(); + if (StringUtils.isNotBlank(normalizedCardNo) && !idCardToResultMap.containsKey(normalizedCardNo)) { + idCardToResultMap.put(normalizedCardNo, "充值失败(系统异常,请重试)"); + log.debug("映射添加[失败]:{} → 充值失败(系统异常,请重试)", normalizedCardNo); + } + } + + + // 4. 处理Excel内容:添加结果列+标红异常行(核心修复:身份证号匹配逻辑) + int resultColumnIndex; + // 4.1 计算结果列索引(避免列数超限) + if (headerRow == null) { + resultColumnIndex = 0; + headerRow = sheet.createRow(0); // 无表头时新建表头行 + } else { + int originalLastColNum = headerRow.getLastCellNum(); + resultColumnIndex = originalLastColNum; + if (resultColumnIndex > maxColumnIndex) { + throw new RuntimeException( + "原始Excel列数已达上限(" + (maxColumnIndex + 1) + "列),无法添加「导入结果」列,请简化原始表格后重试" + ); + } + } + + // 4.2 表头添加“导入结果”列 + Cell resultHeaderCell = headerRow.createCell(resultColumnIndex); + resultHeaderCell.setCellValue("导入结果"); + resultHeaderCell.setCellStyle(resultHeaderStyle); + + // 4.3 遍历数据行(逐行匹配结果,区分不同错误类型) + for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) { + Row dataRow = sheet.getRow(rowNum); + if (dataRow == null) { + log.debug("Excel第{}行为空,跳过处理", rowNum + 1); + continue; + } + // 判断行是否为空行(有行对象但无有效单元格数据) + boolean isEmptyRow = isEmptyRowMethod(dataRow); + if (isEmptyRow) { + log.debug("Excel第{}行为空行(无有效单元格),跳过处理", rowNum + 1); + continue; + } + // 4.3.1 读取并标准化当前行的身份证号(与映射表格式保持一致) + Cell idCardCell = dataRow.getCell(3); // 假设身份证号在第3列(索引2) + String currentIdCard = null; + if (idCardCell != null) { + idCardCell.setCellType(CellType.STRING); // 强制转为字符串,避免数字格式问题 + currentIdCard = idCardCell.getStringCellValue(); // 标准化格式 + log.debug("Excel第{}行:读取的身份证号(标准化后)→ {}", rowNum + 1, currentIdCard); + } + + // 4.3.2 匹配结果(区分4种场景,避免统一错误) + String resultDesc; + if (currentIdCard == null || currentIdCard.isEmpty()) { + // 场景1:Excel中无身份证号 + resultDesc = "未处理(无身份证信息)"; + } else if (!idCardToResultMap.containsKey(currentIdCard)) { + // 场景2:身份证号存在,但未参与充值流程(未在任何DTO列表中) + resultDesc = "未处理(身份证号未参与充值流程)"; + } else { + // 场景3:匹配到映射表中的结果(成功/重复/多用户/缺失/失败) + resultDesc = idCardToResultMap.get(currentIdCard); + } + log.debug("Excel第{}行:最终结果→ {}", rowNum + 1, resultDesc); + + // 4.3.3 添加结果单元格并复制原始样式 + Cell resultCell = dataRow.createCell(resultColumnIndex); + resultCell.setCellValue(resultDesc); + if (dataRow.getLastCellNum() > 0) { + Cell firstCell = dataRow.getCell(0); + if (firstCell != null) { + CellStyle dataCellStyle = workbook.createCellStyle(); + dataCellStyle.cloneStyleFrom(firstCell.getCellStyle()); + resultCell.setCellStyle(dataCellStyle); + } + } + + // 4.3.4 标红异常行(仅“校验正常”不标红) + if (!"校验正常".equals(resultDesc)) { + int maxCol = Math.min(dataRow.getLastCellNum(), maxColumnIndex); + for (int colNum = 0; colNum <= maxCol; colNum++) { + Cell cell = dataRow.getCell(colNum); + if (cell == null) cell = dataRow.createCell(colNum); // 空单元格也标红 + cell.setCellStyle(redBgStyle); + } + resultCell.setCellStyle(redBgStyle); + } + } + + // 4.4 自适应结果列宽度(避免内容被截断) + sheet.autoSizeColumn(resultColumnIndex); + + // 5. 流转换与资源关闭(避免内存泄漏) + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + workbook.write(outputStream); + outputStream.flush(); + // 按顺序关闭资源(先关Workbook,再关输入流) + workbook.close(); + originalInputStream.close(); + outputStream.close(); + + return new ByteArrayInputStream(outputStream.toByteArray()); + } + + // 工具方法:判断行是否为空行(无有效单元格数据) + private static boolean isEmptyRowMethod(Row row) { + // 情况1:行的单元格集合为空(_cells数量为0) + if (row.getPhysicalNumberOfCells() == 0) { + return true; + } + + // 情况2:遍历所有单元格,检查是否均为空白(处理单元格存在但值为空的情况) + for (int cellNum = row.getFirstCellNum(); cellNum <= row.getLastCellNum(); cellNum++) { + Cell cell = row.getCell(cellNum, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); + String cellValue = getCellValue(cell).trim(); // 自定义方法:获取单元格文本值 + if (StringUtils.isNotBlank(cellValue)) { + // 存在非空白单元格,不是空行 + return false; + } + } + + // 所有单元格均为空白,视为空行 + return true; + } + + // 辅助方法:获取单元格的文本值(兼容各种单元格类型) + private static String getCellValue(Cell cell) { + if (cell == null) { + return ""; + } + switch (cell.getCellType()) { + case STRING: + return cell.getStringCellValue(); + case NUMERIC: + if (DateUtil.isCellDateFormatted(cell)) { + return cell.getDateCellValue().toString(); + } else { + // 处理数字避免科学计数法 + return new BigDecimal(cell.getNumericCellValue()).toPlainString(); + } + case BOOLEAN: + return String.valueOf(cell.getBooleanCellValue()); + case FORMULA: + return cell.getCellFormula(); // 或解析公式结果:evaluateFormulaCell + default: + return ""; + } + } + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/utils/ExcelUtils.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/utils/ExcelUtils.java new file mode 100644 index 0000000..9fb180c --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/utils/ExcelUtils.java @@ -0,0 +1,472 @@ +package org.jeecg.payment.utils; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.RegionUtil; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.tomcat.util.http.fileupload.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.math.BigDecimal; +import java.net.URLEncoder; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; + +/** + * 支持复合表头导出的Excel工具类 + * @author fans + * @version 1.0 + * @date 2024/11/25 + */ +public class ExcelUtils { + private ExcelUtils() { + throw new UnsupportedOperationException("initialization is prohibited..."); + } + + private static Logger logger = LoggerFactory.getLogger(ExcelUtils.class); + + /** + * 图片 + */ + public static class Image { + private byte[] imageData; + + public Image(byte[] imageData) { + this.imageData = imageData; + } + + public byte[] getImageData() { + return imageData; + } + } + + /** + * 导出(目前仅支持小于两行表头的合并操作) + * @param realName 导出文件名 + * @param data 导出数据 + * @param fields 导出字段,比如 date|时间,firstHeader|表头1,secondHeader|表头2,sub001|编号-0001|子标题1,sub002|编号-0001|子标题2,sub003|编号-0001|子标题3,sub004|编号-0001|子标题4,remark|备注 + */ + public static void export(HttpServletResponse response, String realName, List> data, String fields) { + String fileName = realName + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + ".xlsx"; + + writeFileToResponse(construct(fields, data, fileName), response); + } + + /** + * 导出并转换时间格式(目前仅支持小于两行表头的合并操作) + * @param realName 导出文件名 + * @param list 导出数据 + * @param fields 导出字段,比如 date|时间,firstHeader|表头1,secondHeader|表头2,sub001|编号-0001|子标题1,sub002|编号-0001|子标题2,sub003|编号-0001|子标题3,sub004|编号-0001|子标题4,remark|备注 + * @param formatter 日期格式化字符串 + * @param tableHeader 是否将文件名称同时设置为表格内的标题行(true:设置,false:不设置) + */ + public static void export(HttpServletResponse response, String realName, List list, String fields, String formatter,Boolean tableHeader) { + if(tableHeader){ + if(StringUtils.isNotBlank(fields) && fields.contains("|")){ + fields = fields.replaceAll("\\|", "|"+realName+"|"); + } + } + List> data = ExcelUtils.listObjToListMap(list); + convertDateToStr(data, formatter); + export(response, realName, data, fields); + } + + /** + * 将文件写入响应流 + * @param file 文件 + * @param response 响应 + * @throws UnsupportedEncodingException + */ + public static void writeFileToResponse(MultipartFile file, HttpServletResponse response) { + // 设置文件ContentType类型,这样设置,会自动判断下载文件类型 + response.setContentType("multipart/form-data"); + + InputStream inputStream = null; + try { + String fileName = URLEncoder.encode(file.getOriginalFilename(), "UTF-8"); + response.setHeader("Content-Disposition", "attachment;filename=" + fileName); + response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0"); + response.setHeader("Pragma", "public"); + response.setDateHeader("Expires", (System.currentTimeMillis() + 1000)); + + inputStream = file.getInputStream(); + OutputStream out = response.getOutputStream(); + byte[] buffer = new byte[1024]; + int count = 0; + while ((count = inputStream.read(buffer)) != -1) { + out.write(buffer, 0, count); + } + out.close(); + out.flush(); + } catch (IOException e) { + logger.error("文件下载异常:文件名【%s】", file.getName(), e); + } finally { + IOUtils.closeQuietly(inputStream); + } + } + + /** + * 创建生成 excel 文档 + * @param fields 导出字段 + * @param data 导出数据 + * @param fileName 导出文档名称 + * @return 生成的 excel 文档 + */ +// private static MultipartFile construct(String fields, List> data, String fileName) { +// Workbook workbook = new XSSFWorkbook(); +// Sheet sheet = workbook.createSheet(); +// CellStyle cellStyle = createCellStyle(workbook); +// Map fieldOrder = getFieldOrder(fields); +// // 头部占的行数 +// int headerRows = calculateHeaderRows(fields); +// +// // 创建一个新的单元格样式 +// CellStyle cellStyle1 = createCellStyle(workbook); +// // 创建一个字体 +// Font font = workbook.createFont(); +//// font.setFontName("Arial"); // 设置字体名称 +//// font.setFontHeightInPoints((short) 14); // 设置字号 +// font.setBold(true); // 设置加粗 +// // 将字体应用到样式 +// cellStyle1.setFont(font); +// +// initHeader(sheet, cellStyle1, fields, headerRows); +// fillData(workbook, sheet, cellStyle, data, fieldOrder, headerRows); +// try { +// fileName = URLEncoder.encode(fileName, "UTF-8"); +// } catch (UnsupportedEncodingException e) { +// e.printStackTrace(); +// } +// DiskFileItem fileItem = (DiskFileItem)(new DiskFileItemFactory()).createItem("file", "text/plain", true, fileName); +// try { +// OutputStream os = fileItem.getOutputStream(); +// Throwable var8 = null; +// +// try { +// workbook.write(os); +// } catch (Throwable var18) { +// var8 = var18; +// throw var18; +// } finally { +// if (os != null) { +// if (var8 != null) { +// try { +// os.close(); +// } catch (Throwable var17) { +// var8.addSuppressed(var17); +// } +// } else { +// os.close(); +// } +// } +// +// } +// +// } catch (Exception var20) { +// throw new IllegalArgumentException("Invalid file: " + var20, var20); +// } +// +// return new CommonsMultipartFile(fileItem); +// } + + private static MultipartFile construct(String fields, List> data, String fileName) { + + try (Workbook workbook = new XSSFWorkbook(); + ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + + Sheet sheet = workbook.createSheet(); + CellStyle cellStyle = createCellStyle(workbook); + Map fieldOrder = getFieldOrder(fields); + + // 头部占的行数 + int headerRows = calculateHeaderRows(fields); + // 创建一个新的单元格样式 + CellStyle cellStyle1 = createCellStyle(workbook); + // 创建一个字体 + Font font = workbook.createFont(); + font.setBold(true); // 设置加粗 + cellStyle1.setFont(font); + initHeader(sheet, cellStyle1, fields, headerRows); + fillData(workbook, sheet, cellStyle, data, fieldOrder, headerRows); + + // 写入输出流 + workbook.write(baos); + + // URL编码文件名(如果需要) + String encodedFileName; + try { + encodedFileName = URLEncoder.encode(fileName, "UTF-8"); + } catch (Exception e) { + encodedFileName = fileName; + } + + return new MockMultipartFile( + "file", + encodedFileName, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + baos.toByteArray() + ); + + } catch (IOException e) { + throw new RuntimeException("Excel文件生成失败", e); + } + } + + + + /** + * 初始化表头 + * @param sheet sheet页 + * @param cellStyle 单元格样式 + * @param fields 导出字段 + * @param headerRows 头部所占的行数 + */ + private static void initHeader(Sheet sheet, CellStyle cellStyle, String fields, int headerRows) { + // 构造表头 + Row row1 = sheet.createRow(0); + + Row row2 = null; + if (headerRows > 1) { + row2 = sheet.createRow(1); + } + + String[] field = fields.split(","); + // 上一个合并的表头名称 + String preMergeName = null; + // 合并的起止索引 + int startIdx = 0,endIdx = -1; + + for (int i = 0; i < field.length; i++) { + String[] f = field[i].split("\\|"); + Cell c1 = row1.createCell(i); + c1.setCellStyle(cellStyle); + c1.setCellValue(f[1]); + // 设置列宽(单位是字符宽度的1/256) + sheet.setColumnWidth(i, 5000); // 第一列宽度 + if (f.length > 2) { + Cell c2 = row2.createCell(i); + c2.setCellStyle(cellStyle); + c2.setCellValue(f[f.length - 1]); + + if (preMergeName == null) { + preMergeName = f[1]; + startIdx = i; + endIdx = startIdx; + } else if (preMergeName.equals(f[1])){ + endIdx++; + } else { + mergeRegion(sheet, 0, 0, startIdx, endIdx); + preMergeName = f[1]; + startIdx = i; + endIdx = startIdx; + } + } else { + if (headerRows > 1) { + mergeRegion(sheet, 0, 1, i, i); + } + + if (preMergeName != null) { + mergeRegion(sheet, 0, 0, startIdx, endIdx); + preMergeName = null; + } + } + + // 处理最后一次循环 + if (i == field.length -1) { + if (preMergeName != null) { + mergeRegion(sheet, 0, 0, startIdx, endIdx); + } + } + } + } + + /** + * 计算表头需要占用的行数 + * @param fields 导出字段 + * @return 表头需要占用的行数 + */ + private static int calculateHeaderRows(String fields) { + int rowMax = 0; + int rowCount = 0; + char[] chars = fields.toCharArray(); + for (char c : chars) { + if (c == '|') { + rowCount ++; + } else if (c == ',') { + if (rowCount > rowMax) { + rowMax = rowCount; + } + rowCount = 0; + } + } + + return rowMax; + } + + /** + * 填充数据 + * @param workbook workbook文档 + * @param sheet sheet页 + * @param cellStyle 单元格样式 + * @param data 待填充的数据 + * @param fieldOrder 字段填充顺序 + * @param headerRows 头部占的行数 + */ + private static void fillData(Workbook workbook, Sheet sheet, CellStyle cellStyle, List> data, Map fieldOrder, int headerRows) { + for (int i = 0; i < data.size(); i++) { + Row row = sheet.createRow(headerRows + i); + LinkedHashMap linkedHashMap = data.get(i); + data.get(i).forEach((key, value) -> { + // 为了保证只导出 fields 中的字段,这里必须加这个判断 + if (fieldOrder.get(key) != null) { + Cell cell = row.createCell(fieldOrder.get(key)); + cell.setCellStyle(cellStyle); + if (value != null) { + if (value instanceof Image) { + setCellPicture(workbook, sheet, cell, (Image) value); + } else if(value instanceof BigDecimal){ + BigDecimal decimal = (BigDecimal) value; + BigDecimal strippedValue = decimal.stripTrailingZeros(); + //去掉小数点后零后的值 + cell.setCellValue(strippedValue.toPlainString()); + } else { + cell.setCellValue(String.valueOf(value)); + } + } else { + cell.setCellValue(""); + } + } + }); + } + } + + /** + * 设置单元格图片(图片是悬浮的) + * @param workbook workbook文档 + * @param sheet sheet页 + * @param cell 单元格 + * @param image 图片 + */ + private static void setCellPicture(Workbook workbook, Sheet sheet, Cell cell, Image image) { + int rowIndex = cell.getRowIndex(); + int columnIndex = cell.getColumnIndex(); + // 绘图对象 + Drawing patriarch = sheet.createDrawingPatriarch(); + // 创建锚点 + XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, columnIndex, rowIndex, columnIndex + 1, rowIndex + 1); + patriarch.createPicture(anchor, workbook.addPicture(image.getImageData(), XSSFWorkbook.PICTURE_TYPE_JPEG)); + } + + /** + * 得到字段的行索引 + * @param fields 字段 + * @return 字段索引map + */ + private static Map getFieldOrder(String fields) { + Map fieldOrder = new HashMap<>(16); + + String[] field = fields.split(","); + for (int i = 0; i < field.length; ++i) { + String[] f = field[i].split("\\|"); + fieldOrder.put(f[0], i); + } + + return fieldOrder; + } + + /** + * 创建自定义单元格样式 + * @param workbook 工作簿 + */ + private static CellStyle createCellStyle(Workbook workbook) { + // 为单元格设置边框线 + CellStyle cellStyle = workbook.createCellStyle(); + cellStyle.setBorderTop(BorderStyle.THIN); + cellStyle.setBorderBottom(BorderStyle.THIN); + cellStyle.setBorderLeft(BorderStyle.THIN); + cellStyle.setBorderRight(BorderStyle.THIN); + // 居中显示 + cellStyle.setAlignment(HorizontalAlignment.CENTER); + cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); + // 设置单元格样式为文本格式 + DataFormat format = workbook.createDataFormat(); + cellStyle.setDataFormat(format.getFormat("@")); + return cellStyle; + } + + /** + * 合并区域 + * @param sheet sheet页 + * @param firstRow 起始行 + * @param lastRow 结束行 + * @param firstCol 起始列 + * @param lastCol 结束列 + */ + private static void mergeRegion(Sheet sheet,int firstRow, int lastRow, int firstCol, int lastCol) { + if (firstRow == lastRow && firstCol == lastCol) { + return; + } + + CellRangeAddress cra = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol); + sheet.addMergedRegion(cra); + // 为合并后的单元格添加边框线 + RegionUtil.setBorderTop(BorderStyle.THIN, cra, sheet); + RegionUtil.setBorderBottom(BorderStyle.THIN, cra, sheet); + RegionUtil.setBorderLeft(BorderStyle.THIN, cra, sheet); + RegionUtil.setBorderRight(BorderStyle.THIN, cra, sheet); + } + + /** + * 转换时间格式 + * @param data 导出数据 + * @param formatter 转换格式 formatter + */ + private static void convertDateToStr(List> data, String formatter) { + for (LinkedHashMap datum : data) { + for (Map.Entry stringObjectEntry : datum.entrySet()) { + String key = stringObjectEntry.getKey(); + Object value = stringObjectEntry.getValue(); + if (value != null) { + if (value instanceof Date) { + datum.put(key, DateFormatUtils.format((Date) value, formatter)); + } else if (value instanceof LocalDateTime) { + datum.put(key, ((LocalDateTime) value).format(DateTimeFormatter.ofPattern(formatter))); + } + } + } + } + } + + /** + * 集合对象转集合map + * @param tList + * @return + */ + public static List> listObjToListMap(List tList){ + List> mapList = new ArrayList<>(); + tList.forEach(obj -> { + ObjectMapper mapper = new ObjectMapper(); + LinkedHashMap personMap = mapper.convertValue(obj, LinkedHashMap.class); + mapList.add(personMap); + }); + return mapList; + } + + + public void exportTest(HttpServletResponse response) { + List< Object> mapList = new ArrayList<>(); + String exportFields = "date|时间,firstHeader|表头1,secondHeader|表头2," + + "sub001|编号-0001|子标题1,sub002|编号-0001|子标题2,sub003|编号-0001|子标题3,sub004|编号-0001|子标题4,remark|备注"; + ExcelUtils.export(response, "导出的文件名", mapList, exportFields, "yyyy-MM-dd HH:mm", false); + } +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/utils/GenericSumUtil.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/utils/GenericSumUtil.java new file mode 100644 index 0000000..5da25cb --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/utils/GenericSumUtil.java @@ -0,0 +1,81 @@ +package org.jeecg.payment.utils; + +import org.springframework.util.CollectionUtils; + +import java.lang.reflect.Field; +import java.math.BigDecimal; +import java.util.List; + +/** + * 通用数值字段求和工具类 + * 支持对任意对象列表中的BigDecimal类型字段进行累加求和 + * 返回与列表元素类型相同的汇总对象 + */ +public class GenericSumUtil { + + /** + * 对列表中所有对象的BigDecimal类型字段进行求和 + * @param list 待求和的对象列表 + * @param clazz 列表元素的类类型 + * @param 泛型类型 + * @return 汇总后的对象(与列表元素同类型,非数值字段保留默认值) + */ + public static T sumList(List list, Class clazz) { + // 处理空列表 + if (CollectionUtils.isEmpty(list)) { + return createEmptySumObject(clazz); + } + + try { + // 创建汇总结果对象 + T result = clazz.getDeclaredConstructor().newInstance(); + + // 获取所有BigDecimal类型的字段 + Field[] fields = clazz.getDeclaredFields(); + + // 遍历每个字段,对列表中所有对象的该字段进行累加 + for (Field field : fields) { + // 只处理BigDecimal类型的字段 + if (field.getType() == BigDecimal.class) { + field.setAccessible(true); // 允许访问私有字段 + + // 累加所有对象的该字段值 + BigDecimal sum = BigDecimal.ZERO; + for (T item : list) { + BigDecimal value = (BigDecimal) field.get(item); + sum = sum.add(value != null ? value : BigDecimal.ZERO); + } + + // 将累加结果设置到汇总对象 + field.set(result, sum); + } + } + + return result; + } catch (Exception e) { + throw new RuntimeException("对象求和失败: " + e.getMessage(), e); + } + } + + /** + * 创建空列表的默认汇总对象(所有BigDecimal字段设为0) + */ + private static T createEmptySumObject(Class clazz) { + try { + T result = clazz.getDeclaredConstructor().newInstance(); + + // 获取所有BigDecimal类型的字段并设置为0 + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + if (field.getType() == BigDecimal.class) { + field.setAccessible(true); + field.set(result, BigDecimal.ZERO); + } + } + + return result; + } catch (Exception e) { + throw new RuntimeException("创建空汇总对象失败: " + e.getMessage(), e); + } + } +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/utils/MealGroupingUtil.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/utils/MealGroupingUtil.java new file mode 100644 index 0000000..2c8dd5b --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/payment/utils/MealGroupingUtil.java @@ -0,0 +1,79 @@ +package org.jeecg.payment.utils; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +public class MealGroupingUtil { + // 定义默认餐次时间范围的常量(开始时间,结束时间,餐次名称) + private static final List DEFAULT_MEAL_TIMES; + private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss"); + + static { + // 初始化默认餐次时间范围 + DEFAULT_MEAL_TIMES = new ArrayList<>(); + try { + DEFAULT_MEAL_TIMES.add(new TimeRange(TIME_FORMAT.parse("06:00:00"), TIME_FORMAT.parse("09:30:00"), "早餐")); + DEFAULT_MEAL_TIMES.add(new TimeRange(TIME_FORMAT.parse("09:31:00"), TIME_FORMAT.parse("14:30:00"), "午餐")); + DEFAULT_MEAL_TIMES.add(new TimeRange(TIME_FORMAT.parse("14:31:00"), TIME_FORMAT.parse("20:00:00"), "晚餐")); + DEFAULT_MEAL_TIMES.add(new TimeRange(TIME_FORMAT.parse("20:01:00"), TIME_FORMAT.parse("05:59:00"), "加餐")); + } catch (ParseException e) { + throw new RuntimeException("初始化默认餐次时间失败", e); + } + } + + // 时间范围内部类 + private static class TimeRange { + Date startTime; + Date endTime; + String mealType; + + TimeRange(Date startTime, Date endTime, String mealType) { + this.startTime = startTime; + this.endTime = endTime; + this.mealType = mealType; + } + } + + /** + * 精确到秒的餐次判断,确保时间区间比较准确 + */ + public static String getDefaultMealType(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int totalSeconds = calendar.get(Calendar.HOUR_OF_DAY) * 3600 + + calendar.get(Calendar.MINUTE) * 60 + + calendar.get(Calendar.SECOND); + + for (TimeRange range : DEFAULT_MEAL_TIMES) { + int startSeconds = timeToSeconds(range.startTime); + int endSeconds = timeToSeconds(range.endTime); + + if ("加餐".equals(range.mealType)) { + if (totalSeconds >= startSeconds || totalSeconds <= endSeconds) { + return range.mealType; + } + } else { + // 正常时间范围:开始秒数 <= 当前秒数 <= 结束秒数 + if (totalSeconds >= startSeconds && totalSeconds <= endSeconds) { + return range.mealType; + } + } + } + return "未知餐次"; + } + + /** + * 将时间转换为当天的总秒数(0-86399),消除日期影响 + */ + private static int timeToSeconds(Date time) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(time); + return calendar.get(Calendar.HOUR_OF_DAY) * 3600 + + calendar.get(Calendar.MINUTE) * 60 + + calendar.get(Calendar.SECOND); + } +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/controller/SlBaseRestaurantController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/controller/SlBaseRestaurantController.java new file mode 100644 index 0000000..9d7755d --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/controller/SlBaseRestaurantController.java @@ -0,0 +1,88 @@ +package org.jeecg.restaurant.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.aspect.SendNotification; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.aspect.annotation.AutoLog; +import org.jeecg.common.system.base.controller.JeecgController; +import org.jeecg.restaurant.entity.SlBaseRestaurant; +import org.jeecg.restaurant.mapper.SlBaseRestaurantMapper; +import org.jeecg.restaurant.service.ISlBaseDinnerTimeService; +import org.jeecg.restaurant.service.ISlBaseRestaurantService; +import org.jeecg.restaurant.vo.SlBaseRestaurantVo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.ModelAndView; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * @Description: 食堂基本信息 + * @Author: jeecg-boot + * @Date: 2022-03-16 + * @Version: V1.0 + */ +@Tag(name = "食堂基本信息") +@RestController +@RequestMapping("/restaurant/slBaseRestaurant") +@Slf4j +public class SlBaseRestaurantController extends JeecgController { + @Autowired + private ISlBaseRestaurantService slBaseRestaurantService; + + + /** + * 编辑 + * + * @param slBaseRestaurant + * @return + */ + @AutoLog(value = "编辑") + @Operation(summary = "编辑", description= "编辑") + @RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST}) + @SendNotification() + public Result edit(@RequestBody SlBaseRestaurant slBaseRestaurant) { + slBaseRestaurant.setIsSync(0); + slBaseRestaurantService.updateById(slBaseRestaurant); + return Result.OK("编辑成功!"); + } + + /** + * 通过id查询 + * + * @param id + * @return + */ + //@AutoLog(value = "通过id查询") + @Operation(summary = "通过id查询", description= "通过id查询") + @GetMapping(value = "/queryById") + public Result queryById(@RequestParam(name = "id", required = true) String id) { + return slBaseRestaurantService.queryById(id); + } + + /** + * 添加 + * + * @param slBaseRestaurant + * @return + */ + @AutoLog(value = "添加或修改") + @Operation(summary = "添加或修改", description= "添加或修改") + @PostMapping(value = "/addOrEdit") + @SendNotification() + public Result addOrEdit(@RequestBody SlBaseRestaurantVo slBaseRestaurant) { + return slBaseRestaurantService.addOrEdit(slBaseRestaurant); + } +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/controller/StBaseRestaurantDeptController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/controller/StBaseRestaurantDeptController.java new file mode 100644 index 0000000..bd44fec --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/controller/StBaseRestaurantDeptController.java @@ -0,0 +1,59 @@ +package org.jeecg.restaurant.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.system.vo.ScreenVo; +import org.jeecg.restaurant.entity.SlBaseRestaurant; +import org.jeecg.restaurant.service.ISlBaseRestaurantService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.ArrayList; +import java.util.List; + +/** + * @Description: s + * @Author: yucl + * @Date: 2022-04-20 + * @Version: V1.0 + */ +@Slf4j +@Tag(name = "食堂部门") +@RestController +@RequestMapping("/restaurant/stBaseRestaurantDept") +public class StBaseRestaurantDeptController { + @Autowired + private ISlBaseRestaurantService slBaseRestaurantService; + + + //@AutoLog(value = "食堂id") + @Operation(summary = "获得食堂数据", description= "获得食堂数据") + @GetMapping(value = "/getRestaurantList") + public Result getRestaurantList() { + LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + List list = user.getCanteenIdList(); + if(CollectionUtils.isNotEmpty(list)){ + return Result.OK(list); + } else {//解决管理员(医生)角色,查看 营养监控 没有食堂列表问题 + List screenVoArrayList = new ArrayList<>(); + List slBaseRestaurantList = slBaseRestaurantService.list(); + if(CollectionUtils.isNotEmpty(slBaseRestaurantList)){ + slBaseRestaurantList.forEach(slBaseRestaurant -> { + if(!"0".equals(slBaseRestaurant.getId())){ + ScreenVo screenVo = new ScreenVo(); + screenVo.setId(slBaseRestaurant.getId()); + screenVo.setRestName(slBaseRestaurant.getRestName()); + screenVoArrayList.add(screenVo); + } + }); + } + return Result.OK(screenVoArrayList); + } + } + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/mapper/SlBaseDinnerTimeMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/mapper/SlBaseDinnerTimeMapper.java new file mode 100644 index 0000000..4c2b321 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/mapper/SlBaseDinnerTimeMapper.java @@ -0,0 +1,14 @@ +package org.jeecg.restaurant.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.jeecg.restaurant.entity.SlBaseDinnerTime; + +/** + * @Description: 食堂餐次定义 + * @Author: jeecg-boot + * @Date: 2022-03-17 + * @Version: V1.0 + */ +public interface SlBaseDinnerTimeMapper extends BaseMapper { + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/mapper/SlBaseRestaurantMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/mapper/SlBaseRestaurantMapper.java new file mode 100644 index 0000000..0e4680c --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/mapper/SlBaseRestaurantMapper.java @@ -0,0 +1,25 @@ +package org.jeecg.restaurant.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.jeecg.common.system.vo.ScreenVo; +import org.jeecg.restaurant.entity.SlBaseRestaurant; +import org.jeecg.restaurant.vo.SlBaseRestaurantVo; + +import java.util.List; +import java.util.Map; + +/** + * @Description: 食堂基本信息 + * @Author: jeecg-boot + * @Date: 2022-03-16 + * @Version: V1.0 + */ +public interface SlBaseRestaurantMapper extends BaseMapper { + + + List queryById(@Param("canteenId") String canteenId); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/mapper/xml/SlBaseDinnerTimeMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/mapper/xml/SlBaseDinnerTimeMapper.xml new file mode 100644 index 0000000..dd27fe7 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/mapper/xml/SlBaseDinnerTimeMapper.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/mapper/xml/SlBaseRestaurantMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/mapper/xml/SlBaseRestaurantMapper.xml new file mode 100644 index 0000000..d3abe77 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/mapper/xml/SlBaseRestaurantMapper.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/service/ISlBaseDinnerTimeService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/service/ISlBaseDinnerTimeService.java new file mode 100644 index 0000000..bc5c6ca --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/service/ISlBaseDinnerTimeService.java @@ -0,0 +1,19 @@ +package org.jeecg.restaurant.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.jeecg.restaurant.entity.SlBaseDinnerTime; + +import java.util.Date; + +/** + * @Description: 食堂餐次定义 + * @Author: jeecg-boot + * @Date: 2022-03-17 + * @Version: V1.0 + */ +public interface ISlBaseDinnerTimeService extends IService { + + SlBaseDinnerTime getCanteenDinnerType(String canteenId, Date dataTime); + + SlBaseDinnerTime getCanteenDinnerType(String canteenId); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/service/ISlBaseRestaurantService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/service/ISlBaseRestaurantService.java new file mode 100644 index 0000000..82b34fe --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/service/ISlBaseRestaurantService.java @@ -0,0 +1,27 @@ +package org.jeecg.restaurant.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.system.vo.ScreenVo; +import org.jeecg.restaurant.entity.SlBaseRestaurant; +import org.jeecg.restaurant.vo.*; + +import java.util.List; +import java.util.Map; + +/** + * @Description: 食堂基本信息 + * @Author: jeecg-boot + * @Date: 2022-03-16 + * @Version: V1.0 + */ +public interface ISlBaseRestaurantService extends IService { + + Result queryById(String canteenId); + + Result addOrEdit(SlBaseRestaurantVo slBaseRestaurant); + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/service/impl/SlBaseDinnerTimeServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/service/impl/SlBaseDinnerTimeServiceImpl.java new file mode 100644 index 0000000..b97dbc8 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/service/impl/SlBaseDinnerTimeServiceImpl.java @@ -0,0 +1,64 @@ +package org.jeecg.restaurant.service.impl; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.apache.commons.collections.CollectionUtils; +import org.jeecg.common.util.DateUtils; +import org.jeecg.restaurant.entity.SlBaseDinnerTime; +import org.jeecg.restaurant.mapper.SlBaseDinnerTimeMapper; +import org.jeecg.restaurant.service.ISlBaseDinnerTimeService; +import org.springframework.stereotype.Service; +import java.util.Date; +import java.util.List; + +/** + * @Description: 食堂餐次定义 + * @Author: jeecg-boot + * @Date: 2022-03-17 + * @Version: V1.0 + */ +@Service +public class SlBaseDinnerTimeServiceImpl extends ServiceImpl implements ISlBaseDinnerTimeService { + + + @Override + public SlBaseDinnerTime getCanteenDinnerType(String canteenId,Date dataTime) { + List list = this.list(new LambdaQueryWrapper() + .eq(SlBaseDinnerTime::getRestId, canteenId) + .orderByAsc(SlBaseDinnerTime::getEndTime)); + if (CollectionUtils.isNotEmpty(list)) { + for (SlBaseDinnerTime slBaseDinnerTime : list) { + DateTime endTime = DateUtil.parse(DateUtil.today() + " " + slBaseDinnerTime.getEndTime()); + Date date = new Date(); + if(null != dataTime){ + endTime = DateUtil.parse(DateUtils.formatDate(dataTime,"yyyy-MM-dd") + " " + slBaseDinnerTime.getEndTime()); + date = dataTime; + } + if (DateUtil.compare(date, endTime) <= 0) { + return slBaseDinnerTime; + } + } + return list.get(list.size() - 1); + } + return null; + } + + @Override + public SlBaseDinnerTime getCanteenDinnerType(String canteenId) { + List list = this.list(new LambdaQueryWrapper() + .eq(SlBaseDinnerTime::getRestId, canteenId) + .orderByAsc(SlBaseDinnerTime::getEndTime)); + if (CollectionUtils.isNotEmpty(list)) { + for (SlBaseDinnerTime slBaseDinnerTime : list) { + DateTime endTime = DateUtil.parse(DateUtil.today() + " " + slBaseDinnerTime.getEndTime()); + if (DateUtil.compare(new Date(), endTime) <= 0) { + return slBaseDinnerTime; + } + } + return list.get(list.size() - 1); + } + return null; + } +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/service/impl/SlBaseRestaurantServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/service/impl/SlBaseRestaurantServiceImpl.java new file mode 100644 index 0000000..d0da243 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/restaurant/service/impl/SlBaseRestaurantServiceImpl.java @@ -0,0 +1,104 @@ +package org.jeecg.restaurant.service.impl; + + +import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.system.vo.ScreenVo; +import org.jeecg.common.util.DateUtils; +import org.jeecg.common.util.PasswordUtil; +import org.jeecg.common.util.RedisUtil; +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.restaurant.entity.SlBaseDinnerTime; +import org.jeecg.restaurant.entity.SlBaseRestaurant; +import org.jeecg.restaurant.mapper.SlBaseRestaurantMapper; +import org.jeecg.restaurant.service.ISlBaseDinnerTimeService; +import org.jeecg.restaurant.service.ISlBaseRestaurantService; +import org.jeecg.restaurant.vo.SlBaseRestaurantVo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @Description: 食堂基本信息 + * @Author: jeecg-boot + * @Date: 2022-03-16 + * @Version: V1.0 + */ +@Service +@Slf4j +public class SlBaseRestaurantServiceImpl extends ServiceImpl implements ISlBaseRestaurantService { + + @Autowired + private ISlBaseDinnerTimeService stBaseDinnerTimeService; + @Resource + private SlBaseRestaurantMapper stBaseRestaurantMapper; + + @Autowired + private RedisUtil redisUtil; + @Override + public Result queryById(String canteenId) { + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + if (sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_4) || sysUser.getUserIdentity().equals(CommonConstant.USER_IDENTITY_5)) { + canteenId = sysUser.getCanteenId(); + } + List slBaseRestaurantVoList = stBaseRestaurantMapper.queryById(canteenId); + SlBaseRestaurantVo slBaseRestaurantVo = new SlBaseRestaurantVo(); + if (CollectionUtil.isNotEmpty(slBaseRestaurantVoList)) { + slBaseRestaurantVo = slBaseRestaurantVoList.get(0); + } + + List dinnerTimeList = stBaseDinnerTimeService.list(new LambdaQueryWrapper().eq(SlBaseDinnerTime::getRestId, canteenId)); + + if (CollectionUtils.isEmpty(dinnerTimeList)) { + List dinnerTypeList = Arrays.asList("早餐", "午餐", "晚餐", "加餐"); + for (String dinnerType : dinnerTypeList) { + SlBaseDinnerTime dinnerTime = new SlBaseDinnerTime(); + dinnerTime.setStartTime(""); + dinnerTime.setDinnerType(dinnerType); + dinnerTime.setEndTime(""); + dinnerTime.setRestId(canteenId); + dinnerTimeList.add(dinnerTime); + } + } + slBaseRestaurantVo.setSlBaseDinnerTimeList(dinnerTimeList); + return Result.OK(slBaseRestaurantVo); + } + + @Override + public Result addOrEdit(SlBaseRestaurantVo restaurantVo) { + if (restaurantVo.getId() != null) { + restaurantVo.setIsSync(0); + this.updateById(restaurantVo); +// List slBaseDinnerTimeList = stBaseDinnerTimeService.list(new LambdaQueryWrapper().eq(SlBaseDinnerTime::getRestId, restaurantVo.getId())); + //删除对应的餐次定义 + stBaseDinnerTimeService.remove(new LambdaQueryWrapper().eq(SlBaseDinnerTime::getRestId, restaurantVo.getId())); + } + + //餐次定义 + if (CollectionUtils.isNotEmpty(restaurantVo.getSlBaseDinnerTimeList())) { + for (SlBaseDinnerTime slBaseDinnerTime : restaurantVo.getSlBaseDinnerTimeList()) { + slBaseDinnerTime.setId(null); + slBaseDinnerTime.setRestId(restaurantVo.getId()); + slBaseDinnerTime.setIsSync(0); + stBaseDinnerTimeService.save(slBaseDinnerTime); + } + } + + return Result.OK("操作成功!"); + } + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/controller/CommonController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/controller/CommonController.java new file mode 100644 index 0000000..9cecec7 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/controller/CommonController.java @@ -0,0 +1,517 @@ +package org.jeecg.system.controller; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.xkcoding.http.util.StringUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FilenameUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.exception.JeecgBootException; +import org.jeecg.common.util.*; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.http.server.ServletServerHttpRequest; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.FileCopyUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; +import org.springframework.web.servlet.HandlerMapping; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLDecoder; +import java.net.URLEncoder; + +/** + *

+ * 用户表 前端控制器 + *

+ * + * @Author scott + * @since 2018-12-20 + */ +@Slf4j +@RestController +@RequestMapping("/sys/common") +public class CommonController { + + + + /** + * 文件上传根目录 设置(这里设置的路径要和file-server.application-dev.yml的file.path保持一致) + * @return + */ + @Value(value = "${jeecg.path.upload}") + private String uploadpath; + + /** + * 本地:local minio:minio 阿里:alioss + */ + @Value(value="${jeecg.uploadType}") + private String uploadType; + + + /** + * 服务部署的外网ip端口、域名地址:用于下载 + */ + @Value(value="${jeecg.serverPublicIp.ip}") + private String serverPublicIp; + + /** + * @Author 政辉 + * @return + */ + @GetMapping("/403") + public Result noauth() { + return Result.error("没有权限,请联系管理员授权"); + } + + /** + * 文件上传统一方法 + * @param request + * @param response + * @return + */ + @PostMapping(value = "/upload") + public Result upload(HttpServletRequest request, HttpServletResponse response) { + Result result = new Result<>(); + String savePath = ""; + String bizPath = request.getParameter("biz"); + boolean needThumbnailFlag = true;//默认改成需要上传缩略图 +// if (request.getParameter("needThumbnail") != null && request.getParameter("needThumbnail").equals("1")){ +// needThumbnailFlag = true; +// } + + //LOWCOD-2580 sys/common/upload接口存在任意文件上传漏洞 + if (oConvertUtils.isNotEmpty(bizPath) && (bizPath.contains("../") || bizPath.contains("..\\"))) { + throw new JeecgBootException("上传目录bizPath,格式非法!"); + } + + MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; + MultipartFile file = multipartRequest.getFile("file");// 获取上传文件对象 + if(request.getParameter("faceFile") != null && "1".equals(request.getParameter("faceFile")) && file != null){//加参数 faceFile = 1 表示人脸图片上传场景 + long fileSize = file.getSize(); // 获取文件大小 + // 设定上传文件的最大大小,例如1MB + long maxSize = 2; + long size = 1024 * 1024; + long maxSizeValue = maxSize * size; + if (fileSize > maxSizeValue) { + String msg = "文件超过了允许的"+maxSize+ "M大小限制。"; + System.out.println(msg); + result.setMessage(msg); + result.setSuccess(false); + return result; + } + } + if(oConvertUtils.isEmpty(bizPath)){ + if(CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)){ + //未指定目录,则用阿里云默认目录 upload + bizPath = "upload"; + //result.setMessage("使用阿里云文件上传时,必须添加目录!"); + //result.setSuccess(false); + //return result; + }else{ + bizPath = ""; + } + } + if(CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)){ + //update-begin-author:lvdandan date:20200928 for:修改JEditor编辑器本地上传 + + try { + String fileName = file.getOriginalFilename();// 获取文件名 + savePath = LocalUploadUtil.uploadLocal(file.getInputStream(), bizPath, fileName); +// savePath = this.uploadLocal(file,bizPath); + }catch (Exception e){ + e.printStackTrace(); + } + + }else{ + //update-begin-author:taoyan date:20200814 for:文件上传改造 + savePath = CommonUtils.upload(file, bizPath, uploadType, needThumbnailFlag); + //update-end-author:taoyan date:20200814 for:文件上传改造 + } + if(oConvertUtils.isNotEmpty(savePath)){ + return Result.OK(savePath,FileShowUtil.getShowUrl(savePath)); + }else { + result.setMessage("上传失败!"); + result.setSuccess(false); + } + return result; + } + +// public String getShowUrl(String path) { +// if (StringUtil.isEmpty(path)) { +// return serverPublicIp+"/sys/common/static/default.png"; +// } +// if(path.contains("https")){ +// return path; +// } +// path = path.replaceAll("\\\\","/"); +// return serverPublicIp+"/sys/common/static/static/" + path; +// } + + + /** + * 文件上传统一方法(上传文件 固定目录 带缩略图(同名覆盖)) + * + * @return + */ + @PostMapping(value = "/upload/fixedDirectory") + public Result uploadFixedDirectory(HttpServletRequest request, HttpServletResponse response) { + Result result = new Result<>(); + String savePath = ""; + String bizPath = request.getParameter("biz"); + boolean needThumbnailFlag = true;//默认改成需要上传缩略图 + //LOWCOD-2580 sys/common/upload接口存在任意文件上传漏洞 + if (oConvertUtils.isNotEmpty(bizPath) && (bizPath.contains("../") || bizPath.contains("..\\"))) { + throw new JeecgBootException("上传目录bizPath,格式非法!"); + } + + MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; + MultipartFile file = multipartRequest.getFile("file");// 获取上传文件对象 + if(request.getParameter("faceFile") != null && "1".equals(request.getParameter("faceFile")) && file != null){//加参数 faceFile = 1 表示人脸图片上传场景 + long fileSize = file.getSize(); // 获取文件大小 + // 设定上传文件的最大大小,例如1MB + long maxSize = 2; + long size = 1024 * 1024; + long maxSizeValue = maxSize * size; + if (fileSize > maxSizeValue) { + String msg = "文件超过了允许的"+maxSize+ "M大小限制。"; + System.out.println(msg); + result.setMessage(msg); + result.setSuccess(false); + return result; + } + } + if(oConvertUtils.isEmpty(bizPath)){ + if(CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)){ + //未指定目录,则用阿里云默认目录 upload + bizPath = "upload"; + //result.setMessage("使用阿里云文件上传时,必须添加目录!"); + //result.setSuccess(false); + //return result; + }else{ + bizPath = ""; + } + } + if(CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)){ + //update-begin-author:lvdandan date:20200928 for:修改JEditor编辑器本地上传 + savePath = this.uploadLocal(file,bizPath); + //update-begin-author:lvdandan date:20200928 for:修改JEditor编辑器本地上传 + /** 富文本编辑器及markdown本地上传时,采用返回链接方式 + //针对jeditor编辑器如何使 lcaol模式,采用 base64格式存储 + String jeditor = request.getParameter("jeditor"); + if(oConvertUtils.isNotEmpty(jeditor)){ + result.setMessage(CommonConstant.UPLOAD_TYPE_LOCAL); + result.setSuccess(true); + return result; + }else{ + savePath = this.uploadLocal(file,bizPath); + } + */ + }else{ + //update-begin-author:taoyan date:20200814 for:文件上传改造 + savePath = CommonUtils.uploadFixedDirectory(file, bizPath, uploadType, needThumbnailFlag); + //update-end-author:taoyan date:20200814 for:文件上传改造 + } + if(oConvertUtils.isNotEmpty(savePath)){ + return Result.OK(savePath,FileShowUtil.getShowUrl(savePath)); + }else { + result.setMessage("上传失败!"); + result.setSuccess(false); + } + return result; + } + + /** + * 本地文件上传 + * @param mf 文件 + * @param bizPath 自定义路径 + * @return + */ + private String uploadLocal(MultipartFile mf,String bizPath){ + try { + String ctxPath = uploadpath; + String fileName = null; + //解决路径遍历漏洞-路径:高危 +// File file = new File(ctxPath + File.separator + bizPath + File.separator ); + String paths = ctxPath + File.separator + bizPath + File.separator; + String pathNew= FilenameUtils.getFullPath(paths);// 输出: C:\Users\Example\Documents\ + System.out.println(pathNew); + File file = new File(XssUtils.checkTraversal(pathNew)); + if (!file.exists()) { + file.mkdirs();// 创建文件根目录 + } + String orgName = mf.getOriginalFilename();// 获取文件名 + orgName = CommonUtils.getFileName(orgName); + if(orgName.indexOf(".")!=-1){ + fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf(".")); + }else{ + fileName = orgName+ "_" + System.currentTimeMillis(); + } + //解决路径遍历漏洞-路径:高危 +// String savePath = file.getPath() + File.separator + fileName; + fileName = FilenameUtils.getName(fileName);//// 输出: file.txt + String normalized = pathNew.replace("\\", "/"); + String path = XssUtils.checkTraversal(normalized); + String savePath = path + fileName; + File savefile = new File(savePath); + FileCopyUtils.copy(mf.getBytes(), savefile); + String dbpath = null; + if(oConvertUtils.isNotEmpty(bizPath)){ + dbpath = bizPath + File.separator + fileName; + }else{ + dbpath = fileName; + } + if (dbpath.contains("\\")) { + dbpath = dbpath.replace("\\", "/"); + } + return dbpath; + } catch (IOException e) { + log.error(e.getMessage(), e); + } + return ""; + } + +// @PostMapping(value = "/upload2") +// public Result upload2(HttpServletRequest request, HttpServletResponse response) { +// Result result = new Result<>(); +// try { +// String ctxPath = uploadpath; +// String fileName = null; +// String bizPath = "files"; +// String tempBizPath = request.getParameter("biz"); +// if(oConvertUtils.isNotEmpty(tempBizPath)){ +// bizPath = tempBizPath; +// } +// String nowday = new SimpleDateFormat("yyyyMMdd").format(new Date()); +// File file = new File(ctxPath + File.separator + bizPath + File.separator + nowday); +// if (!file.exists()) { +// file.mkdirs();// 创建文件根目录 +// } +// MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; +// MultipartFile mf = multipartRequest.getFile("file");// 获取上传文件对象 +// String orgName = mf.getOriginalFilename();// 获取文件名 +// fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.indexOf(".")); +// String savePath = file.getPath() + File.separator + fileName; +// File savefile = new File(savePath); +// FileCopyUtils.copy(mf.getBytes(), savefile); +// String dbpath = bizPath + File.separator + nowday + File.separator + fileName; +// if (dbpath.contains("\\")) { +// dbpath = dbpath.replace("\\", "/"); +// } +// result.setMessage(dbpath); +// result.setSuccess(true); +// } catch (IOException e) { +// result.setSuccess(false); +// result.setMessage(e.getMessage()); +// log.error(e.getMessage(), e); +// } +// return result; +// } + + /** + * 预览图片&下载文件 + * 请求地址:http://localhost:8080/common/static/{user/20190119/e1fe9925bc315c60addea1b98eb1cb1349547719_1547866868179.jpg} + * + * @param request + * @param response + */ + @GetMapping(value = "/static/**") + public void view(HttpServletRequest request, HttpServletResponse response) { + // ISO-8859-1 ==> UTF-8 进行编码转换 + String imgPath = extractPathFromPattern(request); + if(oConvertUtils.isEmpty(imgPath) || imgPath=="null"){ + return; + } + // 其余处理略 + InputStream inputStream = null; + OutputStream outputStream = null; + try { + imgPath = imgPath.replace("..", "").replace("../",""); + if (imgPath.endsWith(",")) { + imgPath = imgPath.substring(0, imgPath.length() - 1); + } + String path = uploadpath + File.separator + imgPath; + //解决路径遍历漏洞-路径:高危 + String filePath = FilenameUtils.getFullPath(path); + File file = new File(XssUtils.checkTraversal(filePath)); + if(!file.exists()){ + response.setStatus(404); + throw new RuntimeException("文件["+imgPath+"]不存在.."); + } + response.setContentType("application/force-download");// 设置强制下载不打开 + //代码注入-HTTP响应截断:中危 + String fileName = URLEncoder.encode(file.getName(), "UTF-8"); + response.addHeader("Content-Disposition", "attachment;fileName=" + fileName); +// response.addHeader("Content-Disposition", "attachment;fileName=" + new String(file.getName().getBytes("UTF-8"),"iso-8859-1")); + inputStream = new BufferedInputStream(new FileInputStream(filePath)); + outputStream = response.getOutputStream(); + byte[] buf = new byte[1024]; + int len; + while ((len = inputStream.read(buf)) > 0) { + outputStream.write(buf, 0, len); + } + response.flushBuffer(); + } catch (IOException e) { + log.error("预览文件失败" + e.getMessage()); + response.setStatus(404); + e.printStackTrace(); + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + + } + +// /** +// * 下载文件 +// * 请求地址:http://localhost:8080/common/download/{user/20190119/e1fe9925bc315c60addea1b98eb1cb1349547719_1547866868179.jpg} +// * +// * @param request +// * @param response +// * @throws Exception +// */ +// @GetMapping(value = "/download/**") +// public void download(HttpServletRequest request, HttpServletResponse response) throws Exception { +// // ISO-8859-1 ==> UTF-8 进行编码转换 +// String filePath = extractPathFromPattern(request); +// // 其余处理略 +// InputStream inputStream = null; +// OutputStream outputStream = null; +// try { +// filePath = filePath.replace("..", ""); +// if (filePath.endsWith(",")) { +// filePath = filePath.substring(0, filePath.length() - 1); +// } +// String localPath = uploadpath; +// String downloadFilePath = localPath + File.separator + filePath; +// File file = new File(downloadFilePath); +// if (file.exists()) { +// response.setContentType("application/force-download");// 设置强制下载不打开             +// response.addHeader("Content-Disposition", "attachment;fileName=" + new String(file.getName().getBytes("UTF-8"),"iso-8859-1")); +// inputStream = new BufferedInputStream(new FileInputStream(file)); +// outputStream = response.getOutputStream(); +// byte[] buf = new byte[1024]; +// int len; +// while ((len = inputStream.read(buf)) > 0) { +// outputStream.write(buf, 0, len); +// } +// response.flushBuffer(); +// } +// +// } catch (Exception e) { +// log.info("文件下载失败" + e.getMessage()); +// // e.printStackTrace(); +// } finally { +// if (inputStream != null) { +// try { +// inputStream.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// if (outputStream != null) { +// try { +// outputStream.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// } +// +// } + + /** + * @功能:pdf预览Iframe + * @param modelAndView + * @return + */ + @RequestMapping("/pdf/pdfPreviewIframe") + public ModelAndView pdfPreviewIframe(ModelAndView modelAndView) { + modelAndView.setViewName("pdfPreviewIframe"); + return modelAndView; + } + + /** + * 把指定URL后的字符串全部截断当成参数 + * 这么做是为了防止URL中包含中文或者特殊字符(/等)时,匹配不了的问题 + * @param request + * @return + */ + private static String extractPathFromPattern(final HttpServletRequest request) { + String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE); + String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); + return new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, path); + } + + /** + * 中转HTTP请求,解决跨域问题 + * + * @param url 必填:请求地址 + * @return + */ + @RequestMapping("/transitRESTful") + public Result transitRESTful(@RequestParam("url") String url, HttpServletRequest request) { + try { + ServletServerHttpRequest httpRequest = new ServletServerHttpRequest(request); + // 中转请求method、body + HttpMethod method = httpRequest.getMethod(); + JSONObject params; + try { + params = JSON.parseObject(JSON.toJSONString(httpRequest.getBody())); + } catch (Exception e) { + params = new JSONObject(); + } + // 中转请求问号参数 + JSONObject variables = JSON.parseObject(JSON.toJSONString(request.getParameterMap())); + variables.remove("url"); + // 在 headers 里传递Token + String token = TokenUtils.getTokenByRequest(request); + HttpHeaders headers = new HttpHeaders(); + //代码注入-HTTP响应截断:中危 + token = URLEncoder.encode(token, "UTF-8"); + headers.set("X-Access-Token", token); + // 发送请求 + String httpURL = URLDecoder.decode(url, "UTF-8"); + ResponseEntity response = RestUtil.request(httpURL, method, headers , variables, params, String.class); + // 封装返回结果 + Result result = new Result<>(); + int statusCode = response.getStatusCodeValue(); + result.setCode(statusCode); + result.setSuccess(statusCode == 200); + String responseBody = response.getBody(); + try { + // 尝试将返回结果转为JSON + Object json = JSON.parse(responseBody); + result.setResult(json); + } catch (Exception e) { + // 转成JSON失败,直接返回原始数据 + result.setResult(responseBody); + } + return result; + } catch (Exception e) { + log.debug("中转HTTP请求失败", e); + return Result.error(e.getMessage()); + } + } + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/controller/LoginController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/controller/LoginController.java new file mode 100644 index 0000000..cc9c541 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/controller/LoginController.java @@ -0,0 +1,262 @@ +package org.jeecg.system.controller; + + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.RandomUtil; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.constant.CacheConstant; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.system.util.JwtUtil; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.system.vo.ScreenVo; +import org.jeecg.common.util.*; +import org.jeecg.modules.base.service.BaseCommonService; +import org.jeecg.system.dto.SysLoginModel; +import org.jeecg.system.service.ISysUserScreenService; +import org.jeecg.system.service.ISysUserService; +import org.jeecg.user.entity.SysUser; +import org.jeecg.user.entity.SysUserScreen; +import org.jeecg.user.vo.SysUserVo; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.*; + +/** + * @Author scott + * @since 2018-12-17 + */ +@RestController +@RequestMapping("/sys") +@Tag(name = "用户登录") +@Slf4j +public class LoginController { + @Autowired + private ISysUserService sysUserService; + @Autowired + private RedisUtil redisUtil; + @Resource + private BaseCommonService baseCommonService; + @Resource + private ISysUserScreenService sysUserScreenService; + + @Operation(summary="登录接口") + @RequestMapping(value = "/login", method = RequestMethod.POST) + public Result login(@RequestBody SysLoginModel sysLoginModel) { + Result result = new Result(); + String username = sysLoginModel.getUsername(); + String password = sysLoginModel.getPassword(); + + //进行密码解密逻辑 + if(StringUtils.isNotBlank(sysLoginModel.getTimes())){ + String passwordEncode = sysLoginModel.getPassword(); + String times = sysLoginModel.getTimes(); + String passwordDecode = Base64Util.decode(passwordEncode); + password = passwordDecode.replace(times,""); + } + + //update-begin-author:taoyan date:20190828 for:校验验证码 + String captcha = sysLoginModel.getCaptcha(); + if (captcha == null) { + result.error500("验证码无效"); + return result; + } + + String lowerCaseCaptcha = captcha.toLowerCase(); + String realKey = MD5Util.MD5Encode(lowerCaseCaptcha + sysLoginModel.getCheckKey(), "utf-8"); + Object checkCode = redisUtil.get(realKey); + //当进入登录页时,有一定几率出现验证码错误 #1714 + if (checkCode == null || !checkCode.toString().equals(lowerCaseCaptcha)) { + log.warn("验证码错误,key= {} , Ui checkCode= {}, Redis checkCode = {}", sysLoginModel.getCheckKey(), lowerCaseCaptcha, checkCode); + result.error500("验证码错误"); + return result; + } + + //1. 校验用户是否有效 + //update-begin-author:wangshuai date:20200601 for: 登录代码验证用户是否注销bug,if条件永远为false + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysUser::getUsername, username); + + // 2.username可能重复,所以查出来list挨个匹配 + List sysUserList = new ArrayList<>(); + + SysUser sysUser = null; + if (StringUtils.equals(CommonConstant.SYSTEM_NAME, username)) { + sysUser = sysUserService.getOne(new LambdaQueryWrapper().eq(SysUser::getUsername, "system")); + sysUserList.add(sysUser); + } else { + sysUserList = sysUserService.list(queryWrapper); + } + // 用户名或密码错误错误标记 + boolean errorFlag = false; + boolean adminFlag = false;//员工(就餐人员))不能登录管理后台 + if (CollectionUtils.isNotEmpty(sysUserList)) { + for (SysUser user : sysUserList) { + //3. 校验用户名或密码是否正确 + String userpassword = PasswordUtil.encrypt(username, password, user.getSalt()); + String syspassword = user.getPassword(); + if (syspassword.equals(userpassword)) { + if (CommonConstant.USER_IDENTITY_6.equals(user.getUserIdentity())) { + adminFlag = true; + break; + } + errorFlag = true; + sysUser = user; + break; + } + } + } + if (adminFlag) { + result.error500("请使用管理员账号登录!"); + return result; + } + if (!errorFlag) { + result.error500("用户名或密码错误"); + return result; + } + + // 登录增加校验,食堂管理员登录需判断是否还有关联食堂。 + if (null != sysUser && null != sysUser.getUserIdentity() && CommonConstant.USER_IDENTITY_4.equals(sysUser.getUserIdentity())) { + //查找食堂id + SysUserScreen sysUserScreen = sysUserScreenService.getOne(new LambdaQueryWrapper().eq(SysUserScreen::getUserId, sysUser.getId())); + if (ObjectUtils.isEmpty(sysUserScreen)) { + result.error500("请检查该账户对应的食堂是否存在"); + return result; + } + } + //update-end-author:wangshuai date:20200601 for: 登录代码验证用户是否注销bug,if条件永远为false + result = sysUserService.checkUserIsEffective(sysUser); + if (!result.isSuccess()) { + return result; + } + //用户登录信息 + userInfo(sysUser, result); + //update-begin--Author:liusq Date:20210126 for:登录成功,删除redis中的验证码 + redisUtil.del(realKey); + //update-begin--Author:liusq Date:20210126 for:登录成功,删除redis中的验证码 + LoginUser loginUser = new LoginUser(); + BeanUtils.copyProperties(sysUser, loginUser); + loginUser.setSysOrgCode(sysUser.getSysOrgCode()); + baseCommonService.addLog("用户名: " + username + ",登录成功!", CommonConstant.LOG_TYPE_1, null, loginUser); + //update-end--Author:wangshuai Date:20200714 for:登录日志没有记录人员 + return result; + } + + + /** + * 用户信息 + * + * @param sysUser + * @param result + * @return + */ + private Result userInfo(SysUser sysUser, Result result) { + String syspassword = sysUser.getPassword(); + String userId = sysUser.getId(); + // 生成token + String token = JwtUtil.sign(userId, syspassword); + // 设置token缓存有效时间 +// redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + username, token); + //修改为hash方式存取 + redisUtil.hset(CommonConstant.USER_TOKEN, CommonConstant.PREFIX_USER_TOKEN + userId, token, JwtUtil.EXPIRE_TIME); + JSONObject obj = new JSONObject(); +// redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + username, JwtUtil.EXPIRE_TIME); + //登录就拿到用户的食堂数据 + SysUserVo vo = new SysUserVo(); + BeanUtils.copyProperties(sysUser, vo); + List screenVoList = sysUserService.listScreenByUser(sysUser); + if (CollectionUtil.isNotEmpty(screenVoList)) { + vo.setCanteenId(screenVoList.get(0).getId()); + } + + obj.put("token", token); + obj.put("userInfo", vo); + result.setResult(obj); + result.success("登录成功"); + return result; + } + + + /** + * 后台生成图形验证码 :有效 + * + * @param response + * @param key + */ + @Operation(summary="获取验证码") + @GetMapping(value = "/randomImage/{key}") + public Result randomImage(HttpServletResponse response, @PathVariable String key) { + Result res = new Result(); + try { + //生成验证码 + final String BASE_CHECK_CODES = "qwertyuiplkjhgfdsazxcvbnmQWERTYUPLKJHGFDSAZXCVBNM1234567890"; + String code = RandomUtil.randomString(BASE_CHECK_CODES, 4); + + //存到redis中 + String lowerCaseCode = code.toLowerCase(); + String realKey = MD5Util.MD5Encode(lowerCaseCode + key, "utf-8"); + log.info("获取验证码,Redis checkCode = {},key = {}", code, key); + redisUtil.set(realKey, lowerCaseCode, 60); + + //返回前端 + String base64 = RandImageUtil.generate(code); +// res.setSuccess(true); +// res.setResult(base64); + return Result.OK(base64); + } catch (Exception e) { + res.error500("获取验证码出错" + e.getMessage()); + e.printStackTrace(); + } + return res; + } + + + /** + * 退出登录 + * + * @param request + * @param response + * @return + */ + @RequestMapping(value = "/logout") + public Result logout(HttpServletRequest request, HttpServletResponse response) { + //用户退出逻辑 + String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); + if (oConvertUtils.isEmpty(token)) { + return Result.error("退出登录失败!"); + } + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + if (sysUser != null) { + //update-begin--Author:wangshuai Date:20200714 for:登出日志没有记录人员 + baseCommonService.addLog("用户名: " + sysUser.getRealname() + ",退出成功!", CommonConstant.LOG_TYPE_1, null, sysUser); + //update-end--Author:wangshuai Date:20200714 for:登出日志没有记录人员 + log.info(" 用户名: " + sysUser.getRealname() + ",退出成功! "); + //清空用户登录Token缓存 + redisUtil.hdel(CommonConstant.USER_TOKEN, CommonConstant.PREFIX_USER_TOKEN + sysUser.getId()); + //清空用户登录Shiro权限缓存 + redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + sysUser.getId()); + //清空用户的缓存信息(包括部门信息),例如sys:cache:user:: + redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, sysUser.getId())); + //调用shiro的logout + SecurityUtils.getSubject().logout(); + return Result.ok("退出登录成功!"); + } else { + return Result.error("Token无效!"); + } + } + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/controller/SwUserFaceimgController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/controller/SwUserFaceimgController.java new file mode 100644 index 0000000..dfefaf4 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/controller/SwUserFaceimgController.java @@ -0,0 +1,679 @@ +package org.jeecg.system.controller; + +import cn.hutool.core.img.ImgUtil; +import cn.hutool.http.HttpDownloader; +import com.arcsoft.face.FaceInfo; +import com.arcsoft.face.toolkit.ImageFactory; +import com.arcsoft.face.toolkit.ImageInfo; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.aspect.SendNotification; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.aspect.annotation.AutoLog; +import org.jeecg.common.system.base.controller.JeecgController; +import org.jeecg.common.util.Base64Util; +import org.jeecg.common.util.FileShowUtil; +import org.jeecg.emp.entity.CqBaseEmployee; +import org.jeecg.emp.service.ICqBaseEmployeeService; +import org.jeecg.face.service.FaceEngineService; +import org.jeecg.system.mapper.SwUserFaceimgMapper; +import org.jeecg.system.service.ISwUserFaceimgService; +import org.jeecg.system.service.ISwUserFaceimgSubService; +import org.jeecg.system.service.UserFaceCacheService; +import org.jeecg.user.entity.SwUserFaceimg; +import org.jeecg.user.entity.SwUserFaceimgSub; +import org.jeecg.user.vo.SwUserFaceimgWeightScaleV2Vo; +import org.jeecg.user.vo.UserFaceInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.ModelAndView; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @Description: 用户人脸数据 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +@Tag(name= "用户人脸数据") +@RestController +@RequestMapping("/userface/swUserFaceimg") +@Slf4j +public class SwUserFaceimgController extends JeecgController { + @Autowired + private ISwUserFaceimgService swUserFaceimgService; + @Autowired + private FaceEngineService faceEngineService; + @Autowired + private UserFaceCacheService userFaceCacheService; + @Resource + private SwUserFaceimgMapper swUserFaceimgMapper; + @Autowired + private ICqBaseEmployeeService iCqBaseEmployeeService; + @Resource + private ISwUserFaceimgSubService swUserFaceimgSubService; + + + /** + * 通过id查询 + * + * @param id + * @return + */ + //@AutoLog(value = "用户人脸数据-通过id查询") + @Operation(summary = "用户人脸数据-通过id查询", description = "用户人脸数据-通过id查询") + @GetMapping(value = "/queryById") + public Result queryById(@RequestParam(name = "id", required = true) String id) { + SwUserFaceimg swUserFaceimg = swUserFaceimgService.getById(id); + if (swUserFaceimg == null) { + return Result.error("未找到对应数据", null); + } + return Result.OK(swUserFaceimg); + } + + /** + * 导出excel + * + * @param request + * @param swUserFaceimg + */ + @RequestMapping(value = "/exportXls") + public ModelAndView exportXls(HttpServletRequest request, SwUserFaceimg swUserFaceimg) { + return super.exportXls(request, swUserFaceimg, SwUserFaceimg.class, "用户人脸数据"); + } + + /** + * 通过excel导入数据 + * + * @param request + * @param response + * @return + */ + @RequestMapping(value = "/importExcel", method = RequestMethod.POST) + public Result importExcel(HttpServletRequest request, HttpServletResponse response) { + return super.importExcel(request, response, SwUserFaceimg.class); + } + + @AutoLog(value = "新增人脸信息") + @Operation(summary = "新增人脸信息", description = "新增人脸信息") + @PostMapping(value = "/saveFaceImg") + @SendNotification() + public Result saveFaceImg(@RequestBody Map map) { + if(!map.containsKey("userType")){ + map.put("userType",0); + } + //用户类型(0: 正式员工 1:非正式员工,2:家庭用户) + Integer userType = (Integer)map.get("userType"); + if(userType != 0){ + //非正式员工 + return saveSubFaceImg(map); + } + //正式员工 + String userId = map.get("userId").toString(); + String imgSrc = map.get("imgSrc").toString(); + if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(imgSrc)) { + return Result.error("参数有误"); + } + if(map.containsKey("face")){ + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(SwUserFaceimg::getUserId, userId); + lambdaQueryWrapper.isNotNull(SwUserFaceimg::getFaceFeatureData); + List list = swUserFaceimgService.list(lambdaQueryWrapper); + if(CollectionUtils.isNotEmpty(list)){ + log.error("同步员工提取人脸特征,已存在人脸特征,跳过"); + return Result.OK(); + } + } + SwUserFaceimg slUserFaceimg = new SwUserFaceimg(); +// slUserFaceimg.setFaceImgDataString(imgSrc); + String imgUrlByBase64 = swUserFaceimgService.getImgUrlByBase64(imgSrc); + slUserFaceimg.setFaceImg(imgUrlByBase64); + ImageInfo rgbData = ImageFactory.getRGBData(Base64Util.base64ToBytes(imgSrc)); + List faceInfoList = faceEngineService.detectFaces(rgbData); + if (CollectionUtils.isNotEmpty(faceInfoList)) { + for (FaceInfo faceInfo : faceInfoList) { + byte[] feature = faceEngineService.extractFaceFeature(rgbData, faceInfo); + slUserFaceimg.setFaceFeatureData(feature); + } + } + slUserFaceimg.setFaceStatus("1"); + slUserFaceimg.setUserId(userId); + CqBaseEmployee cqBaseEmployee = iCqBaseEmployeeService.getById(userId); + if(null != cqBaseEmployee && StringUtils.isNotBlank(cqBaseEmployee.getEmpIdcard())){ + slUserFaceimg.setCardNumber(cqBaseEmployee.getEmpIdcard()); + } + swUserFaceimgService.save(slUserFaceimg); + //默认最多保存三张照片 + List slUserFaceimgList = swUserFaceimgService.lambdaQuery().eq(SwUserFaceimg::getUserId, userId) + .eq(SwUserFaceimg::getFaceSource, 0).eq(SwUserFaceimg::getDataType, 0).eq(SwUserFaceimg::getFaceStatus, "1").orderByDesc(SwUserFaceimg::getCreateTime).list(); + if (slUserFaceimgList.size() > 3) { + List upList = slUserFaceimgList.subList(3, slUserFaceimgList.size()); + if (CollectionUtils.isNotEmpty(upList)) { + upList.forEach(i -> { + i.setIsSync(0); + i.setFaceStatus("0"); + }); + swUserFaceimgService.saveOrUpdateBatch(upList); + } + } + //添加/更新人脸缓存 + UserFaceInfo userInfo = new UserFaceInfo(); + userInfo.setUserId(userId); + userInfo.setUserFaceId(slUserFaceimg.getId()); + userInfo.setFaceFeature(slUserFaceimg.getFaceFeatureData()); + userFaceCacheService.putUser(userInfo); + + //发送消息到扣费闸机(查询下人员是否是sub扣费表的用户) + //保存新增扣费闸机人脸特征数据到增量表 + UserFaceInfo userFaceInfo = new UserFaceInfo(); + userFaceInfo.setUserId(slUserFaceimg.getUserId()); + userFaceInfo.setUserFaceId(slUserFaceimg.getId()); + userFaceInfo.setFaceFeatureString(slUserFaceimg.getFaceFeatureDataString()); + swUserFaceimgService.sendPaymentGateUserInfo(userFaceInfo,1,true,userFaceInfo.getUserId()); + return Result.OK().success("添加成功!"); + } + + public Result saveSubFaceImg(Map map){ + String userId = map.get("userId").toString(); + String imgSrc = map.get("imgSrc").toString(); + if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(imgSrc)) { + return Result.error("参数有误"); + } + if(map.containsKey("face")){ + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(SwUserFaceimgSub::getUserId, userId); + lambdaQueryWrapper.isNotNull(SwUserFaceimgSub::getFaceFeatureData); + List list = swUserFaceimgSubService.list(lambdaQueryWrapper); + if(CollectionUtils.isNotEmpty(list)){ + log.error("同步员工提取人脸特征,已存在人脸特征,跳过"); + return Result.OK(); + } + } + + SwUserFaceimgSub slUserFaceimg = new SwUserFaceimgSub(); + + String imgUrlByBase64 = swUserFaceimgService.getImgUrlByBase64(imgSrc); + slUserFaceimg.setFaceImg(imgUrlByBase64); + ImageInfo rgbData = ImageFactory.getRGBData(Base64Util.base64ToBytes(imgSrc)); + List faceInfoList = faceEngineService.detectFaces(rgbData); + if (CollectionUtils.isNotEmpty(faceInfoList)) { + for (FaceInfo faceInfo : faceInfoList) { + byte[] feature = faceEngineService.extractFaceFeature(rgbData, faceInfo); + slUserFaceimg.setFaceFeatureData(feature); + } + } + slUserFaceimg.setFaceStatus("1"); + slUserFaceimg.setUserId(userId); + swUserFaceimgSubService.save(slUserFaceimg); + + //默认最多保存三张照片 + List slUserFaceimgList = swUserFaceimgSubService.lambdaQuery().eq(SwUserFaceimgSub::getUserId, userId) + .eq(SwUserFaceimgSub::getFaceSource, 0).eq(SwUserFaceimgSub::getFaceStatus, "1").orderByDesc(SwUserFaceimgSub::getCreateTime).list(); + if (slUserFaceimgList.size() > 3) { + List upList = slUserFaceimgList.subList(3, slUserFaceimgList.size()); + if (CollectionUtils.isNotEmpty(upList)) { + upList.forEach(i -> { + i.setIsSync(0); + i.setFaceStatus("0"); + }); + swUserFaceimgSubService.saveOrUpdateBatch(upList); + } + } + //保存新增扣费闸机人脸特征数据到增量表 + UserFaceInfo userInfo = new UserFaceInfo(); + userInfo.setUserId(slUserFaceimg.getUserId()); + userInfo.setUserFaceId(slUserFaceimg.getId()); + userInfo.setFaceFeatureString(slUserFaceimg.getFaceFeatureDataString()); + swUserFaceimgService.sendPaymentGateUserInfo(userInfo,1,false,null); + return Result.OK().success("添加成功!"); + } + + @AutoLog(value = "新增人脸信息本地上传图片") + @Operation(summary = "新增人脸信息本地上传图片", description = "新增人脸信息本地上传图片") + @PostMapping(value = "/saveFaceImgLocalUpload") + @SendNotification() + public Result saveFaceImgLocalUpload(@RequestBody Map map) { + if(!map.containsKey("userType")){ + map.put("userType",0); + } + //用户类型(0: 正式员工 1:非正式员工,2:家庭用户) + Integer userType = (Integer)map.get("userType"); + if(userType != 0){ + //非正式员工 + return saveFaceImgSubLocalUpload(map); + } + String userId = map.get("userId").toString(); + String img = map.get("imgSrc").toString(); + if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(img)) { + System.out.println("+++++++++++++++++++++1+++++++++++++++参数有误++++++++++++++"); + return Result.error("参数有误"); + } + System.out.println("+++++++++++++++++++++2+++++++++++++++img++++++++++++++"+img); +// String img = "http://192.168.10.6:8888/temp/20231011/1c8a1042-9760-4bf4-a89f-ca1f8c0d460d_1697006660528.png"; + //服务器上替换img地址 + if(img.contains("yyjk")){ + img= img.replaceAll("https://yyjk.cqygjk.com/gateway","http://10.78.1.52:9090"); + } + if(img.contains("yyjk")){ + img= img.replaceAll("http://yyjk.cqygjk.com/gateway","http://10.78.1.52:9090"); + } + System.out.println("+++++++++++++++++++++3+++++++++++++++img++++++++++++++"+img); + String imgSrc = "data:image/png;base64," + Base64Util.netImageToBase64(img); + System.out.println("+++++++++++++++++++++4+++++++++++++++imgSrc++++++++++++++"+imgSrc); + imgSrc = imgSrc.replaceAll("\r|\n", ""); + SwUserFaceimg slUserFaceimg = new SwUserFaceimg(); + System.out.println("+++++++++++++++++++++5+++++++++++++++imgSrc++++++++++++++"+imgSrc); +// slUserFaceimg.setFaceImgDataString(imgSrc); + ImageInfo rgbData = ImageFactory.getRGBData(Base64Util.base64ToBytes(imgSrc)); + List faceInfoList = faceEngineService.detectFaces(rgbData); + if (CollectionUtils.isNotEmpty(faceInfoList)) { + for (FaceInfo faceInfo : faceInfoList) { + byte[] feature = faceEngineService.extractFaceFeature(rgbData, faceInfo); + slUserFaceimg.setFaceFeatureData(feature); + } + } + slUserFaceimg.setFaceStatus("1"); + slUserFaceimg.setUserId(userId); + CqBaseEmployee cqBaseEmployee = iCqBaseEmployeeService.getById(userId); + if(null != cqBaseEmployee && StringUtils.isNotBlank(cqBaseEmployee.getEmpIdcard())){ + slUserFaceimg.setCardNumber(cqBaseEmployee.getEmpIdcard()); + } + slUserFaceimg.setFaceImg(img); + swUserFaceimgService.save(slUserFaceimg); + //默认最多保存三张照片 + List slUserFaceimgList = swUserFaceimgService.lambdaQuery().eq(SwUserFaceimg::getUserId, userId) + .eq(SwUserFaceimg::getFaceSource, 0).eq(SwUserFaceimg::getDataType, 0).eq(SwUserFaceimg::getFaceStatus, "1").orderByDesc(SwUserFaceimg::getCreateTime).list(); + if (slUserFaceimgList.size() > 3) { + List upList = slUserFaceimgList.subList(3, slUserFaceimgList.size()); + if (CollectionUtils.isNotEmpty(upList)) { + upList.forEach(i -> { + i.setFaceStatus("0"); + }); + swUserFaceimgService.saveOrUpdateBatch(upList); + } + } + //添加/更新人脸缓存 + UserFaceInfo userInfo = new UserFaceInfo(); + userInfo.setUserId(userId); + userInfo.setUserFaceId(slUserFaceimg.getId()); + userInfo.setFaceFeature(slUserFaceimg.getFaceFeatureData()); + userFaceCacheService.putUser(userInfo); + + //保存新增扣费闸机人脸特征数据到增量表 + UserFaceInfo userFaceInfo = new UserFaceInfo(); + userFaceInfo.setUserId(slUserFaceimg.getUserId()); + userFaceInfo.setUserFaceId(slUserFaceimg.getId()); + userFaceInfo.setFaceFeatureString(slUserFaceimg.getFaceFeatureDataString()); + swUserFaceimgService.sendPaymentGateUserInfo(userFaceInfo,1,true,userFaceInfo.getUserId()); + return Result.OK().success("添加成功!"); + } + + public Result saveFaceImgSubLocalUpload(@RequestBody Map map) { + String userId = map.get("userId").toString(); + String img = map.get("imgSrc").toString(); + if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(img)) { + System.out.println("+++++++++++++++++++++1+++++++++++++++参数有误++++++++++++++"); + return Result.error("参数有误"); + } + System.out.println("+++++++++++++++++++++2+++++++++++++++img++++++++++++++"+img); +// String img = "http://192.168.10.6:8888/temp/20231011/1c8a1042-9760-4bf4-a89f-ca1f8c0d460d_1697006660528.png"; + //服务器上替换img地址 + if(img.contains("yyjk")){ + img= img.replaceAll("https://yyjk.cqygjk.com/gateway","http://10.78.1.52:9090"); + } + if(img.contains("yyjk")){ + img= img.replaceAll("http://yyjk.cqygjk.com/gateway","http://10.78.1.52:9090"); + } + System.out.println("+++++++++++++++++++++3+++++++++++++++img++++++++++++++"+img); + String imgSrc = "data:image/png;base64," + Base64Util.netImageToBase64(img); + System.out.println("+++++++++++++++++++++4+++++++++++++++imgSrc++++++++++++++"+imgSrc); + imgSrc = imgSrc.replaceAll("\r|\n", ""); + SwUserFaceimgSub slUserFaceimg = new SwUserFaceimgSub(); + System.out.println("+++++++++++++++++++++5+++++++++++++++imgSrc++++++++++++++"+imgSrc); + ImageInfo rgbData = ImageFactory.getRGBData(Base64Util.base64ToBytes(imgSrc)); + List faceInfoList = faceEngineService.detectFaces(rgbData); + if (CollectionUtils.isNotEmpty(faceInfoList)) { + for (FaceInfo faceInfo : faceInfoList) { + byte[] feature = faceEngineService.extractFaceFeature(rgbData, faceInfo); + slUserFaceimg.setFaceFeatureData(feature); + } + } + slUserFaceimg.setFaceStatus("1"); + slUserFaceimg.setUserId(userId); + + slUserFaceimg.setFaceImg(img); + swUserFaceimgSubService.save(slUserFaceimg); + //默认最多保存三张照片 + List slUserFaceimgList = swUserFaceimgSubService.lambdaQuery().eq(SwUserFaceimgSub::getUserId, userId) + .eq(SwUserFaceimgSub::getFaceSource, 0).eq(SwUserFaceimgSub::getFaceStatus, "1").orderByDesc(SwUserFaceimgSub::getCreateTime).list(); + if (slUserFaceimgList.size() > 3) { + List upList = slUserFaceimgList.subList(3, slUserFaceimgList.size()); + if (CollectionUtils.isNotEmpty(upList)) { + upList.forEach(i -> { + i.setFaceStatus("0"); + }); + swUserFaceimgSubService.saveOrUpdateBatch(upList); + } + } + //保存新增扣费闸机人脸特征数据到增量表 + UserFaceInfo userFaceInfo = new UserFaceInfo(); + userFaceInfo.setUserId(slUserFaceimg.getUserId()); + userFaceInfo.setUserFaceId(slUserFaceimg.getId()); + userFaceInfo.setFaceFeatureString(slUserFaceimg.getFaceFeatureDataString()); + swUserFaceimgService.sendPaymentGateUserInfo(userFaceInfo,1,false,null); + return Result.OK().success("添加成功!"); + } + + @AutoLog(value = "查询人脸信息列表") + @Operation(summary = "查询人脸信息列表", description = "查询人脸信息列表") + @GetMapping(value = "/queryFaceImgList") + public Result queryFaceImgList(@RequestParam("userId") String userId, + @RequestParam(value = "userType",defaultValue = "0") Integer userType) { + if(userType != 0){ + return queryFaceImgSubList(userId,userType); + } + List slUserFaceimgList = swUserFaceimgService.lambdaQuery().eq(SwUserFaceimg::getUserId, userId) + .eq(SwUserFaceimg::getFaceStatus, "1") + .eq(SwUserFaceimg::getFaceSource, 0).list();//只展示非绑盘机渠道获取的人脸图片和特征 + slUserFaceimgList.forEach(i -> { + if(StringUtils.isNotEmpty(i.getFaceImg())){ + i.setFaceImg(FileShowUtil.getShowUrl(i.getFaceImg())); + } + }); + return Result.OK(slUserFaceimgList); + } + + public Result queryFaceImgSubList(String userId,Integer userType) { + + List slUserFaceimgList = swUserFaceimgSubService.lambdaQuery().eq(SwUserFaceimgSub::getUserId, userId) + .eq(SwUserFaceimgSub::getFaceStatus, "1") + .eq(SwUserFaceimgSub::getDelFlag, 0) + .eq(SwUserFaceimgSub::getFaceSource, 0).list();//只展示非绑盘机渠道获取的人脸图片和特征 + slUserFaceimgList.forEach(i -> { + if(StringUtils.isNotEmpty(i.getFaceImg())){ + i.setFaceImg(FileShowUtil.getShowUrl(i.getFaceImg())); + } + }); + return Result.OK(slUserFaceimgList); + } + + @AutoLog(value = "删除指定人脸照片") + @Operation(summary = "删除指定人脸照片", description = "删除指定人脸照片") + @DeleteMapping(value = "/deleteFaceImgById") + @SendNotification() + public Result deleteFaceImgById(@RequestParam("id") String id, + @RequestParam(value = "userType",defaultValue = "0") Integer userType) { + //用户类型(0: 正式员工 1:非正式员工,2:家庭用户) + if(userType != 0){ + //非正式员工 + SwUserFaceimgSub swUserFaceimgSub = swUserFaceimgSubService.getById(id); + swUserFaceimgSub.setDelFlag(1); + //2025-05-07 余:逻辑删除用户特征 + swUserFaceimgSubService.updateById(swUserFaceimgSub); + //发送删除扣费闸机人脸特征数据 + UserFaceInfo userInfo = new UserFaceInfo(); + userInfo.setUserFaceId(swUserFaceimgSub.getId()); + swUserFaceimgService.sendPaymentGateUserInfo(userInfo,0,false,null); + return Result.OK().success("删除成功"); + } + SwUserFaceimg swUserFaceimgV1 = swUserFaceimgService.getById(id); + swUserFaceimgService.removeById(id); + + //删除人脸数据缓存 + if(null != swUserFaceimgV1){ + userFaceCacheService.removeUserFaceImgByFaceId(swUserFaceimgV1.getUserId(),id); + //发送删除扣费闸机人脸特征数据 + UserFaceInfo userInfo = new UserFaceInfo(); + userInfo.setUserFaceId(swUserFaceimgV1.getId()); + swUserFaceimgService.sendPaymentGateUserInfo(userInfo,0,true,swUserFaceimgV1.getUserId()); + } + return Result.OK().success("删除成功"); + } + +// @AutoLog(value = "重新获取所有人脸数据到redis中") +// @Operation(summary = "重新获取所有人脸数据到redis中", description = "重新获取所有人脸数据到redis中") +// @DeleteMapping(value = "/reputUserFaceDataToRedis") +// public Result reputUserFaceDataToRedis() { +// swUserFaceimgService.reputUserFaceDataToRedis(); +// return Result.OK().success("操作成功"); +// } + + + + @AutoLog(value = "指定部门人脸到人脸库中") + @Operation(summary = "指定部门人脸到人脸库中", description = "指定部门人脸到人脸库中") + @PostMapping(value = "/saveOrgCodeDepartUserFaceImgDate") + @SendNotification() + public Result saveFaceImg(@RequestParam(name = "orgCode", required = true) String orgCode) { + // 获取指定二级单位的所有部门 + List departList = swUserFaceimgMapper.getDepart(orgCode); + + List> imgMapList = swUserFaceimgMapper.getUserImgUrl(departList); + List swUserFaceimgs = swUserFaceimgMapper.selectList(new LambdaQueryWrapper<>()); + + if (CollectionUtils.isNotEmpty(imgMapList)){ + for (Map map : imgMapList) { + try { + // 如果已经有人脸了,就跳过 + String userId = map.get("id"); + int count = (int)swUserFaceimgs.stream().filter(a -> userId.equals(a.getUserId())).count(); + if (count > 0){ + continue; + } + + // 头像为空跳过 + String imgStr = map.get("portrait"); + if (StringUtils.isEmpty(imgStr)){ + continue; + } + + // 下载图片转换成byte数组 + String imageType = imgStr.substring(imgStr.lastIndexOf(".") + 1); + //硬编码IP-中等风险 +// String preStr = "http://113.200.64.63:9003/cqyt/systemController/showOrDownByurlFTP.do?dbPath="; +// String imgUrl = preStr + imgStr; + byte[] bytes = HttpDownloader.downloadBytes(""); + + // 获取图片的base64格式不对,出来string太短就跳过 + BufferedImage bufferedImage = ImgUtil.toImage(bytes); + String imgSrc = ImgUtil.toBase64DateUri(bufferedImage, imageType); + if (imgSrc.length() < 100){ + continue; + } + + // 保存逻辑 + SwUserFaceimg slUserFaceimg = new SwUserFaceimg(); + slUserFaceimg.setFaceImgDataString(imgSrc); + + ImageInfo rgbData = ImageFactory.getRGBData(Base64Util.base64ToBytes(imgSrc)); + List faceInfoList = faceEngineService.detectFaces(rgbData); + if (CollectionUtils.isNotEmpty(faceInfoList)) { + for (FaceInfo faceInfo : faceInfoList) { + byte[] feature = faceEngineService.extractFaceFeature(rgbData, faceInfo); + slUserFaceimg.setFaceFeatureData(feature); + } + } + slUserFaceimg.setFaceStatus("1"); + slUserFaceimg.setUserId(userId); + swUserFaceimgService.save(slUserFaceimg); + + // 每次重置大对象 + bytes = null; + imgSrc = null; + rgbData = null; + + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + return Result.OK(); + } + + + + /** + * 上传人脸压缩包 + * (支付用户场景暂不处理发送消息通知) + * @param request + * @return + */ + @PostMapping("/uploadFaceImagePackage") + public Result uploadFaceImagePackage(HttpServletRequest request){ + return swUserFaceimgService.uploadFaceImagePackage(request,0,null); + } + + /** + * 上传人脸压缩包 体重秤 + */ + @PostMapping("/uploadFaceImagePackageWeight") + public Result uploadFaceImagePackageWeight(HttpServletRequest request){ + return swUserFaceimgService.uploadFaceImagePackage(request,1,null); + } + /** + * 上传人脸压缩包 公共 + */ + @PostMapping("/uploadFaceImagePackageCommon") + public Result uploadFaceImagePackageCommon(HttpServletRequest request){ + return swUserFaceimgService.uploadFaceImagePackage(request,2,null); + } + + + /** + * 根据二级部门id 获取没有人脸图片的用户列表 + * @return 人员信息 + */ + @GetMapping("/getUserInfoNotHaveFaceUrlBySecondDepartId") + public Result getUserInfoNotHaveFaceUrlBySecondDepartId(@RequestParam(name = "secondDepartId") String secondDepartId){ + return Result.OK(swUserFaceimgService.getUserInfoNotHaveFaceUrlBySecondDepartId(secondDepartId)); + } + + @AutoLog(value = "保存新增扣费闸机人脸特征数据到增量表") + @Operation(summary = "保存新增扣费闸机人脸特征数据到增量表", description = "保存新增扣费闸机人脸特征数据到增量表") + @PostMapping(value = "/sendPaymentGateUserInfo") + public Result sendPaymentGateUserInfo(@RequestBody List userIdList) { + //查询该特征人员是否是 SysUserSub 扣费表的用户,是就发送消息给扣费闸机,不是不发送 + //发送消息到扣费闸机(查询下人员是否是sub扣费表的用户) + //保存新增扣费闸机人脸特征数据到增量表 + if(CollectionUtils.isNotEmpty(userIdList)){ + userIdList.forEach(userId->{ + List slUserFaceimgList = swUserFaceimgService.lambdaQuery() + .select(SwUserFaceimg::getFaceFeatureData,SwUserFaceimg::getId) + .eq(SwUserFaceimg::getUserId, userId) + .eq(SwUserFaceimg::getDataType, 0) + .eq(SwUserFaceimg::getFaceSource, 0) + .eq(SwUserFaceimg::getFaceStatus, "1") + .orderByDesc(SwUserFaceimg::getCreateTime).list(); + if(CollectionUtils.isNotEmpty(slUserFaceimgList)){ + slUserFaceimgList.forEach(swUserFaceimg -> { + UserFaceInfo userFaceInfo = new UserFaceInfo(); + userFaceInfo.setFaceFeatureString(swUserFaceimg.getFaceFeatureDataString()); + userFaceInfo.setUserFaceId(swUserFaceimg.getId()); + userFaceInfo.setUserId(userId); + swUserFaceimgService.sendPaymentGateUserInfo(userFaceInfo,1,false,userFaceInfo.getUserId()); + }); + } + }); + + } + return Result.OK(); + } + + /** + * 用户人脸图片下载本地(3265=135s) + * @return + */ + @Operation(summary = "用户人脸图片下载本地", description = "用户人脸图片下载本地") + @GetMapping(value = "/exportFaceData") + public Result exportFaceData() { + List swUserFaceimg = swUserFaceimgMapper.swUserFaceimgListV2(); + if (swUserFaceimg == null) { + return Result.error("未找到对应数据", null); + } + List listF = new ArrayList<>(); + List listS = new ArrayList<>(); + LinkedHashMap stringObjectMap = new LinkedHashMap<>(); + for (int i = 0; i < swUserFaceimg.size(); i++) { + // 保存图片到本地 + SwUserFaceimgWeightScaleV2Vo swUserFaceimgWeightScaleV2Vo = swUserFaceimg.get(i); + String faceImgDataString = swUserFaceimgWeightScaleV2Vo.getFaceImgDataString(); + try { + // // 生成图片文件名 + String fileName1 = swUserFaceimgWeightScaleV2Vo.getName() + "_" + swUserFaceimgWeightScaleV2Vo.getCardNumber(); + String fileName = generateTimestampFileName(fileName1,extractMimeType(faceImgDataString)); + faceImgDataString = faceImgDataString.replaceAll(" ", "+"); + faceImgDataString = faceImgDataString.replaceAll("\r|\n", ""); + byte[] imageBytes = null; + if(StringUtils.isNotBlank(faceImgDataString)){ + if(faceImgDataString.contains(",")){ + String img = faceImgDataString.split(",")[1]; + imageBytes = Base64.getDecoder().decode(img); + } else { + imageBytes = Base64.getDecoder().decode(faceImgDataString); + } + if(!fileName.contains("unknown")){ + saveImageToFile(imageBytes, fileName); + listS.add(swUserFaceimgWeightScaleV2Vo.getName()+"_"+swUserFaceimgWeightScaleV2Vo.getCardNumber()); + } else { + listF.add(swUserFaceimgWeightScaleV2Vo.getName()+"_"+swUserFaceimgWeightScaleV2Vo.getCardNumber()+"_图片base64文件格式异常->"+fileName); + log.error("图片base64文件格式异常{},用户信息:{},{},faceImgDataString:{}", fileName,swUserFaceimgWeightScaleV2Vo.getCardNumber(), swUserFaceimgWeightScaleV2Vo.getName(),faceImgDataString); + } + } else { + listF.add(swUserFaceimgWeightScaleV2Vo.getName()+"_"+swUserFaceimgWeightScaleV2Vo.getCardNumber()+"_图片base64为空"); + log.error("图片base64为空,用户信息:{},{},faceImgDataString:{}", swUserFaceimgWeightScaleV2Vo.getCardNumber(), swUserFaceimgWeightScaleV2Vo.getName(),faceImgDataString); + } + } catch (IllegalArgumentException | IOException e ) { + listF.add(swUserFaceimgWeightScaleV2Vo.getName()+"_"+swUserFaceimgWeightScaleV2Vo.getCardNumber()+"_图片保存异常"); + log.error("图片保存异常,用户信息:{},{},错误:{}", swUserFaceimgWeightScaleV2Vo.getCardNumber(), swUserFaceimgWeightScaleV2Vo.getName(), e.getMessage()); + } + } + stringObjectMap.put("人脸总人数",swUserFaceimg.size()); + stringObjectMap.put("人脸失败人数",listF.size()); + stringObjectMap.put("人脸失败详情",listF); + stringObjectMap.put("人脸成功人数",listS.size()); + return Result.OK(stringObjectMap); + } + + + private static String extractMimeType(String base64Image) { + String mimeType = "image/unknown"; // 默认未知类型 + Pattern pattern = Pattern.compile("data:image/(\\w+);base64,"); + Matcher matcher = pattern.matcher(base64Image); + if (matcher.find()) { + mimeType = "image/" + matcher.group(1); + } + return mimeType; + } + + private static String generateTimestampFileName(String fileName1,String mimeType) { + String extension = mimeType.substring(mimeType.lastIndexOf("/") + 1); + return fileName1 + "." + extension; + } + + private static void saveImageToFile(byte[] imageBytes, String fileName) throws IOException { + File folder = new File("C:\\faceImg\\"); + if (!folder.exists()) { + folder.mkdirs(); + } + String filePath = folder + File.separator + fileName; + try (FileOutputStream fos = new FileOutputStream(filePath)) { + fos.write(imageBytes); + } + } + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/controller/SwUserFaceimgSubController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/controller/SwUserFaceimgSubController.java new file mode 100644 index 0000000..e7af6e8 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/controller/SwUserFaceimgSubController.java @@ -0,0 +1,89 @@ +package org.jeecg.system.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.aspect.annotation.AutoLog; +import org.jeecg.common.system.base.controller.JeecgController; +import org.jeecg.common.util.DateUtils; +import org.jeecg.system.service.IStUserFaceimgSubIncremInfoService; +import org.jeecg.system.service.ISwUserFaceimgSubService; +import org.jeecg.user.entity.StUserFaceimgSubIncremInfo; +import org.jeecg.user.entity.SwUserFaceimgSub; +import org.springframework.beans.factory.annotation.Autowired; +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.RestController; + +import javax.annotation.Resource; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +/** + * @Description: 用户人脸数据 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +@Tag(name= "用户人脸数据") +@RestController +@RequestMapping("/userface/swUserFaceimgSub") +@Slf4j +public class SwUserFaceimgSubController extends JeecgController { + + @Autowired + private ISwUserFaceimgSubService iSwUserFaceimgSubService; + @Resource + private IStUserFaceimgSubIncremInfoService iStUserFaceimgSubIncremInfoService; + + + /** + * 人脸初始化-分页列表查询 + * + * @param pageNo + * @param pageSize + * @return + */ + @AutoLog(value = "人脸初始化-分页列表查询") + @Operation(summary = "人脸初始化-分页列表查询", description = "人脸初始化-分页列表查询") + @GetMapping(value = "/list") + public Result queryPageListV2( + @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, + @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) { + Page page = new Page(pageNo, pageSize); + IPage pageList = iSwUserFaceimgSubService.queryPageList(page); + return Result.OK(pageList); + } + + + /** + * 扣费闸机心跳 + */ + @Operation(summary = "扣费闸机心跳", description = "扣费闸机心跳") + @GetMapping("/heartbeat/interface") + @AutoLog(value = "扣费闸机心跳") + public Result heartbeatInterface(@RequestParam(name = "restId", required = false) String restId, + @RequestParam(name = "qrcodeId", required = false) String qrcodeId, + @RequestParam(name = "timeDate", required = false) String timeDate) { + if(StringUtils.isNotBlank(timeDate)){ + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + Date date = new Date(Long.parseLong(timeDate)); + // 设置查询条件:createTime >= 当前时间 + lambdaQueryWrapper.ge(StUserFaceimgSubIncremInfo::getCreateTime, DateUtils.formatDate(date,"yyyy-MM-dd HH:mm:ss")); + lambdaQueryWrapper.eq(StUserFaceimgSubIncremInfo::getDelFlag, 0); + // 按 createTime 降序排列,获取最新数据 + lambdaQueryWrapper.orderByDesc(StUserFaceimgSubIncremInfo::getCreateTime); + List list = iStUserFaceimgSubIncremInfoService.list(lambdaQueryWrapper); + return Result.OK(list); + } + return Result.OK(Collections.emptyList()); + } + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/controller/SysPayDepartController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/controller/SysPayDepartController.java new file mode 100644 index 0000000..d4b33ec --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/controller/SysPayDepartController.java @@ -0,0 +1,211 @@ +package org.jeecg.system.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.system.util.JwtUtil; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.system.service.ISysPayDepartService; +import org.jeecg.user.entity.SysPayDepart; +import org.jeecg.user.vo.PayDepartIdModel; +import org.jeecg.user.vo.SysPayDepartTreeModel; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.Arrays; +import java.util.List; + +/** + *

+ * 部门表 前端控制器 + *

+ * + * @Author: Steve @Since: 2019-01-22 + */ +@RestController + @Tag(name = "部门表") +@RequestMapping("/emp/cqBaseEmployee/sys/sysPayDepart") +@Slf4j +public class SysPayDepartController { + + @Autowired + private ISysPayDepartService sysDepartService; + /** + * 查询数据 查出所有部门,并以树结构数据格式响应给前端 + * + * @return + */ + @Operation(summary = "s-1-部门列表 ", description = "s-1-部门列表") + @RequestMapping(value = "/queryTreeList", method = RequestMethod.GET) + public Result> queryTreeList(@RequestParam(name = "ids", required = false) String ids) { + try { + if(oConvertUtils.isNotEmpty(ids)){ + List departList = sysDepartService.queryTreeList(ids); + return Result.OK(departList); + }else{ + List list = sysDepartService.queryTreeListCach(); + return Result.OK(list); + } + } catch (Exception e) { + log.error(e.getMessage(),e); + return Result.error(e.getMessage()); + } + } + + + @RequestMapping(value = "/queryTreeListBy", method = RequestMethod.GET) + public Result> queryTreeListBy() { + Result> result = new Result<>(); + try { + + List list = sysDepartService.querySysDepartListBy(); + result.setResult(list); + result.setSuccess(true); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return result; + } + + + /** + * 添加新数据 添加用户新建的部门对象数据,并保存到数据库 + * + * @param sysDepart + * @return + */ + @RequestMapping(value = "/add", method = RequestMethod.POST) + public Result add(@RequestBody SysPayDepart sysDepart, HttpServletRequest request) { + Result result = new Result(); + String userId = JwtUtil.getUserIdByToken(request); + try { + sysDepart.setCreateBy(userId); + sysDepartService.saveDepartData(sysDepart, userId); + result.success("添加成功!"); + + } catch (Exception e) { + log.error(e.getMessage(), e); + result.error500("操作失败"); + } + return result; + } + + + /** + * 编辑数据 编辑部门的部分数据,并保存到数据库 + * + * @param sysDepart + * @return + */ + @RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST}) + public Result edit(@RequestBody SysPayDepart sysDepart, HttpServletRequest request) { + if(StringUtils.isBlank(sysDepart.getId())){ + return Result.error("请选择要修改的部门"); + } + String userId = JwtUtil.getUserIdByToken(request); + sysDepart.setUpdateBy(userId); + Result result = new Result(); + SysPayDepart sysDepartEntity = sysDepartService.getById(sysDepart.getId()); + if (sysDepartEntity == null) { + result.error500("未找到对应实体"); + } else { + boolean ok = sysDepartService.updateDepartDataById(sysDepart, userId); + } + return result; + } + + /** + * 通过id删除 + * + * @param id + * @return + */ + @RequestMapping(value = "/delete", method = RequestMethod.DELETE) + public Result delete(@RequestParam(name = "id", required = true) String id) { + + Result result = new Result(); + SysPayDepart sysDepart = sysDepartService.getById(id); + if (sysDepart == null) { + result.error500("未找到对应实体"); + } else { + boolean ok = sysDepartService.delete(id); + if (ok) { + result.success("删除成功!"); + } + } + return result; + } + + + /** + * 批量删除 根据前端请求的多个ID,对数据库执行删除相关部门数据的操作 + * + * @param ids + * @return + */ + @RequestMapping(value = "/deleteBatch", method = RequestMethod.DELETE) + public Result deleteBatch(@RequestParam(name = "ids", required = true) String ids) { + + Result result = new Result(); + if (ids == null || "".equals(ids.trim())) { + result.error500("参数不识别!"); + } else { + this.sysDepartService.deleteBatchWithChildren(Arrays.asList(ids.split(","))); + result.success("删除成功!"); + } + return result; + } + + /** + * 查询数据 添加或编辑页面对该方法发起请求,以树结构形式加载所有部门的名称,方便用户的操作 + * + * @return + */ + @RequestMapping(value = "/queryIdTree", method = RequestMethod.GET) + public Result> queryIdTree() { + Result> result = new Result<>(); + try { + List list = sysDepartService.queryDepartIdTreeList(); + result.setResult(list); + result.setSuccess(true); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return result; + } + + /** + *

+ * 部门搜索功能方法,根据关键字模糊搜索相关部门 + *

+ * + * @param keyWord + * @return + */ + @RequestMapping(value = "/searchBy", method = RequestMethod.GET) + public Result> searchBy(@RequestParam(name = "keyWord", required = true) String keyWord, @RequestParam(name = "myDeptSearch", required = false) String myDeptSearch) { + Result> result = new Result>(); + //部门查询,myDeptSearch为1时为我的部门查询,登录用户为上级时查只查负责部门下数据 + LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + String departIds = null; + if (oConvertUtils.isNotEmpty(user.getUserIdentity()) && user.getUserIdentity().equals(CommonConstant.USER_IDENTITY_2)) { + departIds = user.getDepartIds(); + } + List treeList = this.sysDepartService.searchByKeyWord(keyWord, myDeptSearch, departIds); + if (treeList == null || treeList.size() == 0) { + result.setSuccess(false); + result.setMessage("未查询匹配数据!"); + return result; + } + result.setResult(treeList); + return result; + } + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/StUserFaceimgSubIncremInfoMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/StUserFaceimgSubIncremInfoMapper.java new file mode 100644 index 0000000..de14c79 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/StUserFaceimgSubIncremInfoMapper.java @@ -0,0 +1,18 @@ +package org.jeecg.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; +import org.jeecg.user.entity.StUserFaceimgSubIncremInfo; + +import java.util.List; + +/** + * @Description: 支付用户人脸信息增量记录表 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +public interface StUserFaceimgSubIncremInfoMapper extends BaseMapper { + + List selectFaceImagesAndSubs(@Param("timestamp") Long timestamp); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SwUserFaceimgMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SwUserFaceimgMapper.java new file mode 100644 index 0000000..7338dc9 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SwUserFaceimgMapper.java @@ -0,0 +1,49 @@ +package org.jeecg.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.jeecg.emp.vo.UserInfoVo; +import org.jeecg.user.entity.SwUserFaceimg; +import org.jeecg.user.entity.SysUserSub; +import org.jeecg.user.vo.SwUserFaceimgWeightScaleV2Vo; +import org.jeecg.user.vo.SwUserFaceimgWeightScaleVo; + +import java.util.List; +import java.util.Map; + +/** + * @Description: 用户人脸数据 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +public interface SwUserFaceimgMapper extends BaseMapper { + + List> getUserImgUrl(@Param("departIdList") List departIdList); + + List getDepart(String orgCode); + + @Select("select id, user_id, face_feature_data from st_user_faceimg") + List getList(); + + List> getSecondDepartNotHaveFaceUrl(String secondDepartId); + + + List swUserFaceimgList(Page page); + + List swUserFaceimgListV2(); + + IPage selectFaceImagesAndSubs(@Param("page") Page page,@Param("orgCode") String orgCode); + + List selectUserIdByOrgCode(@Param("orgCode") String orgCode); + /** + * 查询扣费闸机人员数据 + * @param userId + * @param status 状态(1-正常,2-冻结) + * @return + */ + SysUserSub getUserSubInfo(@Param("userId") String userId, @Param("status") Integer status); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SwUserFaceimgSelfMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SwUserFaceimgSelfMapper.java new file mode 100644 index 0000000..cb40536 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SwUserFaceimgSelfMapper.java @@ -0,0 +1,19 @@ +package org.jeecg.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Select; +import org.jeecg.user.entity.SwUserFaceimgSelf; + +import java.util.List; + +/** + * @Description: 用户人脸数据-自研引擎 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +public interface SwUserFaceimgSelfMapper extends BaseMapper { + + @Select("select id, user_id, face_feature_data from st_user_faceimg_self") + List getList(); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SwUserFaceimgSubMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SwUserFaceimgSubMapper.java new file mode 100644 index 0000000..376aa63 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SwUserFaceimgSubMapper.java @@ -0,0 +1,40 @@ +package org.jeecg.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.jeecg.user.entity.SwUserFaceimgSub; +import org.jeecg.user.vo.SwUserFaceimgWeightScaleV2Vo; +import org.jeecg.user.vo.SwUserFaceimgWeightScaleVo; + +import java.util.List; +import java.util.Map; + +/** + * @Description: 用户人脸数据 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +public interface SwUserFaceimgSubMapper extends BaseMapper { + + List> getUserImgUrl(@Param("departIdList") List departIdList); + + List getDepart(String orgCode); + + @Select("select id, user_id, face_feature_data from st_user_faceimg_sub") + List getList(); + + List> getSecondDepartNotHaveFaceUrl(String secondDepartId); + + + List swUserFaceimgList(Page page); + + List swUserFaceimgListV2(); + + IPage selectFaceImagesAndSubs(@Param("page") Page page,@Param("timestamp") Long timestamp); + + List selectFaceImagesAndSubs(@Param("timestamp") Long timestamp); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SysGatewayRouteMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SysGatewayRouteMapper.java new file mode 100644 index 0000000..c9719d5 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SysGatewayRouteMapper.java @@ -0,0 +1,14 @@ +package org.jeecg.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.jeecg.system.entity.SysGatewayRoute; + +/** + * @Description: gateway路由管理 + * @Author: jeecg-boot + * @Date: 2020-05-26 + * @Version: V1.0 + */ +public interface SysGatewayRouteMapper extends BaseMapper { + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SysPayDepartMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SysPayDepartMapper.java new file mode 100644 index 0000000..3743354 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SysPayDepartMapper.java @@ -0,0 +1,61 @@ +package org.jeecg.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Select; +import org.jeecg.user.entity.SysPayDepart; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +/** + *

+ * 部门 Mapper 接口 + *

+ * + * @Author: Steve + * @Since: 2019-01-22 + */ +public interface SysPayDepartMapper extends BaseMapper { + + /** + * 根据用户ID查询部门集合 + */ + public List queryUserDeparts(@Param("userId") String userId); + + /** + * 根据用户名查询部门 + * + * @param username + * @return + */ + public List queryDepartsByUsername(@Param("username") String username); + + + @Select("select id,parent_id from sys_depart where id=#{departId}") + public SysPayDepart getParentDepartId(@Param("departId") String departId); + + /** + * 根据部门Id查询,当前和下级所有部门IDS + * @param departId + * @return + */ + List getSubDepIdsByDepId(@Param("departId") String departId); + + /** + * 根据部门编码获取部门下所有IDS + * @param orgCodes + * @return + */ + List getSubDepIdsByOrgCodes(@org.apache.ibatis.annotations.Param("orgCodes") String[] orgCodes); + + List queryTreeListByPid(@Param("parentId") String parentId); + /** + * 根据OrgCod查询所属公司信息 + * @param orgCode + * @return + */ + SysPayDepart queryCompByOrgCode(@Param("orgCode")String orgCode); + + String findIdByName(String name); + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SysUserMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SysUserMapper.java new file mode 100644 index 0000000..7439de5 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SysUserMapper.java @@ -0,0 +1,30 @@ +package org.jeecg.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.jeecg.common.system.vo.ScreenVo; +import org.jeecg.user.entity.SysUser; + +import java.util.List; + +/** + *

+ * 用户表 Mapper 接口 + *

+ * + * @Author scott + * @since 2018-12-20 + */ +public interface SysUserMapper extends BaseMapper { + + + /** + * 按照用户所属机构编码前置匹配食堂编号进行数据权限处理 + */ + @Select("select * from st_base_restaurant where id!=0 and rest_no LIKE CONCAT(#{orgCode},'%')") + List queryScreenVOByorgCode(@Param("orgCode") String orgCode); + + @Select("select * from st_base_restaurant r, sys_user_screen s where s.screen_id=r.id and s.user_id=#{userId}") + List queryScreenVOByUserId(@Param("userId") String userId); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SysUserScreenMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SysUserScreenMapper.java new file mode 100644 index 0000000..1cc9837 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SysUserScreenMapper.java @@ -0,0 +1,14 @@ +package org.jeecg.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.jeecg.user.entity.SysUserScreen; + +/** + * @Description: s + * @Author: yucl + * @Date: 2022-04-20 + * @Version: V1.0 + */ +public interface SysUserScreenMapper extends BaseMapper { + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SysUserSubMapper.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SysUserSubMapper.java new file mode 100644 index 0000000..79a421a --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/SysUserSubMapper.java @@ -0,0 +1,23 @@ +package org.jeecg.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; +import org.jeecg.user.entity.SysUserSub; +import org.jeecg.user.vo.StClientMemberBalanceVo; + +/** + *

+ * 子用户表 Mapper 接口 + *

+ * + * @Author scott + * @since 2018-12-20 + */ +public interface SysUserSubMapper extends BaseMapper { + + + void insertStClientMemberBalance(StClientMemberBalanceVo stClientMemberBalanceVo); + + StClientMemberBalanceVo selectByMemberId(@Param("memberId") String memberId); + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/StUserFaceimgSubIncremInfoMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/StUserFaceimgSubIncremInfoMapper.xml new file mode 100644 index 0000000..efab530 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/StUserFaceimgSubIncremInfoMapper.xml @@ -0,0 +1,24 @@ + + + + + + + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SwUserFaceimgMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SwUserFaceimgMapper.xml new file mode 100644 index 0000000..0c6fa43 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SwUserFaceimgMapper.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SwUserFaceimgSelfMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SwUserFaceimgSelfMapper.xml new file mode 100644 index 0000000..8446f77 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SwUserFaceimgSelfMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SwUserFaceimgSubMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SwUserFaceimgSubMapper.xml new file mode 100644 index 0000000..2c187ed --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SwUserFaceimgSubMapper.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SysGatewayRouteMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SysGatewayRouteMapper.xml new file mode 100644 index 0000000..b771b5a --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SysGatewayRouteMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SysPayDepartMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SysPayDepartMapper.xml new file mode 100644 index 0000000..5d4a5f9 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SysPayDepartMapper.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SysUserMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SysUserMapper.xml new file mode 100644 index 0000000..40b3651 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SysUserMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SysUserScreenMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SysUserScreenMapper.xml new file mode 100644 index 0000000..106a3b4 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SysUserScreenMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SysUserSubMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SysUserSubMapper.xml new file mode 100644 index 0000000..1e656f0 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/mapper/xml/SysUserSubMapper.xml @@ -0,0 +1,73 @@ + + + + + + + INSERT INTO st_client_member_balance ( + id, + member_id, + top_up_total, + top_up_balance, + reward_balance, + integral_balance, + consumption_count, + consumption_total, + last_consumption_time, + first_top_up_time, + invoice_total, + user_inner_type, + is_del, + is_status, + canteen_id, + create_by, + create_time, + is_sync + ) VALUES ( + #{id}, + #{memberId}, + #{topUpTotal, jdbcType=DECIMAL}, + #{topUpBalance, jdbcType=DECIMAL}, + #{rewardBalance, jdbcType=DECIMAL}, + #{integralBalance, jdbcType=INTEGER}, + #{consumptionCount, jdbcType=INTEGER}, + #{consumptionTotal, jdbcType=DECIMAL}, + #{lastConsumptionTime, jdbcType=TIMESTAMP}, + #{firstTopUpTime, jdbcType=TIMESTAMP}, + #{invoiceTotal, jdbcType=DECIMAL}, + #{userInnerType, jdbcType=INTEGER}, + #{isDel, jdbcType=INTEGER}, + #{isStatus, jdbcType=INTEGER}, + #{canteenId, jdbcType=VARCHAR}, + #{createBy, jdbcType=VARCHAR}, + #{createTime, jdbcType=TIMESTAMP}, + #{isSync, jdbcType=INTEGER} + ) + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/IStUserFaceimgSubIncremInfoService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/IStUserFaceimgSubIncremInfoService.java new file mode 100644 index 0000000..3cc5e78 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/IStUserFaceimgSubIncremInfoService.java @@ -0,0 +1,14 @@ +package org.jeecg.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.jeecg.user.entity.StUserFaceimgSubIncremInfo; + +/** + * @Description: 支付用户人脸信息增量记录表 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +public interface IStUserFaceimgSubIncremInfoService extends IService { + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISwUserFaceimgSelfService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISwUserFaceimgSelfService.java new file mode 100644 index 0000000..12b7c74 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISwUserFaceimgSelfService.java @@ -0,0 +1,14 @@ +package org.jeecg.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.jeecg.user.entity.SwUserFaceimgSelf; + +/** + * @Description: 用户人脸数据-自研引擎 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +public interface ISwUserFaceimgSelfService extends IService { + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISwUserFaceimgService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISwUserFaceimgService.java new file mode 100644 index 0000000..0b1ac14 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISwUserFaceimgService.java @@ -0,0 +1,48 @@ +package org.jeecg.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.jeecg.common.api.vo.Result; +import org.jeecg.user.entity.SwUserFaceimg; +import org.jeecg.user.vo.UserFaceInfo; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; +import java.util.Map; + +/** + * @Description: 用户人脸数据 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +public interface ISwUserFaceimgService extends IService { + + /** + * 重新获取所有人脸数据到redis中 + */ + void reputUserFaceDataToRedis(); + + String getImgUrlByBase64(String imgSrc); + + /** + * 上传人脸压缩包 + * @param request 文件 + * @param dataType 数据标记 0:食堂,1:体重秤 2:公共 + * @param dataStatus 用户借调数据状态(1:借调数据 ,否则默认非借调数据 ) + * @return 上传人脸压缩包 + */ + Result uploadFaceImagePackage(HttpServletRequest request,Integer dataType,String dataStatus); + + List> getUserInfoNotHaveFaceUrlBySecondDepartId(String secondDepartId); + + /** + * 发送扣费闸机人脸特征数据,删除/更新他本地数据 + * @param userFaceInfo + * @param type type =0 人员特征删除数据 + * type =1 人员特征新增数据 + * type =3 人员特征初始化 + * @param isConfirm 是否需要入库前确认扣费系统人员:true需要确认,false不需要确认 + * @param userId isConfirm= true需要确认 ,根据userId确认 + */ + void sendPaymentGateUserInfo(UserFaceInfo userFaceInfo, Integer type, Boolean isConfirm, String userId) ; +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISwUserFaceimgSubService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISwUserFaceimgSubService.java new file mode 100644 index 0000000..b44dd35 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISwUserFaceimgSubService.java @@ -0,0 +1,28 @@ +package org.jeecg.system.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.jeecg.user.entity.SwUserFaceimgSub; + +import java.util.List; + +/** + * @Description: 用户人脸数据 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +public interface ISwUserFaceimgSubService extends IService { + + /** + * 重新获取所有人脸数据到redis中 + */ + void reputUserFaceDataToRedis(); + + + IPage queryPageList(Page page); + + + List queryPageList(Long timestamp); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISysGatewayRouteService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISysGatewayRouteService.java new file mode 100644 index 0000000..3d8de04 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISysGatewayRouteService.java @@ -0,0 +1,38 @@ +package org.jeecg.system.service; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.extension.service.IService; +import org.jeecg.system.entity.SysGatewayRoute; + +/** + * @Description: gateway路由管理 + * @Author: jeecg-boot + * @Date: 2020-05-26 + * @Version: V1.0 + */ +public interface ISysGatewayRouteService extends IService { + + /** + * 添加所有的路由信息到redis + * @param key + */ + void addRoute2Redis(String key); + + /** + * 删除路由 + * @param id + */ + void deleteById(String id); + + /** + * 保存路由配置 + * @param array + */ + void updateAll(JSONObject array); + + /** + * 清空redis中的route信息 + */ + void clearRedis(); + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISysPayDepartService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISysPayDepartService.java new file mode 100644 index 0000000..64d0bfe --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISysPayDepartService.java @@ -0,0 +1,99 @@ +package org.jeecg.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.jeecg.user.entity.SysPayDepart; +import org.jeecg.user.vo.PayDepartIdModel; +import org.jeecg.user.vo.SysPayDepartTreeModel; + +import java.util.List; + +/** + *

+ * 部门表 服务实现类 + *

+ * + */ +public interface ISysPayDepartService extends IService{ + + + /** + * 根据任意一级部门ID,查询其级联的所有部门ID(包括自身及所有子部门) + * + * @param deptId 部门ID + * @return 级联部门ID集合 + */ + List getAllCascadeDeptIds(String deptId); + + /** + * 根据部门ID查询其完整层级路径(从顶级部门到当前部门) + * 例如:采气厂->定边保障点->定边外雇人员 + * + * @param deptId 部门ID + * @return 层级路径字符串,若部门不存在则返回空 + */ + String getDeptHierarchyName(String deptId); + + /** + * 查询所有部门信息,并分节点进行显示 + * @return + */ + List queryTreeListCach(); + + + + /** + * 根据type查询部门 目前type =1 查询二级部门以上的 + + * @return + */ + List querySysDepartListBy(); + + + /** + * 查询所有部门信息,并分节点进行显示 + * @return + */ + List queryTreeList(String ids); + + /** + * 查询所有部门DepartId信息,并分节点进行显示 + * @return + */ + List queryDepartIdTreeList(); + + /** + * 保存部门数据 + * @param sysDepart + */ + void saveDepartData(SysPayDepart sysDepart,String username); + + /** + * 更新depart数据 + * @param sysDepart + * @return + */ + Boolean updateDepartDataById(SysPayDepart sysDepart,String username); + + + /** + * 根据关键字搜索相关的部门数据 + * @param keyWord + * @return + */ + List searchByKeyWord(String keyWord,String myDeptSearch,String departIds); + + /** + * 根据部门id删除并删除其可能存在的子级部门 + * @param id + * @return + */ + boolean delete(String id); + + /** + * 根据部门id批量删除并删除其可能存在的子级部门 + * @return + */ + void deleteBatchWithChildren(List ids); + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISysUserScreenService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISysUserScreenService.java new file mode 100644 index 0000000..bf81a63 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISysUserScreenService.java @@ -0,0 +1,14 @@ +package org.jeecg.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.jeecg.user.entity.SysUserScreen; + +/** + * @Description: s + * @Author: yucl + * @Date: 2022-04-20 + * @Version: V1.0 + */ +public interface ISysUserScreenService extends IService { + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISysUserService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISysUserService.java new file mode 100644 index 0000000..9ef637c --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISysUserService.java @@ -0,0 +1,40 @@ +package org.jeecg.system.service; + + +import com.baomidou.mybatisplus.extension.service.IService; + +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.vo.ScreenVo; +import org.jeecg.user.entity.SysUser; +import org.jeecg.user.entity.SysUserSub; + +import java.util.List; + +/** + *

+ * 用户表 服务类 + *

+ * + * @Author scott + * @since 2018-12-20 + */ +public interface ISysUserService extends IService { + + + /** + * 编辑用户 + * @param user 用户 + */ + void editUserSub(SysUserSub user); + + /** + * 保存用户 + * @param user 用户 + */ + Result saveUserSub(SysUserSub user); + + Result checkUserIsEffective(SysUser sysUser); + + List listScreenByUser(SysUser user); + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISysUserSubService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISysUserSubService.java new file mode 100644 index 0000000..aa74aee --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/ISysUserSubService.java @@ -0,0 +1,30 @@ +package org.jeecg.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.jeecg.user.entity.SysUserSub; + +import java.util.List; + +/** + *

+ * 子用户表 服务类 + *

+ * + * @Author scott + * @since 2018-12-20 + */ +public interface ISysUserSubService extends IService { + + /** + * 初始化钱包数据 + * innerUser = true 表示内部用户 + * innerUser = false 表示外部用户 + */ + public void initializeUserWallet(List memberId,Boolean innerUser); + /** + * 删除用户 + * @param userId + * @return + */ + boolean deleteUserSub(String userId); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/UserFaceCachePayService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/UserFaceCachePayService.java new file mode 100644 index 0000000..74cf6b5 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/UserFaceCachePayService.java @@ -0,0 +1,25 @@ +package org.jeecg.system.service; + + + +import org.jeecg.user.vo.UserFaceInfo; + +import java.util.List; + +/** + * @author lianlonggang + * @date 2021/3/12 11:37:52 + * @throws + * @since + */ +public interface UserFaceCachePayService { + + public void putUser(UserFaceInfo userInfo) ; + + public void removeUser(String userIds); + + + public List getUserList() ; + + public void init(); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/UserFaceCacheService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/UserFaceCacheService.java new file mode 100644 index 0000000..ca53050 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/UserFaceCacheService.java @@ -0,0 +1,26 @@ +package org.jeecg.system.service; + + +import org.jeecg.user.vo.UserFaceInfo; + +import java.util.List; + +/** + * @author lianlonggang + * @date 2021/3/12 11:37:52 + * @throws + * @since + */ +public interface UserFaceCacheService { + + public void putUser(UserFaceInfo userInfo) ; + + public void init(); + + /** + * 根据用户id和人脸id删除指定人脸数据 + * @param userId + * @param faceId + */ + public void removeUserFaceImgByFaceId(String userId, String faceId); +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/StUserFaceimgSubIncremInfoServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/StUserFaceimgSubIncremInfoServiceImpl.java new file mode 100644 index 0000000..ff90906 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/StUserFaceimgSubIncremInfoServiceImpl.java @@ -0,0 +1,20 @@ +package org.jeecg.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.jeecg.system.mapper.StUserFaceimgSubIncremInfoMapper; +import org.jeecg.system.service.IStUserFaceimgSubIncremInfoService; +import org.jeecg.user.entity.StUserFaceimgSubIncremInfo; +import org.springframework.stereotype.Service; + +/** + * @Description: 支付用户人脸信息增量记录表 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +@Slf4j +@Service +public class StUserFaceimgSubIncremInfoServiceImpl extends ServiceImpl implements IStUserFaceimgSubIncremInfoService { + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SwUserFaceimgServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SwUserFaceimgServiceImpl.java new file mode 100644 index 0000000..8d8a115 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SwUserFaceimgServiceImpl.java @@ -0,0 +1,411 @@ +package org.jeecg.system.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.arcsoft.face.FaceInfo; +import com.arcsoft.face.toolkit.ImageFactory; +import com.arcsoft.face.toolkit.ImageInfo; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveInputStream; +import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; +import org.apache.tomcat.util.http.fileupload.IOUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.util.*; +import org.jeecg.emp.service.ICqBaseEmployeeService; +import org.jeecg.face.service.FaceEngineService; +import org.jeecg.system.mapper.SwUserFaceimgMapper; +import org.jeecg.system.service.IStUserFaceimgSubIncremInfoService; +import org.jeecg.system.service.ISwUserFaceimgService; +import org.jeecg.system.service.UserFaceCacheService; +import org.jeecg.user.entity.StUserFaceimgSubIncremInfo; +import org.jeecg.user.entity.SwUserFaceimg; +import org.jeecg.user.entity.SysUser; +import org.jeecg.user.entity.SysUserSub; +import org.jeecg.user.vo.UserCompareInfo; +import org.jeecg.user.vo.UserFaceInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +/** + * @Description: 用户人脸数据 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +@Slf4j +@Service +public class SwUserFaceimgServiceImpl extends ServiceImpl implements ISwUserFaceimgService { + + @Autowired + private RedisUtil redisUtil; + @Resource + private SwUserFaceimgMapper swUserFaceimgMapper; + @Autowired + private UserFaceCacheService userFaceCacheService; + @Resource + private ICqBaseEmployeeService cqBaseEmployeeService; + @Resource + private ISwUserFaceimgService swUserFaceimgService; + @Resource + private FaceEngineService faceEngineService; + @Resource + private IStUserFaceimgSubIncremInfoService iStUserFaceimgSubIncremInfoService; + @Override + public void reputUserFaceDataToRedis() { + log.info("重新获取人脸数据,注册到内存---开始"); + redisUtil.hdel(CommonConstant.FACE_CACHE); + //查询所有人脸信息 + List faceimgList = swUserFaceimgMapper.getList(); + for (SwUserFaceimg swUserFaceimg : faceimgList) { + UserFaceInfo userInfo = new UserCompareInfo(); + userInfo.setUserId(swUserFaceimg.getUserId()); + userInfo.setUserFaceId(swUserFaceimg.getId()); + userInfo.setDataType(swUserFaceimg.getDataType()); + userInfo.setFaceFeature(swUserFaceimg.getFaceFeatureData()); + userFaceCacheService.putUser(userInfo); + } + if (CollectionUtils.isEmpty(faceimgList)) { + log.info("未找到人脸数据,请录入后重试!"); + } + log.info("重新获取人脸数据,注册到内存---完成"); + } + + @Value(value = "${jeecg.path.upload}") + private String uploadpath; + + @Override + public String getImgUrlByBase64(String imgSrc){ + //将base64图片转为文件 , 先上传 再保存路径 + + String[] parts = imgSrc.split(","); + String encodingPrefix = "base64"; + String encodedData; + String mateData = parts.length > 1 ? parts[0] : null; + + if (parts.length > 1 && parts[0].contains(encodingPrefix)) { + encodedData = parts[1]; + } else { + encodedData = imgSrc; + } + // 解码Base64 + byte[] decodedBytes = Base64.getDecoder().decode(encodedData); + + String bizPath ="temp"; + String name = generateRandomFileName(mateData); + try { + InputStream fileInputStream = new ByteArrayInputStream(decodedBytes); + String fileUrl = LocalUploadUtil.uploadLocal(fileInputStream, bizPath, name); + return fileUrl; + }catch (Exception e){ + e.printStackTrace(); + } + return null; + } + + private static String generateRandomFileName(String metaData) { + // 生成UUID作为基础名称 + String uuid = UUID.randomUUID().toString().substring(0, 8); + + // 根据元数据获取扩展名 + String extension = "png"; // 默认扩展名 + if (metaData != null && metaData.contains("/")) { + String mimeType = metaData.split(";")[0].split("/")[1]; + switch (mimeType) { + case "png": extension = "png"; break; + case "jpeg": extension = "jpg"; break; + case "gif": extension = "gif"; break; + // 添加更多类型支持... + } + } + + return uuid + "." + extension; + } + + @Override + public Result uploadFaceImagePackage(HttpServletRequest request,Integer dataType,String dataStatus){ + + MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; + // 获取文件对象 + MultipartFile file = multipartRequest.getFile("file"); + String bizPath ="temp"; + //解压后文件存储地址 + String destinationFolder = uploadpath + File.separator + bizPath; + try ( + InputStream inputStream = file.getInputStream(); + ArchiveInputStream ais = new ZipArchiveInputStream(inputStream)) { + + ArchiveEntry entry; + List cardNoList = new ArrayList<>(); + Map map = new HashMap<>(); + while ((entry = ais.getNextEntry())!= null) { + // 根据需要修改支持的图片格式 + String[] parts = entry.getName().split("/"); + String name = parts[parts.length - 1]; + if (name.endsWith(".jpg") || name.endsWith(".png") || name.endsWith(".JPG") || name.endsWith(".PNG")) { + String[] split = name.split("\\."); + name = split[0].replaceAll("[^A-Za-z0-9]", "")+"."+split[1]; + name = name.replace('x', 'X'); + String url = destinationFolder + File.separator + name; + File destinationFile = new File(url); + try (FileOutputStream fos = new FileOutputStream(destinationFile)) { + IOUtils.copy(ais, fos); + //key: cardNo; + //value: imgUrl + //裁取.jpg前的串,再裁取数字 + String numbers = name.split("\\.")[0]; + map.put(numbers,url); +// //上传人脸图片到文件服务器 +// FileInputStream fileInputStream = new FileInputStream(destinationFile); +// String fileUrl = MyUploadUtil.upload(fileInputStream, bizPath, name, false); + map.put(numbers+"name",name); + cardNoList.add(numbers); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + if(CollectionUtils.isNotEmpty(cardNoList)){ + //根据身份证获取用户信息 + List sysUserList = cqBaseEmployeeService.getUserList(cardNoList); + + if(CollectionUtils.isNotEmpty(sysUserList)){ + Map finalMap = map; + List collect = sysUserList.stream().map(SysUser::getCardNo).collect(Collectors.toList()); + //库中没有用户信息的列表 + List resultList = cardNoList.stream().filter(idCard -> !collect.contains(idCard)).collect(Collectors.toList()); + //用户人脸图片转base64 + List> userResultMapList = new ArrayList<>(); + sysUserList.forEach(user ->{ + String userId = user.getId(); + String base64 = ""; + String replace = user.getCardNo().replace("x", "X"); + //本地图片 + String imgUrlPath = finalMap.get(replace); + + if(org.apache.commons.lang.StringUtils.isNotBlank(imgUrlPath)){ + base64 = ImgToBase64(imgUrlPath); + } else { + try { + if(org.apache.commons.lang.StringUtils.isNotBlank(user.getAvatar())){ + base64 = DownFileUtil.getBase64(user.getAvatar()); + } + } catch (IOException e) { + //图片转换异常 人员未导入 + resultList.add(user.getCardNo()); + e.printStackTrace(); + } + } + + Map resultMap = new HashMap<>(); + resultMap.put("userId",userId); + resultMap.put("imgSrc","data:image/jpg;base64," + base64); + //文件服务器上传图片 + String name = finalMap.get(replace + "name"); + try{ + //上传人脸图片到文件服务器 + File destinationFile = new File(imgUrlPath); + FileInputStream fileInputStream = new FileInputStream(destinationFile); + String fileUrl = MyUploadUtil.upload(fileInputStream, bizPath, name, false); + resultMap.put("faceImg", fileUrl); + }catch (Exception e){ + System.out.println("文件上传失败"); + e.printStackTrace(); + } + resultMap.put("cardNo",user.getCardNo()); + System.out.println(JSONObject.toJSONString(resultMap)); + userResultMapList.add(resultMap); + + }); + if(CollectionUtils.isNotEmpty(userResultMapList)){ + //删除本地解压的所有图片 + for (Map.Entry stringStringEntry : map.entrySet()) { + String value = stringStringEntry.getValue(); + if(value.contains(bizPath)){ + Files.delete(Paths.get(stringStringEntry.getValue())); + } + } + List list = this.saveFaceImg(userResultMapList,dataType,dataStatus); + if(CollectionUtils.isEmpty(list)){ + return Result.error("人脸识别失败"); + }else { + if(list.size() == cardNoList.size()){ + return Result.OK("成功导入人脸图片"+sysUserList.size()+"条"); + } + + return Result.error("成功导入人脸图片"+list.size()+"条, 未匹配到人员信息/图片转存失败"+resultList.size()+"条 : "+resultList + + "; "); + } + } + }else { + return Result.error("未匹配到人员信息",cardNoList); + } + }else { + return Result.error("压缩包解析身份证号数据为空"); + } + + } catch (IOException e) { + e.printStackTrace(); + } + return Result.error("压缩包解析失败"); + } + + + + public static String ImgToBase64(String imgFile){ + //将图片文件转化为字节数组字符串,并对其进行Base64编码处理 + InputStream in = null; + byte[] data = null; + //读取图片字节数组 + try + { + in = new FileInputStream(imgFile); + data = new byte[in.available()]; + in.read(data); + in.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + return new String(Base64.getEncoder().encode(data)); + } + + + public List saveFaceImg(@RequestBody List> userResultMapList,Integer dataType,String dataStatus) { + SwUserFaceimg slUserFaceimg; + List list = new ArrayList<>(); + for (Map stringStringMap : userResultMapList) { + String userId = stringStringMap.get("userId").toString(); + String imgSrc = stringStringMap.get("imgSrc").toString(); + String cardNo = stringStringMap.get("cardNo").toString(); + String faceImg = stringStringMap.get("faceImg").toString(); + slUserFaceimg = new SwUserFaceimg(); +// slUserFaceimg.setFaceImgDataString(imgSrc); + slUserFaceimg.setFaceImg(faceImg); + ImageInfo rgbData = ImageFactory.getRGBData(Base64Util.base64ToBytes(imgSrc)); + List faceInfoList = faceEngineService.detectFaces(rgbData); + if (CollectionUtils.isNotEmpty(faceInfoList)) { + for (FaceInfo faceInfo : faceInfoList) { + byte[] feature = faceEngineService.extractFaceFeature(rgbData, faceInfo); + slUserFaceimg.setFaceFeatureData(feature); + } + } + slUserFaceimg.setFaceStatus("1"); + slUserFaceimg.setCardNumber(cardNo); + slUserFaceimg.setDataType(dataType); + slUserFaceimg.setUserId(userId); + list.add(slUserFaceimg); + } + //保存 + if(CollectionUtils.isNotEmpty(list)){ + swUserFaceimgService.saveBatch(list); + List userIdList = list.stream().map(SwUserFaceimg::getUserId).collect(Collectors.toList()); + //异步 + CompletableFuture.runAsync(() -> { + userIdList.forEach(userId ->{ + //默认最多保存三张照片 + List slUserFaceimgList = swUserFaceimgService.lambdaQuery().eq(SwUserFaceimg::getUserId, userId) + .eq(SwUserFaceimg::getFaceSource, 0) .eq(SwUserFaceimg::getDataType, 0).eq(SwUserFaceimg::getFaceStatus, "1").orderByDesc(SwUserFaceimg::getCreateTime).list(); + if (slUserFaceimgList.size() > 3) { + List upList = slUserFaceimgList.subList(3, slUserFaceimgList.size()); + if (CollectionUtils.isNotEmpty(upList)) { + upList.forEach(i -> { + i.setFaceStatus("0"); + }); + swUserFaceimgService.saveOrUpdateBatch(upList); + } + } + }); + for (SwUserFaceimg swUserFaceimg : list) { + //添加/更新人脸缓存 + UserFaceInfo userInfo = new UserFaceInfo(); + userInfo.setUserId(swUserFaceimg.getUserId()); + userInfo.setUserFaceId(swUserFaceimg.getId()); + userInfo.setFaceFeature(swUserFaceimg.getFaceFeatureData()); + userInfo.setDataType(swUserFaceimg.getDataType()); + userFaceCacheService.putUser(userInfo); + +// //推送给小马 UserFaceInfo +// //开启线程发送推送信息 +// Thread t = new Thread(() -> { +// //发送推送信息 +// String j = "sw_test"; +// Topic t1 = () -> j; +// userInfo.setFaceFeature(null); +// producer.sendMessage(t1, JSON.toJSONString(userInfo)); +// }); +// t.start(); + } + }); + return userIdList; + + } + return null; + } + + @Override + public List> getUserInfoNotHaveFaceUrlBySecondDepartId(String secondDepartId){ + return baseMapper.getSecondDepartNotHaveFaceUrl(secondDepartId); + } + + /** + * 保存增量数据 扣费闸机人脸特征数据记录到表 + * @param userFaceInfo + * @param type type =0 人员特征删除数据 + * type =1 人员特征新增数据 + * type =3 人员特征初始化 + * @param isConfirm 是否需要入库前确认是扣费系统人员:true需要确认,false不需要确认 + * @param userId + */ + @Override + public void sendPaymentGateUserInfo(UserFaceInfo userFaceInfo, Integer type,Boolean isConfirm,String userId) { + CompletableFuture.runAsync(() -> { + StUserFaceimgSubIncremInfo stUserFaceimgSubIncremInfo = new StUserFaceimgSubIncremInfo(); + //需要发送消息前确认扣费系统人员 + if(isConfirm){ + //查询该特征人员是否是 SysUserSub 扣费表的用户,是就发送消息给扣费闸机,不是不发送 + SysUserSub sysUserSub = swUserFaceimgMapper.getUserSubInfo(userId,null); + if(null != sysUserSub){ + if(type == 1) {//人员特征新增数据 + stUserFaceimgSubIncremInfo.setRealname(sysUserSub.getRealname()); + stUserFaceimgSubIncremInfo.setStatus(sysUserSub.getStatus()); + } + stUserFaceimgSubIncremInfo.setFaceFeatureString(userFaceInfo.getFaceFeatureString()); + stUserFaceimgSubIncremInfo.setFaceType(type); + stUserFaceimgSubIncremInfo.setUserFaceId(userFaceInfo.getUserFaceId()); + stUserFaceimgSubIncremInfo.setUserId(userFaceInfo.getUserId()); + iStUserFaceimgSubIncremInfoService.save(stUserFaceimgSubIncremInfo); + } + } else { + if(type == 1) {//人员特征新增数据 + SysUserSub sysUserSub = swUserFaceimgMapper.getUserSubInfo(userFaceInfo.getUserId(),null); + stUserFaceimgSubIncremInfo.setRealname(sysUserSub.getRealname()); + stUserFaceimgSubIncremInfo.setStatus(sysUserSub.getStatus()); + } + stUserFaceimgSubIncremInfo.setFaceFeatureString(userFaceInfo.getFaceFeatureString()); + stUserFaceimgSubIncremInfo.setFaceType(type); + stUserFaceimgSubIncremInfo.setUserFaceId(userFaceInfo.getUserFaceId()); + stUserFaceimgSubIncremInfo.setUserId(userFaceInfo.getUserId()); + iStUserFaceimgSubIncremInfoService.save(stUserFaceimgSubIncremInfo); + } + }); + + } +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SwUserFaceimgServiceSelfImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SwUserFaceimgServiceSelfImpl.java new file mode 100644 index 0000000..0666f5b --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SwUserFaceimgServiceSelfImpl.java @@ -0,0 +1,18 @@ +package org.jeecg.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.jeecg.system.mapper.SwUserFaceimgSelfMapper; +import org.jeecg.system.service.ISwUserFaceimgSelfService; +import org.jeecg.user.entity.SwUserFaceimgSelf; +import org.springframework.stereotype.Service; + +/** + * @Description: 用户人脸数据-自研引擎 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +@Service +public class SwUserFaceimgServiceSelfImpl extends ServiceImpl implements ISwUserFaceimgSelfService { + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SwUserFaceimgSubServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SwUserFaceimgSubServiceImpl.java new file mode 100644 index 0000000..e51dc87 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SwUserFaceimgSubServiceImpl.java @@ -0,0 +1,86 @@ +package org.jeecg.system.service.impl; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.util.RedisUtil; +import org.jeecg.system.mapper.SwUserFaceimgSubMapper; +import org.jeecg.system.service.ISwUserFaceimgSubService; +import org.jeecg.system.service.UserFaceCachePayService; +import org.jeecg.user.entity.SwUserFaceimgSub; +import org.jeecg.user.vo.UserCompareInfo; +import org.jeecg.user.vo.UserFaceInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; + +/** + * @Description: 用户人脸数据 + * @Author: jeecg-boot + * @Date: 2022-04-12 + * @Version: V1.0 + */ +@Slf4j +@Service +public class SwUserFaceimgSubServiceImpl extends ServiceImpl implements ISwUserFaceimgSubService { + + @Autowired + private RedisUtil redisUtil; + @Resource + private SwUserFaceimgSubMapper swUserFaceimgSubMapper; + @Autowired + private UserFaceCachePayService userFaceCachePayService; + + @Override + public void reputUserFaceDataToRedis() { + log.info("重新获取人脸数据,注册到内存---开始"); + redisUtil.hdel(CommonConstant.FACE_CACHE_PAY); + //查询所有人脸信息 + List faceimgList = swUserFaceimgSubMapper.getList(); + for (SwUserFaceimgSub swUserFaceimg : faceimgList) { + UserFaceInfo userInfo = new UserCompareInfo(); + userInfo.setUserId(swUserFaceimg.getUserId()); + userInfo.setUserFaceId(swUserFaceimg.getId()); + userInfo.setFaceFeature(swUserFaceimg.getFaceFeatureData()); + userFaceCachePayService.putUser(userInfo); + } + if (CollectionUtils.isEmpty(faceimgList)) { + log.info("未找到人脸数据,请录入后重试!"); + } + log.info("重新获取人脸数据,注册到内存---完成"); + } + + + @Override + public IPage queryPageList(Page page) { + IPage swUserFaceimgSubs = swUserFaceimgSubMapper.selectFaceImagesAndSubs(page,null); + if(CollectionUtils.isNotEmpty(swUserFaceimgSubs.getRecords())){ + swUserFaceimgSubs.getRecords().forEach(w->{ + // 重新设置 faceFeatureDataString 的值 + w.setFaceFeatureString(w.getFaceFeatureDataString()); + // 将 faceFeatureData 置为空 + w.setFaceFeatureData(null); + }); + } + return swUserFaceimgSubs; + } + + @Override + public List queryPageList(Long timestamp) { + List swUserFaceimgSubs = swUserFaceimgSubMapper.selectFaceImagesAndSubs(timestamp); + if(CollectionUtils.isNotEmpty(swUserFaceimgSubs)){ + swUserFaceimgSubs.forEach(w->{ + // 重新设置 faceFeatureDataString 的值 + w.setFaceFeatureString(w.getFaceFeatureDataString()); + // 将 faceFeatureData 置为空 + w.setFaceFeatureData(null); + }); + } + return swUserFaceimgSubs; + } +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysBaseApiImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysBaseApiImpl.java new file mode 100644 index 0000000..39ca98b --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysBaseApiImpl.java @@ -0,0 +1,165 @@ +package org.jeecg.system.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.google.common.base.Joiner; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.api.dto.OnlineAuthDTO; +import org.jeecg.common.api.dto.message.*; +import org.jeecg.common.aspect.UrlMatchEnum; +import org.jeecg.common.constant.CacheConstant; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.constant.DataBaseConstant; +import org.jeecg.common.constant.WebsocketConst; +import org.jeecg.common.exception.JeecgBootException; +import org.jeecg.common.system.api.ISysBaseAPI; +import org.jeecg.common.system.query.QueryGenerator; +import org.jeecg.common.system.vo.*; +import org.jeecg.common.util.SysAnnmentTypeEnum; +import org.jeecg.common.util.YouBianCodeUtil; +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.system.mapper.SysUserMapper; +import org.jeecg.system.service.ISysUserService; +import org.jeecg.user.entity.SysUser; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; + +import javax.annotation.Resource; +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; +import java.util.*; + +/** + * @Description: 底层共通业务API,提供其他独立模块调用 + * @Author: scott + * @Date:2019-4-20 + * @Version:V1.0 + */ +@Slf4j +@Service +public class SysBaseApiImpl implements ISysBaseAPI { + /** 当前系统数据库类型 */ + private static String DB_TYPE = ""; +// @Autowired +// private ISysMessageTemplateService sysMessageTemplateService; +// @Resource +// private SysLogMapper sysLogMapper; + @Resource + private SysUserMapper userMapper; + @Resource + private ISysUserService sysUserService; + + @Override + public Set queryUserRoles(String username) { + return null; + } + + @Override + public Set queryUserAuths(String username) { + return null; + } + + @Override + public DynamicDataSourceModel getDynamicDbSourceByCode(String dbSourceCode) { + return null; + } + +// @Resource +// private SysUserRoleMapper sysUserRoleMapper; +// @Autowired +// private ISysDepartService sysDepartService; +// @Autowired +// private ISysDictService sysDictService; +// @Resource +// private SysAnnouncementMapper sysAnnouncementMapper; +// @Resource +// private SysAnnouncementSendMapper sysAnnouncementSendMapper; +// @Resource +// private WebSocket webSocket; +// @Resource +// private SysRoleMapper roleMapper; +// @Resource +// private SysDepartMapper departMapper; +// @Resource +// private SysCategoryMapper categoryMapper; +// +// @Autowired +// private ISysDataSourceService dataSourceService; +// @Autowired +// private ISysUserDepartService sysUserDepartService; +// @Resource +// private SysPermissionMapper sysPermissionMapper; +// @Autowired +// private ISysPermissionDataRuleService sysPermissionDataRuleService; + + @Override + public LoginUser getUserById(String id) { + if(oConvertUtils.isEmpty(id)) { + return null; + } + LoginUser loginUser = new LoginUser(); + SysUser sysUser = userMapper.selectById(id); + if(sysUser==null) { + return null; + } + BeanUtils.copyProperties(sysUser, loginUser); + + BeanUtils.copyProperties(sysUser, loginUser); + if(!"apiuser".equals(loginUser.getUsername())){ + //如果获得用户的食堂数据 + List screenVoList=sysUserService.listScreenByUser(sysUser); + if(CollectionUtil.isNotEmpty(screenVoList)){ + loginUser.setCanteenIdList(screenVoList); + loginUser.setCanteenId(screenVoList.get(0).getId()); + } + } + + return loginUser; + } + + @Override + public List queryPermissionDataRule(String component, String requestPath, String userId) { + return null; + } + + @Override + public SysUserCacheInfo getCacheUser(String userId) { + return null; + } + + @Override + public List queryDictItemsByCode(String code) { + return null; + } + + @Override + public List queryTableDictItemsByCode(String table, String text, String code) { + return null; + } + + @Override + public Map> translateManyDict(String dictCodes, String keys) { + return null; + } + + @Override + public List translateDictFromTableByKeys(String table, String text, String code, String keys) { + return null; + } + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysGatewayRouteServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysGatewayRouteServiceImpl.java new file mode 100644 index 0000000..396cd78 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysGatewayRouteServiceImpl.java @@ -0,0 +1,110 @@ +package org.jeecg.system.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.base.BaseMap; +import org.jeecg.common.constant.CacheConstant; +import org.jeecg.common.constant.GlobalConstants; +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.system.entity.SysGatewayRoute; +import org.jeecg.system.mapper.SysGatewayRouteMapper; +import org.jeecg.system.service.ISysGatewayRouteService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * @Description: gateway路由管理 + * @Author: jeecg-boot + * @Date: 2020-05-26 + * @Version: V1.0 + */ +@Service +@Slf4j +public class SysGatewayRouteServiceImpl extends ServiceImpl implements ISysGatewayRouteService { + + @Autowired + private RedisTemplate redisTemplate; + + private static final String STRING_STATUS = "status"; + + @Override + public void addRoute2Redis(String key) { + List ls = this.list(new LambdaQueryWrapper()); + redisTemplate.opsForValue().set(key, JSON.toJSONString(ls)); + } + + @Override + public void deleteById(String id) { + this.removeById(id); + this.resreshRouter(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateAll(JSONObject json) { + log.info("--gateway 路由配置修改--"); + try { + json = json.getJSONObject("router"); + String id = json.getString("id"); + //update-begin-author:taoyan date:20211025 for: oracle路由网关新增小bug /issues/I4EV2J + SysGatewayRoute route; + if(oConvertUtils.isEmpty(id)){ + route = new SysGatewayRoute(); + }else{ + route = getById(id); + } + //update-end-author:taoyan date:20211025 for: oracle路由网关新增小bug /issues/I4EV2J + if (ObjectUtil.isEmpty(route)) { + route = new SysGatewayRoute(); + } + route.setRouterId(json.getString("routerId")); + route.setName(json.getString("name")); + route.setPredicates(json.getString("predicates")); + String filters = json.getString("filters"); + if (ObjectUtil.isEmpty(filters)) { + filters = "[]"; + } + route.setFilters(filters); + route.setUri(json.getString("uri")); + if (json.get(STRING_STATUS) == null) { + route.setStatus(1); + } else { + route.setStatus(json.getInteger(STRING_STATUS)); + } + this.saveOrUpdate(route); + resreshRouter(null); + } catch (Exception e) { + log.error("路由配置解析失败", e); + resreshRouter(null); + e.printStackTrace(); + } + } + + /** + * 更新redis路由缓存 + */ + private void resreshRouter(String delRouterId) { + //更新redis路由缓存 + addRoute2Redis(CacheConstant.GATEWAY_ROUTES); + BaseMap params = new BaseMap(); + params.put(GlobalConstants.HANDLER_NAME, GlobalConstants.LODER_ROUDER_HANDLER); + params.put("delRouterId", delRouterId); + //刷新网关 + redisTemplate.convertAndSend(GlobalConstants.REDIS_TOPIC_NAME, params); + } + + @Override + public void clearRedis() { + redisTemplate.opsForValue().set(CacheConstant.GATEWAY_ROUTES, null); + } + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysPayDepartServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysPayDepartServiceImpl.java new file mode 100644 index 0000000..7fa845c --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysPayDepartServiceImpl.java @@ -0,0 +1,404 @@ +package org.jeecg.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import io.netty.util.internal.StringUtil; +import org.apache.commons.lang.StringUtils; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.system.mapper.SysPayDepartMapper; +import org.jeecg.system.service.ISysPayDepartService; +import org.jeecg.system.util.FindsPayDepartsChildrenUtil; +import org.jeecg.user.entity.SysPayDepart; +import org.jeecg.user.vo.PayDepartIdModel; +import org.jeecg.user.vo.SysPayDepartTreeModel; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + *

+ * 部门表 服务实现类 + *

+ * + * @Author Steve + * @Since 2019-01-22 + */ +@Service +public class SysPayDepartServiceImpl extends ServiceImpl implements ISysPayDepartService { + + + + /** + * 根据任意一级部门ID,查询其级联的所有部门ID(包括自身及所有子部门) + * @param deptId 部门ID + * @return 级联部门ID集合 + */ + @Override + public List getAllCascadeDeptIds(String deptId) { + List allDeptIds = new ArrayList<>(); + if (deptId == null) { + return allDeptIds; + } + // 从目标部门开始递归查询 + recursiveQueryDeptIds(deptId, allDeptIds); + return allDeptIds; + } + + /** + * 递归查询部门ID及其所有子部门ID + * @param deptId 当前部门ID + * @param result 用于收集结果的集合 + */ + private void recursiveQueryDeptIds(String deptId, List result) { + // 1. 先查询当前部门信息并添加到结果集(去重) + if (!result.contains(deptId)) { + // 验证当前部门是否存在且有效 + SysPayDepart currentDept = this.getOne(new LambdaQueryWrapper() + .eq(SysPayDepart::getId, deptId) + .eq(SysPayDepart::getDelFlag, "0")); + + if (currentDept != null) { + result.add(deptId); + } else { + // 当前部门不存在或已删除,终止递归 + return; + } + } + + // 2. 查询当前部门的直接子部门 + List childDepts = this.list(new LambdaQueryWrapper() + .eq(SysPayDepart::getParentId, deptId) + .eq(SysPayDepart::getDelFlag, "0")); + + // 3. 对每个子部门递归查询其下属部门 + for (SysPayDepart child : childDepts) { + String childId = child.getId(); + // 递归处理子部门,会自动添加子部门及其下级 + recursiveQueryDeptIds(childId, result); + } + } + + + /** + * 根据部门ID查询其完整层级路径(从顶级部门到当前部门) + * 例如:采气厂->定边保障点->定边外雇人员 + * @param deptId 部门ID + * @return 层级路径字符串,若部门不存在则返回空 + */ + @Override + public String getDeptHierarchyName(String deptId) { + if (deptId == null) { + return ""; + } + + // 存储层级名称的链表(从当前部门向上追溯) + LinkedList hierarchyNames = new LinkedList<>(); + // 递归追溯上级部门 + boolean hasValidDept = recursiveQueryHierarchy(deptId, hierarchyNames); + + if (!hasValidDept || hierarchyNames.isEmpty()) { + return ""; + } + + // 拼接层级路径(从顶级到当前部门) + return String.join("->", hierarchyNames); + } + + /** + * 递归追溯部门的上级部门,构建层级名称列表 + * @param deptId 当前部门ID + * @param hierarchyNames 用于存储层级名称的链表(从当前部门向上累积) + * @return 是否存在有效的部门 + */ + private boolean recursiveQueryHierarchy(String deptId, LinkedList hierarchyNames) { + // 查询当前部门信息 + SysPayDepart currentDept = this.getOne(new LambdaQueryWrapper() + .eq(SysPayDepart::getId, deptId) + .eq(SysPayDepart::getDelFlag, "0")); + + if (currentDept == null) { + return false; // 部门不存在或已删除 + } + + // 将当前部门名称添加到链表头部(保证从顶级到当前的顺序) + hierarchyNames.addFirst(currentDept.getDepartName()); + + // 如果存在上级部门,继续向上追溯 + String parentId = currentDept.getParentId(); + if (parentId != null && !parentId.isEmpty()) { + // 递归查询上级部门 + recursiveQueryHierarchy(parentId, hierarchyNames); + } + + return true; + } + + /** + * queryTreeList 对应 queryTreeList 查询所有的部门数据,以树结构形式响应给前端 + */ + @Override + public List queryTreeListCach() { + LambdaQueryWrapper query = new LambdaQueryWrapper(); + query.eq(SysPayDepart::getDelFlag, CommonConstant.DEL_FLAG_0.toString()); + query.orderByAsc(SysPayDepart::getDepartOrder); + List list = this.list(query); + // 调用wrapTreeDataToTreeList方法生成树状数据 + List listResult = FindsPayDepartsChildrenUtil.wrapTreeDataToTreeList(list); + return listResult; + } + + + @Override + public List querySysDepartListBy() { + LambdaQueryWrapper query = new LambdaQueryWrapper(); + query.eq(SysPayDepart::getDelFlag, CommonConstant.DEL_FLAG_0.toString()); + LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal(); + if (user.getUserIdentity().equals(CommonConstant.USER_IDENTITY_1) || user.getUserIdentity().equals(CommonConstant.USER_IDENTITY_0)) { + query.apply(" char_length(org_code)=6"); + } else { + if(StringUtils.isNotBlank(user.getDepartIds())){ + query.eq(SysPayDepart::getId, user.getDepartIds()); + } else { + query.eq(SysPayDepart::getOrgCode, user.getSysOrgCode()); + } + } + query.orderByAsc(SysPayDepart::getDepartOrder); + List list = this.list(query); + + return list; + } + + /** + * queryTreeList 根据部门id查询,前端回显调用 + */ + @Override + public List queryTreeList(String ids) { + List listResult = new ArrayList<>(); + LambdaQueryWrapper query = new LambdaQueryWrapper(); + query.eq(SysPayDepart::getDelFlag, CommonConstant.DEL_FLAG_0.toString()); + if (oConvertUtils.isNotEmpty(ids)) { + query.in(true, SysPayDepart::getId, ids.split(",")); + } + query.orderByAsc(SysPayDepart::getDepartOrder); + List list = this.list(query); + for (SysPayDepart depart : list) { + listResult.add(new SysPayDepartTreeModel(depart)); + } + return listResult; + + } + + @Override + public List queryDepartIdTreeList() { + LambdaQueryWrapper query = new LambdaQueryWrapper(); + query.eq(SysPayDepart::getDelFlag, CommonConstant.DEL_FLAG_0.toString()); + query.orderByAsc(SysPayDepart::getDepartOrder); + List list = this.list(query); + // 调用wrapTreeDataToTreeList方法生成树状数据 + List listResult = FindsPayDepartsChildrenUtil.wrapTreeDataToDepartIdTreeList(list); + return listResult; + } + + /** + * saveDepartData 对应 add 保存用户在页面添加的新的部门对象数据 + */ + @Override + @Transactional + public void saveDepartData(SysPayDepart sysDepart, String userId) { + if (sysDepart != null && userId != null) { + String s = UUID.randomUUID().toString().replace("-", ""); + sysDepart.setId(s); + // 先判断该对象有无父级ID,有则意味着不是最高级,否则意味着是最高级 + String parentId = sysDepart.getParentId(); + if(org.apache.commons.lang3.StringUtils.isNotBlank(parentId)){ + sysDepart.setParentId(parentId); + } + sysDepart.setCreateTime(new Date()); + sysDepart.setDelFlag(CommonConstant.DEL_FLAG_0.toString()); + sysDepart.setIsSync(0); + this.save(sysDepart); + } + + } + + /** + * updateDepartDataById 对应 edit 根据部门主键来更新对应的部门数据 + */ + @Override + @Transactional + public Boolean updateDepartDataById(SysPayDepart sysDepart, String userId) { + if (sysDepart != null && userId != null) { + sysDepart.setUpdateTime(new Date()); + sysDepart.setUpdateBy(userId); + sysDepart.setIsSync(0); + this.updateById(sysDepart); + return true; + } else { + return false; + } + + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteBatchWithChildren(List ids) { + List idList = new ArrayList(); + for (String id : ids) { + idList.add(id); + this.checkChildrenExists(id, idList); + } + List sysDepartList = this.listByIds(idList); + boolean flag = this.removeByIds(idList); + } + + + /** + *

+ * 根据关键字搜索相关的部门数据 + *

+ */ + @Override + public List searchByKeyWord(String keyWord, String myDeptSearch, String departIds) { + LambdaQueryWrapper query = new LambdaQueryWrapper(); + List newList = new ArrayList<>(); + //myDeptSearch不为空时为我的部门搜索,只搜索所负责部门 + if (!StringUtil.isNullOrEmpty(myDeptSearch)) { + //departIds 为空普通用户或没有管理部门 + if (StringUtil.isNullOrEmpty(departIds)) { + return newList; + } + //根据部门id获取所负责部门 + String[] codeArr = this.getMyDeptParentOrgCode(departIds); + //update-begin-author:taoyan date:20220104 for:/issues/3311 当用户属于两个部门的时候,且这两个部门没有上下级关系,我的部门-部门名称查询条件模糊搜索失效! + if (codeArr != null && codeArr.length > 0) { + query.nested(i -> { + for (String s : codeArr) { + i.or().likeRight(SysPayDepart::getOrgCode, s); + } + }); + } + //update-end-author:taoyan date:20220104 for:/issues/3311 当用户属于两个部门的时候,且这两个部门没有上下级关系,我的部门-部门名称查询条件模糊搜索失效! + query.eq(SysPayDepart::getDelFlag, CommonConstant.DEL_FLAG_0.toString()); + } + query.like(SysPayDepart::getDepartName, keyWord); + //update-begin--Author:huangzhilin Date:20140417 for:[bugfree号]组织机构搜索回显优化-------------------- + SysPayDepartTreeModel model = new SysPayDepartTreeModel(); + List departList = this.list(query); + if (departList.size() > 0) { + for (SysPayDepart depart : departList) { + model = new SysPayDepartTreeModel(depart); + model.setChildren(null); + //update-end--Author:huangzhilin Date:20140417 for:[bugfree号]组织机构搜索功回显优化---------------------- + newList.add(model); + } + return newList; + } + return null; + } + + /** + * 根据部门id删除并且删除其可能存在的子级任何部门 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean delete(String id) { + List idList = new ArrayList<>(); + idList.add(id); + this.checkChildrenExists(id, idList); + boolean ok = this.removeByIds(idList); + return ok; + } + + /** + * delete 方法调用 + * + * @param id + * @param idList + */ + private void checkChildrenExists(String id, List idList) { + LambdaQueryWrapper query = new LambdaQueryWrapper(); + query.eq(SysPayDepart::getParentId, id); + List departList = this.list(query); + if (departList != null && departList.size() > 0) { + for (SysPayDepart depart : departList) { + idList.add(depart.getId()); + this.checkChildrenExists(depart.getId(), idList); + } + } + } + + + /** + * 根据用户所负责部门ids获取父级部门编码 + * + * @param departIds + * @return + */ + private String[] getMyDeptParentOrgCode(String departIds) { + //根据部门id查询所负责部门 + LambdaQueryWrapper query = new LambdaQueryWrapper(); + query.eq(SysPayDepart::getDelFlag, CommonConstant.DEL_FLAG_0.toString()); + query.in(SysPayDepart::getId, Arrays.asList(departIds.split(","))); + query.orderByAsc(SysPayDepart::getOrgCode); + List list = this.list(query); + //查找根部门 + if (list == null || list.size() == 0) { + return null; + } + String orgCode = this.getMyDeptParentNode(list); + String[] codeArr = orgCode.split(","); + return codeArr; + } + + /** + * 获取负责部门父节点 + * + * @param list + * @return + */ + private String getMyDeptParentNode(List list) { + Map map = new HashMap<>(); + //1.先将同一公司归类 + for (SysPayDepart dept : list) { + String code = dept.getOrgCode().substring(0, 3); + if (map.containsKey(code)) { + String mapCode = map.get(code) + "," + dept.getOrgCode(); + map.put(code, mapCode); + } else { + map.put(code, dept.getOrgCode()); + } + } + StringBuffer parentOrgCode = new StringBuffer(); + //2.获取同一公司的根节点 + for (String str : map.values()) { + String[] arrStr = str.split(","); + parentOrgCode.append(",").append(this.getMinLengthNode(arrStr)); + } + return parentOrgCode.substring(1); + } + + /** + * 获取同一公司中部门编码长度最小的部门 + * + * @param str + * @return + */ + private String getMinLengthNode(String[] str) { + int min = str[0].length(); + String orgCode = str[0]; + for (int i = 1; i < str.length; i++) { + if (str[i].length() <= min) { + min = str[i].length(); + orgCode = orgCode + "," + str[i]; + } + } + return orgCode; + } + + + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysUserScreenServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysUserScreenServiceImpl.java new file mode 100644 index 0000000..d5fd00f --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysUserScreenServiceImpl.java @@ -0,0 +1,18 @@ +package org.jeecg.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.jeecg.system.mapper.SysUserScreenMapper; +import org.jeecg.system.service.ISysUserScreenService; +import org.jeecg.user.entity.SysUserScreen; +import org.springframework.stereotype.Service; + +/** + * @Description: s + * @Author: yucl + * @Date: 2022-04-20 + * @Version: V1.0 + */ +@Service +public class SysUserScreenServiceImpl extends ServiceImpl implements ISysUserScreenService { + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysUserServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..d11817b --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysUserServiceImpl.java @@ -0,0 +1,117 @@ +package org.jeecg.system.service.impl; + + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.system.vo.ScreenVo; +import org.jeecg.modules.base.service.BaseCommonService; +import org.jeecg.system.mapper.SysUserMapper; +import org.jeecg.system.mapper.SysUserSubMapper; +import org.jeecg.system.service.ISysUserService; +import org.jeecg.system.service.ISysUserSubService; +import org.jeecg.user.entity.SysUser; +import org.jeecg.user.entity.SysUserSub; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.Collections; +import java.util.List; + +/** + *

+ * 用户表 服务实现类 + *

+ * + * @Author: scott + * @Date: 2018-12-20 + */ +@Service +@Slf4j +public class SysUserServiceImpl extends ServiceImpl implements ISysUserService { + + + @Resource + private SysUserSubMapper sysUserSubMapper; + + @Resource + private ISysUserSubService iSysUserSubService; + @Resource + private BaseCommonService baseCommonService; + + @Override + @Transactional(rollbackFor = Exception.class) + public void editUserSub(SysUserSub user) { + sysUserSubMapper.updateById(user); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Result saveUserSub(SysUserSub user) { + if(org.apache.commons.lang3.StringUtils.isNotBlank(user.getMainUserId())){ + LambdaQueryWrapper sysUserLambdaQueryWrapper = new LambdaQueryWrapper<>(); + sysUserLambdaQueryWrapper.eq(SysUserSub::getUsername,user.getUsername()); + sysUserLambdaQueryWrapper.eq(SysUserSub::getMainUserId,user.getMainUserId()); + List list = sysUserSubMapper.selectList(sysUserLambdaQueryWrapper); + if(CollectionUtils.isNotEmpty(list)){ + return Result.error("该主用户下名称【"+user.getUsername()+"】已存在"); + } + } + //step.1 保存用户 + sysUserSubMapper.insert(user); + if(2 != user.getUserType()){//只有正式员工、非正式员工才创建钱包 + //创建钱包 + iSysUserSubService.initializeUserWallet(Collections.singletonList(user.getId()),false); + } + return Result.OK(); + } + @Override + public Result checkUserIsEffective(SysUser sysUser) { + Result result = new Result(); + //情况1:根据用户信息查询,该用户不存在 + if (sysUser == null) { + result.error500("该用户不存在,请注册"); + baseCommonService.addLog("用户登录失败,用户不存在!", CommonConstant.LOG_TYPE_1, null); + return result; + } + //情况2:根据用户信息查询,该用户已注销 + //update-begin---author:王帅 Date:20200601 for:if条件永远为falsebug------------ + if (CommonConstant.DEL_FLAG_1.equals(sysUser.getDelFlag()) && !StringUtils.equals(CommonConstant.SYSTEM_NAME, sysUser.getUsername())) { + //update-end---author:王帅 Date:20200601 for:if条件永远为falsebug------------ + baseCommonService.addLog("用户登录失败,用户名:" + sysUser.getUsername() + "已注销!", CommonConstant.LOG_TYPE_1, null); + result.error500("该用户已注销"); + return result; + } + //情况3:根据用户信息查询,该用户已冻结 + if (CommonConstant.USER_FREEZE.equals(sysUser.getStatus())) { + baseCommonService.addLog("用户登录失败,用户名:" + sysUser.getUsername() + "已冻结!", CommonConstant.LOG_TYPE_1, null); + result.error500("该用户已冻结"); + return result; + } + return result; + } + @Override + public List listScreenByUser(SysUser user) { + log.error("这里转换SysUser====="+ JSONObject.toJSONString(user)); + List list = null; + if (user.getUserIdentity() < CommonConstant.USER_IDENTITY_4) { +// if("A01A37".equals(user.getSysOrgCode())){ +// user.setSysOrgCode("A01A02");//这里转换下,机关食堂的部门编码由于之前数据是通过sql批量执行的给手动设置了 A01A02, +// // 后面同步完组织机构后编码实际是 A01A37,用户sysOrgCode也是A01A37,但是食堂编码已经按照A01A02生成 +// } + log.error("这里转换user.getSysOrgCode()====="+ user.getSysOrgCode()); + list = baseMapper.queryScreenVOByorgCode(user.getSysOrgCode()); + log.error("这里转换list====="+ JSONArray.toJSONString(list)); + } else { + list = baseMapper.queryScreenVOByUserId(user.getId()); + } + return list; + } +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysUserSubServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysUserSubServiceImpl.java new file mode 100644 index 0000000..62b64ea --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/SysUserSubServiceImpl.java @@ -0,0 +1,97 @@ +package org.jeecg.system.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.util.DateUtils; +import org.jeecg.system.mapper.SysUserSubMapper; +import org.jeecg.system.service.ISysUserSubService; +import org.jeecg.user.entity.SysUserSub; +import org.jeecg.user.vo.StClientMemberBalanceVo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +/** + *

+ * 子用户表 服务实现类 + *

+ * + * @Author: scott + * @Date: 2018-12-20 + */ +@Service +@Slf4j +public class SysUserSubServiceImpl extends ServiceImpl implements ISysUserSubService { + + @Resource + private SysUserSubMapper sysUserSubMapper; + + + /** + * 初始化钱包数据 + * * innerUser = true 表示内部用户 + * * innerUser = false 表示外部用户 + * @param memberIds + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void initializeUserWallet(List memberIds,Boolean innerUser) { + if(CollectionUtil.isNotEmpty(memberIds)){ + memberIds.forEach(memberId->{ + // 根据 memberId 查询用户数据 + StClientMemberBalanceVo balance = sysUserSubMapper.selectByMemberId(memberId); + if (balance == null || StringUtils.isBlank(balance.getId())) { + // 如果用户数据不存在,则创建新记录 + StClientMemberBalanceVo newBalance = new StClientMemberBalanceVo(); + newBalance.setId(UUID.randomUUID().toString()); + newBalance.setMemberId(memberId); + newBalance.setTopUpTotal(BigDecimal.ZERO); + newBalance.setTopUpBalance(BigDecimal.ZERO); + newBalance.setRewardBalance(BigDecimal.ZERO); + newBalance.setIntegralBalance(0); + newBalance.setConsumptionCount(0); + newBalance.setConsumptionTotal(BigDecimal.ZERO); + newBalance.setLastConsumptionTime(null); + newBalance.setFirstTopUpTime(null); + newBalance.setInvoiceTotal(BigDecimal.ZERO); + if(innerUser){ + newBalance.setUserInnerType(0); // 默认内部用户 + } else { + newBalance.setUserInnerType(1); // 默认外部用户 + } + newBalance.setIsDel(0); // 默认未删除 + newBalance.setIsStatus(0); + newBalance.setCreateBy("system"); // 默认创建人 + newBalance.setCreateTime(new Date()); + newBalance.setIsSync(0); // 默认未同步 + // 插入新记录 + sysUserSubMapper.insertStClientMemberBalance(newBalance); + } + }); + } + // 如果用户数据已存在,则无需操作,直接跳过 + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteUserSub(String userId) { + //1.逻辑删除用户 + SysUserSub sysUserSub = this.getById(userId); + if(null != sysUserSub){ + sysUserSub.setDelFlag(1); + String msg = DateUtils.formatDateTime() + "用户【"+sysUserSub.getRealname()+"】被删除"; + sysUserSub.setRemark(sysUserSub.getRemark()+","+msg); + sysUserSub.setUpdateTime(new Date()); + this.updateById(sysUserSub); + } + return true; + } + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/UserFaceCachePayServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/UserFaceCachePayServiceImpl.java new file mode 100644 index 0000000..648afa2 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/UserFaceCachePayServiceImpl.java @@ -0,0 +1,111 @@ +package org.jeecg.system.service.impl; + +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.util.RedisUtil; +import org.jeecg.system.service.ISwUserFaceimgService; +import org.jeecg.system.service.UserFaceCachePayService; +import org.jeecg.user.entity.SwUserFaceimg; +import org.jeecg.user.vo.UserFaceInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author lianlonggang + * @date 2021/3/12 11:38:56 + * @throws + * @since + */ +@Service +@Transactional +@Slf4j +public class UserFaceCachePayServiceImpl implements UserFaceCachePayService { + @Autowired + private RedisUtil redisUtil; + @Autowired + private ISwUserFaceimgService swUserFaceimgService; + @Override + public void init() { + redisUtil.removeAll(CommonConstant.FACE_CACHE_PAY); + + //查询所有人脸数据 + List userFaceimgList = swUserFaceimgService.lambdaQuery().eq(SwUserFaceimg::getFaceStatus, "1").select(SwUserFaceimg::getUserId, SwUserFaceimg::getId, SwUserFaceimg::getFaceFeatureData).list(); + for (SwUserFaceimg swUserFaceimg : userFaceimgList) { + UserFaceInfo userInfo = new UserFaceInfo(); + userInfo.setUserId(swUserFaceimg.getUserId()); + userInfo.setUserFaceId(swUserFaceimg.getId()); + userInfo.setFaceFeature(swUserFaceimg.getFaceFeatureData()); + putUser(userInfo); + } + } + + @Override + public void putUser(UserFaceInfo userInfo) { + if( null == userInfo.getFaceFeature() || userInfo.getFaceFeature().length == 0){ + return; + } + //获取用户人脸存量数据 + List userFaceInfoList = new ArrayList<>(); + List valueOld = (List) redisUtil.hget(CommonConstant.FACE_CACHE_PAY, userInfo.getUserId()); + if(CollectionUtils.isNotEmpty(valueOld)){ + //排除掉绑盘机历史的一条数据 + List imgList = valueOld.stream().filter(img -> !img.getUserFaceId().equals(userInfo.getUserFaceId())).collect(Collectors.toList()); + userFaceInfoList.addAll(imgList); + } + userFaceInfoList.add(userInfo); + redisUtil.hset(CommonConstant.FACE_CACHE_PAY,userInfo.getUserId(),userFaceInfoList); + } + + + + + + @Override + public void removeUser(String userIds) { + if (StringUtils.isNotEmpty(userIds)){ + String[] userId = userIds.split(","); + for (String id : userId) { + redisUtil.hdel(CommonConstant.FACE_CACHE_PAY, id); + } + } + } + + + @Override + public List getUserList() { + List userInfoList = Lists.newLinkedList(); + Map hmget = redisUtil.hmget(CommonConstant.FACE_CACHE_PAY); + if (hmget == null || Objects.requireNonNull(hmget).size() == 0) { + init(); + buildUserFaceInfo(redisUtil.hmget(CommonConstant.FACE_CACHE_PAY), userInfoList); + } else { + buildUserFaceInfo(hmget, userInfoList); + } + + return userInfoList; + } + + private void buildUserFaceInfo( Map hmget,List userInfoList){ + for (Map.Entry entry : hmget.entrySet()) { + List userFaceInfo = (List)entry.getValue(); + if(CollectionUtils.isNotEmpty(userFaceInfo)){ + userFaceInfo.forEach(userFaceInfo1 -> { + try { + userFaceInfo1.setFaceFeatureString(Base64.encodeBase64String(userFaceInfo1.getFaceFeature())); + userInfoList.add(userFaceInfo1); + }catch (Exception ex){ + } + }); + } + } + } + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/UserFaceCacheServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/UserFaceCacheServiceImpl.java new file mode 100644 index 0000000..5211688 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/service/impl/UserFaceCacheServiceImpl.java @@ -0,0 +1,87 @@ +package org.jeecg.system.service.impl; + + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.util.RedisUtil; +import org.jeecg.system.service.ISwUserFaceimgService; +import org.jeecg.system.service.UserFaceCacheService; +import org.jeecg.user.entity.SwUserFaceimg; +import org.jeecg.user.vo.UserFaceInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author lianlonggang + * @date 2021/3/12 11:38:56 + * @throws + * @since + */ +@Service +@Transactional +@Slf4j +public class UserFaceCacheServiceImpl implements UserFaceCacheService { + @Autowired + private RedisUtil redisUtil; + @Autowired + private ISwUserFaceimgService swUserFaceimgService; + + private static final String FACE_CACHE = "FACE_CACHE"; + + + + @Override + public void init() { + redisUtil.removeAll(FACE_CACHE); + + //查询所有人脸数据 + List userFaceimgList = swUserFaceimgService.lambdaQuery() + .eq(SwUserFaceimg::getFaceStatus, "1").select(SwUserFaceimg::getUserId, SwUserFaceimg::getId, SwUserFaceimg::getFaceFeatureData,SwUserFaceimg::getDataType).list(); + for (SwUserFaceimg swUserFaceimg : userFaceimgList) { + UserFaceInfo userInfo = new UserFaceInfo(); + userInfo.setUserId(swUserFaceimg.getUserId()); + userInfo.setUserFaceId(swUserFaceimg.getId()); + userInfo.setFaceFeature(swUserFaceimg.getFaceFeatureData()); + userInfo.setDataType(swUserFaceimg.getDataType()); + putUser(userInfo); + } + } + + @Override + public void putUser(UserFaceInfo userInfo) { + if( null == userInfo.getFaceFeature() || userInfo.getFaceFeature().length == 0){ + return; + } + //获取用户人脸存量数据 + List userFaceInfoList = new ArrayList<>(); + List valueOld = (List) redisUtil.hget(FACE_CACHE, userInfo.getUserId()); + if(CollectionUtils.isNotEmpty(valueOld)){ + //排除掉绑盘机历史的一条数据 + List imgList = valueOld.stream().filter(img -> !img.getUserFaceId().equals(userInfo.getUserFaceId())).collect(Collectors.toList()); + userFaceInfoList.addAll(imgList); + } + userFaceInfoList.add(userInfo); + redisUtil.hset(FACE_CACHE,userInfo.getUserId(),userFaceInfoList); + } + + @Override + public void removeUserFaceImgByFaceId(String userId,String faceId) { + if (StringUtils.isNotEmpty(faceId) && StringUtils.isNotEmpty(userId)){ + List valueOld = (List) redisUtil.hget(FACE_CACHE, userId); + if(CollectionUtils.isNotEmpty(valueOld)){ + List valueNew = valueOld.stream().filter(img -> !img.getUserFaceId().equals(faceId)).collect(Collectors.toList()); + if(CollectionUtils.isNotEmpty(valueNew)){//删除掉某个人脸,还有人脸,进行替换 + redisUtil.hset(FACE_CACHE,userId,valueNew); + } else {//删除掉某个人脸,没有人脸,进行删除用户key + redisUtil.hdel(FACE_CACHE, userId); + } + } + } + } + +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/util/FindsPayDepartsChildrenUtil.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/util/FindsPayDepartsChildrenUtil.java new file mode 100644 index 0000000..8f34ce6 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/system/util/FindsPayDepartsChildrenUtil.java @@ -0,0 +1,127 @@ +package org.jeecg.system.util; + +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.user.entity.SysPayDepart; +import org.jeecg.user.vo.PayDepartIdModel; +import org.jeecg.user.vo.SysPayDepartTreeModel; + +import java.util.ArrayList; +import java.util.List; + +/** + *

+ * 对应部门的表,处理并查找树级数据 + *

+ * + * @Author: Steve + * @Date: 2019-01-22 + */ +public class FindsPayDepartsChildrenUtil { + + //部门树信息-树结构 + //private static List sysDepartTreeList = new ArrayList(); + + //部门树id-树结构 + //private static List idList = new ArrayList<>(); + + + /** + * queryTreeList的子方法 ====1===== + * 该方法是s将SysDepart类型的list集合转换成SysDepartTreeModel类型的集合 + */ + public static List wrapTreeDataToTreeList(List recordList) { + // 在该方法每请求一次,都要对全局list集合进行一次清理 + //idList.clear(); + List idList = new ArrayList(); + List records = new ArrayList<>(); + for (int i = 0; i < recordList.size(); i++) { + SysPayDepart depart = recordList.get(i); + records.add(new SysPayDepartTreeModel(depart)); + } + List tree = findChildren(records, idList); + setEmptyChildrenAsNull(tree); + return tree; + } + + /** + * 获取 DepartIdModel + * @param recordList + * @return + */ + public static List wrapTreeDataToDepartIdTreeList(List recordList) { + // 在该方法每请求一次,都要对全局list集合进行一次清理 + //idList.clear(); + List idList = new ArrayList(); + List records = new ArrayList<>(); + for (int i = 0; i < recordList.size(); i++) { + SysPayDepart depart = recordList.get(i); + records.add(new SysPayDepartTreeModel(depart)); + } + findChildren(records, idList); + return idList; + } + + /** + * queryTreeList的子方法 ====2===== + * 该方法是找到并封装顶级父类的节点到TreeList集合 + */ + private static List findChildren(List recordList, + List departIdList) { + + List treeList = new ArrayList<>(); + for (int i = 0; i < recordList.size(); i++) { + SysPayDepartTreeModel branch = recordList.get(i); + if (oConvertUtils.isEmpty(branch.getParentId())) { + treeList.add(branch); + PayDepartIdModel departIdModel = new PayDepartIdModel().convert(branch); + departIdList.add(departIdModel); + } + } + getGrandChildren(treeList,recordList,departIdList); + + //idList = departIdList; + return treeList; + } + + /** + * queryTreeList的子方法====3==== + *该方法是找到顶级父类下的所有子节点集合并封装到TreeList集合 + */ + private static void getGrandChildren(List treeList,List recordList,List idList) { + + for (int i = 0; i < treeList.size(); i++) { + SysPayDepartTreeModel model = treeList.get(i); + PayDepartIdModel idModel = idList.get(i); + for (int i1 = 0; i1 < recordList.size(); i1++) { + SysPayDepartTreeModel m = recordList.get(i1); + if (m.getParentId()!=null && m.getParentId().equals(model.getId())) { + model.getChildren().add(m); + PayDepartIdModel dim = new PayDepartIdModel().convert(m); + idModel.getChildren().add(dim); + } + } + getGrandChildren(treeList.get(i).getChildren(), recordList, idList.get(i).getChildren()); + } + + } + + + /** + * queryTreeList的子方法 ====4==== + * 该方法是将子节点为空的List集合设置为Null值 + */ + private static void setEmptyChildrenAsNull(List treeList) { + + for (int i = 0; i < treeList.size(); i++) { + SysPayDepartTreeModel model = treeList.get(i); + if (model.getChildren().size() == 0) { + model.setChildren(null); + model.setIsLeaf(true); + }else{ + setEmptyChildrenAsNull(model.getChildren()); + model.setIsLeaf(false); + } + } + // sysDepartTreeList = treeList; + } +} diff --git a/jeecg-module-system/jeecg-system-biz/src/main/resources/lib/arcsoft-sdk-face-4.1.1.0.jar b/jeecg-module-system/jeecg-system-biz/src/main/resources/lib/arcsoft-sdk-face-4.1.1.0.jar new file mode 100644 index 0000000..c49fe45 Binary files /dev/null and b/jeecg-module-system/jeecg-system-biz/src/main/resources/lib/arcsoft-sdk-face-4.1.1.0.jar differ diff --git a/jeecg-module-system/jeecg-system-biz/src/main/resources/templates/announcement/showContent.ftl b/jeecg-module-system/jeecg-system-biz/src/main/resources/templates/announcement/showContent.ftl new file mode 100644 index 0000000..cb9408c --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/resources/templates/announcement/showContent.ftl @@ -0,0 +1,135 @@ + + + + + + + 通告详情 + + + +

+

${data.titile}

+
+ <#if data.priority??> + + <#if data.priority == "H"> + 高 + <#elseif data.priority == "M"> + 中 + <#elseif data.priority == "L"> + 低 + <#else > + ${data.priority} + + + + <#if data.sender??> + ${data.sender} + + <#if data.sendTime??> + ${data.sendTime?string('yyyy年MM月dd日')} + +
+
+
+ ${data.msgContent!''} +
+ + \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-biz/src/main/resources/templates/demo3.ftl b/jeecg-module-system/jeecg-system-biz/src/main/resources/templates/demo3.ftl new file mode 100644 index 0000000..d75badc --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/resources/templates/demo3.ftl @@ -0,0 +1,17 @@ + + + +Spring Boot FreeMarker + + + Freemarker HTML

+ + Sessionid: ${sessionid!}

+ + + <#list userList as item> + ${item!}
+ +
+ + \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-biz/src/main/resources/templates/pdfPreviewIframe.ftl b/jeecg-module-system/jeecg-system-biz/src/main/resources/templates/pdfPreviewIframe.ftl new file mode 100644 index 0000000..f20c68c --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/resources/templates/pdfPreviewIframe.ftl @@ -0,0 +1,30 @@ +<#assign base=springMacroRequestContext.getContextUrl("")> + + + + + + + +PDF预览 + + + + + diff --git a/jeecg-module-system/jeecg-system-biz/src/main/resources/templates/thirdLogin.ftl b/jeecg-module-system/jeecg-system-biz/src/main/resources/templates/thirdLogin.ftl new file mode 100644 index 0000000..dd3ab8a --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/resources/templates/thirdLogin.ftl @@ -0,0 +1,28 @@ + + + + + + + 第三方登录 + + +登陆中... + + + \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-biz/src/main/resources/templates/设备信息虹软配置模板.xls b/jeecg-module-system/jeecg-system-biz/src/main/resources/templates/设备信息虹软配置模板.xls new file mode 100644 index 0000000..63f5ac9 Binary files /dev/null and b/jeecg-module-system/jeecg-system-biz/src/main/resources/templates/设备信息虹软配置模板.xls differ diff --git a/jeecg-module-system/jeecg-system-start/Dockerfile b/jeecg-module-system/jeecg-system-start/Dockerfile new file mode 100644 index 0000000..33d0040 --- /dev/null +++ b/jeecg-module-system/jeecg-system-start/Dockerfile @@ -0,0 +1,16 @@ +FROM anapsix/alpine-java:8_server-jre_unlimited + +MAINTAINER jeecgos@163.com + +RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime + +#RUN mkdir -p /jeecg-boot/config/jeecg/ + +WORKDIR /jeecg-boot + +EXPOSE 8080 + +#ADD ./src/main/resources/jeecg ./config/jeecg +ADD ./target/jeecg-system-start-3.4.0.jar ./ + +CMD sleep 60;java -Djava.security.egd=file:/dev/./urandom -jar jeecg-system-start-3.4.0.jar \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-start/README.md b/jeecg-module-system/jeecg-system-start/README.md new file mode 100644 index 0000000..4e6740c --- /dev/null +++ b/jeecg-module-system/jeecg-system-start/README.md @@ -0,0 +1,12 @@ +# 这个是单体启动项目 +- 项目: jeecg-module-system/jeecg-system-start +- 启动类:jeecg-module-system/jeecg-system-start/src/main/java/org/jeecg/JeecgSystemApplication.java + +- 端口:8080 +- 访问地址:http://localhost:8080/jeecg-boot +- 账号密码:admin/123456 + + +# 微服务启动项目在这里 +- 项目: jeecg-server-cloud/jeecg-system-cloud-start +- 启动类:jeecg-server-cloud/jeecg-system-cloud-start/src/main/java/org/jeecg/JeecgSystemCloudApplication.java \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-start/pom.xml b/jeecg-module-system/jeecg-system-start/pom.xml new file mode 100644 index 0000000..d7b08dd --- /dev/null +++ b/jeecg-module-system/jeecg-system-start/pom.xml @@ -0,0 +1,38 @@ + + + + jeecg-module-system + org.jeecgframework.boot + 3.4.0 + + 4.0.0 + + jeecg-system-start + + + + + org.jeecgframework.boot + jeecg-system-biz + ${jeecgboot.version} + + + + org.jeecgframework.boot + jeecg-module-demo + ${jeecgboot.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-start/src/main/java/org/jeecg/JeecgSystemApplication.java b/jeecg-module-system/jeecg-system-start/src/main/java/org/jeecg/JeecgSystemApplication.java new file mode 100644 index 0000000..25d77ea --- /dev/null +++ b/jeecg-module-system/jeecg-system-start/src/main/java/org/jeecg/JeecgSystemApplication.java @@ -0,0 +1,51 @@ +package org.jeecg; + +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.util.oConvertUtils; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.scheduling.annotation.EnableScheduling; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** +* 单体启动类 +* 报错提醒: 未集成mongo报错,可以打开启动类上面的注释 exclude={MongoAutoConfiguration.class} +*/ +@Slf4j +@SpringBootApplication +@EnableAutoConfiguration(exclude={MongoAutoConfiguration.class}) +@EnableScheduling +public class JeecgSystemApplication extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(JeecgSystemApplication.class); + } + + public static void main(String[] args) throws UnknownHostException { + ConfigurableApplicationContext application=new SpringApplicationBuilder(JeecgSystemApplication.class) + // 允许循环引用 + .allowCircularReferences(true) + .run(args); + Environment env = application.getEnvironment(); + String ip = InetAddress.getLocalHost().getHostAddress(); + String port = env.getProperty("server.port"); + String path = oConvertUtils.getString(env.getProperty("server.servlet.context-path")); + log.info("\n----------------------------------------------------------\n\t" + + "Application Jeecg-Boot is running! Access URLs:\n\t" + + "Local: \t\thttp://localhost:" + port + path + "/\n\t" + + "External: \thttp://" + ip + ":" + port + path + "/\n\t" + + "Swagger文档: \thttp://" + ip + ":" + port + path + "/doc.html\n" + + "----------------------------------------------------------"); + + } + +} \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-start/src/main/java/org/jeecg/codegenerate/JeecgOneGUI.java b/jeecg-module-system/jeecg-system-start/src/main/java/org/jeecg/codegenerate/JeecgOneGUI.java new file mode 100644 index 0000000..b4aa18a --- /dev/null +++ b/jeecg-module-system/jeecg-system-start/src/main/java/org/jeecg/codegenerate/JeecgOneGUI.java @@ -0,0 +1,18 @@ +package org.jeecg.codegenerate; + +import org.jeecgframework.codegenerate.window.CodeWindow; + +/** + * @Title: 单表代码生成器入口 + * @Author 张代浩 + * @site www.jeecg.com + * @Version:V1.0.1 + */ +public class JeecgOneGUI { + + /** 使用手册: http://doc.jeecg.com/2684691 */ + public static void main(String[] args) { + new CodeWindow().pack(); + } + +} \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-start/src/main/java/org/jeecg/codegenerate/JeecgOneToMainUtil.java b/jeecg-module-system/jeecg-system-start/src/main/java/org/jeecg/codegenerate/JeecgOneToMainUtil.java new file mode 100644 index 0000000..39b74ee --- /dev/null +++ b/jeecg-module-system/jeecg-system-start/src/main/java/org/jeecg/codegenerate/JeecgOneToMainUtil.java @@ -0,0 +1,81 @@ +package org.jeecg.codegenerate; + +import java.util.ArrayList; +import java.util.List; + +import org.jeecgframework.codegenerate.generate.impl.CodeGenerateOneToMany; +import org.jeecgframework.codegenerate.generate.pojo.onetomany.MainTableVo; +import org.jeecgframework.codegenerate.generate.pojo.onetomany.SubTableVo; + +/** + * 代码生成器入口【一对多】 + * @Author 张代浩 + * @site www.jeecg.com + * + */ +public class JeecgOneToMainUtil { + + /** + * 一对多(父子表)数据模型,生成方法 + * @param args + */ + public static void main(String[] args) { + //第一步:设置主表配置 + MainTableVo mainTable = new MainTableVo(); + //表名 + mainTable.setTableName("jeecg_order_main"); + //实体名 + mainTable.setEntityName("GuiTestOrderMain"); + //包名 + mainTable.setEntityPackage("gui"); + //描述 + mainTable.setFtlDescription("GUI订单管理"); + + //第二步:设置子表集合配置 + List subTables = new ArrayList(); + //[1].子表一 + SubTableVo po = new SubTableVo(); + //表名 + po.setTableName("jeecg_order_customer"); + //实体名 + po.setEntityName("GuiTestOrderCustom"); + //包名 + po.setEntityPackage("gui"); + //描述 + po.setFtlDescription("客户明细"); + //子表外键参数配置 + /*说明: + * a) 子表引用主表主键ID作为外键,外键字段必须以_ID结尾; + * b) 主表和子表的外键字段名字,必须相同(除主键ID外); + * c) 多个外键字段,采用逗号分隔; + */ + po.setForeignKeys(new String[]{"order_id"}); + subTables.add(po); + //[2].子表二 + SubTableVo po2 = new SubTableVo(); + //表名 + po2.setTableName("jeecg_order_ticket"); + //实体名 + po2.setEntityName("GuiTestOrderTicket"); + //包名 + po2.setEntityPackage("gui"); + //描述 + po2.setFtlDescription("产品明细"); + //子表外键参数配置 + /*说明: + * a) 子表引用主表主键ID作为外键,外键字段必须以_ID结尾; + * b) 主表和子表的外键字段名字,必须相同(除主键ID外); + * c) 多个外键字段,采用逗号分隔; + */ + po2.setForeignKeys(new String[]{"order_id"}); + subTables.add(po2); + mainTable.setSubTables(subTables); + + //第三步:一对多(父子表)数据模型,代码生成 + try { + new CodeGenerateOneToMany(mainTable,subTables).generateCodeFile(null); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml b/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml new file mode 100644 index 0000000..238d1ff --- /dev/null +++ b/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml @@ -0,0 +1,303 @@ +server: + port: 2223 + tomcat: + max-swallow-size: -1 + error: + include-exception: true + include-stacktrace: ALWAYS + include-message: ALWAYS +# servlet: +# context-path: /jeecg-boot + compression: + enabled: true + min-response-size: 1024 + mime-types: application/javascript,application/json,application/xml,text/html,text/xml,text/plain,text/css,image/* + +management: + endpoints: + web: + exposure: + include: metrics,httptrace + +spring: + servlet: + multipart: + max-file-size: 1500000KB + max-request-size: 1500000KB + #json 时间戳统一转换 + jackson: + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 + jpa: + open-in-view: false + aop: + proxy-target-class: true + downloadPath: + #内网 + # intranet: http://192.168.10.223:8888/ + intranet: http://192.168.1.16:8888/ + #外网 + outernet: https://vip.shuziweidao.com/appPackage/ + #配置freemarker + freemarker: + # 设置模板后缀名 + suffix: .ftl + # 设置文档类型 + content-type: text/html + # 设置页面编码格式 + charset: UTF-8 + # 设置页面缓存 + cache: false + prefer-file-system-access: false + # 设置ftl文件路径 + template-loader-path: + - classpath:/templates + # 设置静态文件路径,js,css等 + mvc: + static-path-pattern: /** + #Spring Boot 2.6+后映射匹配的默认策略已从AntPathMatcher更改为PathPatternParser,需要手动指定为ant-path-matcher + pathmatch: + matching-strategy: ant_path_matcher + resource: + static-locations: classpath:/static/,classpath:/public/ + autoconfigure: + exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure + datasource: + druid: + stat-view-servlet: + enabled: true + loginUsername: admin + loginPassword: 123456 + allow: + web-stat-filter: + enabled: true + dynamic: + druid: # 全局druid参数,绝大部分值和默认保持一致。(现已支持的参数如下,不清楚含义不要乱设置) + # 连接池的配置信息 + # 初始化大小,最小,最大 + initial-size: 5 + min-idle: 5 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + validationQuery: SELECT 1 + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + # 打开PSCache,并且指定每个连接上PSCache的大小 + poolPreparedStatements: true + maxPoolPreparedStatementPerConnectionSize: 20 + # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 + filters: stat,wall,slf4j + # 通过connectProperties属性来打开mergeSql功能;慢SQL记录 + connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 + datasource: + master: + url: jdbc:mysql://127.0.0.1:3306/zhst_finance?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + username: root + password: St2580147%369 + driver-class-name: com.mysql.cj.jdbc.Driver + # 多数据源配置 + #multi-datasource1: + #url: jdbc:mysql://localhost:3306/jeecg-boot2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + #username: root + #password: root + #driver-class-name: com.mysql.cj.jdbc.Driver + #redis 配置 + redis: + database: 2 + host: 127.0.0.1 + port: 6379 + password: 123qwe!@#P@ssw0rd +#mybatis plus 设置 +mybatis-plus: + mapper-locations: classpath*:org/jeecg/**/**/**/xml/*Mapper.xml + global-config: + # 关闭MP3.0自带的banner + banner: false + db-config: + #主键类型 0:"数据库ID自增",1:"该类型为未设置主键类型", 2:"用户输入ID",3:"全局唯一ID (数字类型唯一ID)", 4:"全局唯一ID UUID",5:"字符串全局唯一ID (idWorker 的字符串表示)"; + id-type: ASSIGN_ID + # 默认数据库表下划线命名 + table-underline: true + configuration: + # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用 + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + # 返回类型为Map,显示null对应的字段 + call-setters-on-nulls: true +#消息队列配置 +activemq: + device: + brokerUrl: failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61616)?randomize=false&priorityBackup=true&maxReconnectDelay=1000&startupMaxReconnectAttempts=3 + username: mtqquser + password: Mqtt@150326 + +#jeecg专用配置 +minidao: + base-package: org.jeecg.modules.jmreport.* +myuploadconfig: + # 文件服务器配置-此项目暂时没有用到 + url: http://192.168.1.205:9091 + token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJhZG1pbiJ9.tFLwb_2diqvbmQZSLuidMJIXq9CE8p3qzAJqMGvn-lk +jeecg: + # 是否启用安全模式 + safeMode: false + # 签名密钥串(前后端要一致,正式发布请自行修改) + signatureSecret: dd05f1c54d63749eda95f9fa6d49v442a + # 签名拦截接口 + signUrls: /sys/dict/getDictItems/*,/sys/dict/loadDict/*,/sys/dict/loadDictOrderByValue/*,/sys/dict/loadDictItem/*,/sys/dict/loadTreeData,/sys/api/queryTableDictItemsByCode,/sys/api/queryFilterTableDictInfo,/sys/api/queryTableDictByKeys,/sys/api/translateDictFromTable,/sys/api/translateDictFromTableByKeys + #local、minio、alioss、myupload + uploadType: local + # 前端访问地址 + domainUrl: + pc: http://localhost:3100 + app: http://localhost:8051 + #服务部署的外网ip端口、域名地址:用于下载文件 + serverPublicIp: +# ip: http://8.140.145.173:8889 + ip: http://192.168.1.16:2333 + + path: + #文件上传根目录 设置 + upload: /file/apklog + #webapp文件路径 + webapp: /opt/webapp + shiro: + excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/** + #阿里云oss存储和大鱼短信秘钥配置 + oss: + accessKey: ?? + secretKey: ?? + endpoint: oss-cn-beijing.aliyuncs.com + bucketName: jeecgdev + # ElasticSearch 6设置 + elasticsearch: + cluster-name: jeecg-ES + cluster-nodes: 127.0.0.1:9200 + check-enabled: false + # 在线预览文件服务器地址配置 + file-view-domain: 127.0.0.1:8012 + # minio文件上传 + minio: + minio_url: http://minio.jeecg.com + minio_name: ?? + minio_pass: ?? + bucketName: otatest + #大屏报表参数设置 + jmreport: + mode: dev + #数据字典是否进行saas数据隔离,自己看自己的字典 + saas: false + #是否需要校验token + is_verify_token: true + #必须校验方法 + verify_methods: remove,delete,save,add,update + #xxl-job配置 + xxljob: + enabled: false + adminAddresses: http://127.0.0.1:9080/xxl-job-admin + appname: ${spring.application.name} + accessToken: '' + address: 127.0.0.1:30007 + ip: 127.0.0.1 + port: 30007 + logPath: logs/jeecg/job/jobhandler/ + logRetentionDays: 30 + #分布式锁配置 + redisson: + address: 192.168.1.250:6379 + password: + type: STANDALONE + enabled: true + + # myupload文件上传 + myupload: + mu_url: ${myuploadconfig.url} + mu_token: ${myuploadconfig.token} + mu_file_exist_path: fileManager/exists + mu_file_length_path: fileManager/getFileLength + mu_file_upload_path: fileManager/uploadFile + mu_file_list_upload_path: fileManager/uploadFileList + mu_file_down_path: fileManager/downFile +#cas单点登录 +cas: + prefixUrl: http://cas.example.org:8443/cas +#Mybatis输出sql日志 +logging: + level: + org.jeecg.system.mapper: info +#swagger +knife4j: + #开启增强配置 + enable: true + #开启生产环境屏蔽 + production: false + basic: + enable: false + username: jeecg + password: jeecg1314 +#第三方登录 +justauth: + enabled: true + type: + GITHUB: + client-id: ?? + client-secret: ?? + redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/github/callback + WECHAT_ENTERPRISE: + client-id: ?? + client-secret: ?? + redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/wechat_enterprise/callback + agent-id: ?? + DINGTALK: + client-id: ?? + client-secret: ?? + redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/dingtalk/callback + WECHAT_OPEN: + client-id: ?? + client-secret: ?? + redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/wechat_open/callback + cache: + type: default + prefix: 'demo::' + timeout: 1h +#第三方APP对接 +third-app: + enabled: false + type: + #企业微信 + WECHAT_ENTERPRISE: + enabled: false + #CORP_ID + client-id: ?? + #SECRET + client-secret: ?? + #自建应用id + agent-id: ?? + #自建应用秘钥(新版企微需要配置) + # agent-app-secret: ?? + #钉钉 + DINGTALK: + enabled: false + # appKey + client-id: ?? + # appSecret + client-secret: ?? + agent-id: ?? +# 虹软人脸识别(WIN和UNIX的sdkKey不一致,需要切换) +face: + # windows + appId: 3RX8bPDaEx3ZLYrxKEZ9F8hT5cqTma49AoeQhTMW8Wvg + sdkKey: CsNxFiPekJAyuQRjwY3c918ARVEEyCyzuvmMwWDUAjsF + activeKey: 86L1-11ZG-411K-8WAF + libPath: jeecg-module-system\jeecg-system-api\jeecg-system-local-api\facelib +detect: + pool: + size: 5 +compare: + pool: + size: 5 \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-start/src/main/resources/application-dm8.yml b/jeecg-module-system/jeecg-system-start/src/main/resources/application-dm8.yml new file mode 100644 index 0000000..b12d010 --- /dev/null +++ b/jeecg-module-system/jeecg-system-start/src/main/resources/application-dm8.yml @@ -0,0 +1,271 @@ +server: + port: 8080 + tomcat: + max-swallow-size: -1 + error: + include-exception: true + include-stacktrace: ALWAYS + include-message: ALWAYS + servlet: + context-path: /jeecg-boot + compression: + enabled: true + min-response-size: 1024 + mime-types: application/javascript,application/json,application/xml,text/html,text/xml,text/plain,text/css,image/* + +management: + endpoints: + web: + exposure: + include: metrics,httptrace + +spring: + servlet: + multipart: + max-file-size: 10MB + max-request-size: 10MB + mail: + host: smtp.163.com + username: jeecgos@163.com + password: ?? + properties: + mail: + smtp: + auth: true + starttls: + enable: true + required: true + ## quartz定时任务,采用数据库方式 + quartz: + job-store-type: jdbc + jdbc: + initialize-schema: never + auto-startup: true + #启动时更新己存在的Job + overwrite-existing-jobs: true + #json 时间戳统一转换 + jackson: + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 + jpa: + open-in-view: false + properties: + hibernate: + dialect: org.hibernate.dialect.Oracle8iDialect + aop: + proxy-target-class: true + #配置freemarker + freemarker: + # 设置模板后缀名 + suffix: .ftl + # 设置文档类型 + content-type: text/html + # 设置页面编码格式 + charset: UTF-8 + # 设置页面缓存 + cache: false + prefer-file-system-access: false + # 设置ftl文件路径 + template-loader-path: + - classpath:/templates + # 设置静态文件路径,js,css等 + mvc: + static-path-pattern: /** + resource: + static-locations: classpath:/static/,classpath:/public/ + datasource: + type: com.alibaba.druid.pool.DruidDataSource + druid: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 5 + # 最大连接池数量 + maxActive: 10 + stat-view-servlet: + enabled: true + loginUsername: admin + loginPassword: 123456 + allow: + web-stat-filter: + enabled: true + driverClassName: dm.jdbc.driver.DmDriver + url: jdbc:dm://127.0.0.1:5236/?JEECG&zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8 + username: ?? + password: ?? + validationQuery: SELECT 1 FROM DUAL + #redis 配置 + redis: + database: 0 + host: 127.0.0.1 + port: 6379 + password: '' +#mybatis plus 设置 +mybatis-plus: + mapper-locations: classpath*:org/jeecg/modules/**/xml/*Mapper.xml + global-config: + # 关闭MP3.0自带的banner + banner: false + db-config: + #主键类型 0:"数据库ID自增",1:"该类型为未设置主键类型", 2:"用户输入ID",3:"全局唯一ID (数字类型唯一ID)", 4:"全局唯一ID UUID",5:"字符串全局唯一ID (idWorker 的字符串表示)"; + id-type: ASSIGN_ID + # 默认数据库表下划线命名 + table-underline: true + configuration: + # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用 + #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + # 返回类型为Map,显示null对应的字段 + call-setters-on-nulls: true +#jeecg专用配置 +jeecg: + #乐福体重秤配置 + lefu: + appKey: lefudc91611833e18d94 + appSecret: eQ50JYimMp0X7uhNLj6D3lTEk4TUUvEG7dvaqKM9t1U= + #4in1推送体重数据 + jkcq: + #获取token + token: /sys/thirdLogin + #体重秤数据对接 + weighingPush: /health-system/remote/data/device/weighing/v2/push + #通过小秤snCode获取用户信息 + getUserInfoBySn: /health-system/api/sys/getUserInfoBySnCode + #通过工号/手机号/idCard获取用户信息 + getUserInfoByTypeCode: /health-system/api/sys/getUserInfoByTypeCode + #inBody体脂仪数据对接 + inBodyWeighingPush: /health-system/remote/data/device/weighing/v3/push + #cquser人脸初始化分页列表根据orgCode查询 + getUserFaceCacheByOrgCode: /stapi/cquser/getUserFaceCache/orgCode + #蚁熊人脸初始化分页列表根据 + getUserFaceCacheByOrgCodeYix: /stapi/cquser/getUserFaceCache/v2 + #system二三级部门列表 + queryTreeListBy: /sys/sysDepart/queryTreeListBy + # 是否启用安全模式 + safeMode: false + # 签名密钥串(前后端要一致,正式发布请自行修改) + signatureSecret: dd05f1c54d63749eda95f9fa6d49v442a + # 本地:local\Minio:minio\阿里云:alioss + uploadType: alioss + path: + #文件上传根目录 设置 + upload: D://opt//upFiles + #webapp文件路径 + webapp: D://opt//webapp + shiro: + excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/** + #阿里云oss存储配置 + oss: + accessKey: ?? + secretKey: ?? + endpoint: oss-cn-beijing.aliyuncs.com + bucketName: ?? + # ElasticSearch 6设置 + elasticsearch: + cluster-name: jeecg-ES + cluster-nodes: 127.0.0.1:9200 + check-enabled: false + # 在线预览文件服务器地址配置 + file-view-domain: 127.0.0.1:8012 +#消息队列配置 +activemq: + device: + brokerUrl: failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61616)?randomize=false&priorityBackup=true&maxReconnectDelay=1000&startupMaxReconnectAttempts=3 + username: sw_mqtt + password: 150326sw@ + zhst: + brokerUrl: failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61616)?randomize=false&priorityBackup=true&maxReconnectDelay=1000&startupMaxReconnectAttempts=3 + username: mtqquser + password: Mqtt@150326 + # minio文件上传 + minio: + minio_url: http://minio.jeecg.com + minio_name: ?? + minio_pass: ?? + bucketName: otatest + #大屏报表参数设置 + jmreport: + mode: dev + #是否需要校验token + is_verify_token: false + #必须校验方法 + verify_methods: remove,delete,save,add,update + #xxl-job配置 + xxljob: + enabled: false + adminAddresses: http://127.0.0.1:9080/xxl-job-admin + appname: ${spring.application.name} + accessToken: '' + address: 127.0.0.1:30007 + ip: 127.0.0.1 + port: 30007 + logPath: logs/jeecg/job/jobhandler/ + logRetentionDays: 30 + #分布式锁配置 + redisson: + address: 127.0.0.1:6379 + password: + type: STANDALONE + enabled: true +#cas单点登录 +cas: + prefixUrl: http://cas.example.org:8443/cas +#Mybatis输出sql日志 +logging: + level: + org.jeecg.modules.system.mapper: info +#enable swagger +swagger: + enable: true + production: false + basic: + enable: false + username: jeecg + password: jeecg1314 +#第三方登录 +justauth: + enabled: true + type: + GITHUB: + client-id: ?? + client-secret: ?? + redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/github/callback + WECHAT_ENTERPRISE: + client-id: ?? + client-secret: ?? + redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/wechat_enterprise/callback + agent-id: 1000002 + DINGTALK: + client-id: ?? + client-secret: ?? + redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/dingtalk/callback + WECHAT_OPEN: + client-id: ?? + client-secret: ?? + redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/wechat_open/callback + cache: + type: default + prefix: 'demo::' + timeout: 1h +#第三方APP对接 +third-app: + enabled: true + type: + #企业微信 + WECHAT_ENTERPRISE: + enabled: true + #CORP_ID + client-id: ?? + #SECRET + client-secret: ??-ij8 + #自建应用id + agent-id: 1000004 + #自建应用秘钥(新版企微需要配置) + agent-app-secret: ?? + #钉钉 + DINGTALK: + enabled: false + # appKey + client-id: ?? + # appSecret + client-secret: ?? + agent-id: ?? diff --git a/jeecg-module-system/jeecg-system-start/src/main/resources/application-prod.yml b/jeecg-module-system/jeecg-system-start/src/main/resources/application-prod.yml new file mode 100644 index 0000000..aedd627 --- /dev/null +++ b/jeecg-module-system/jeecg-system-start/src/main/resources/application-prod.yml @@ -0,0 +1,301 @@ +server: + port: 2223 + tomcat: + max-swallow-size: -1 + error: + include-exception: true + include-stacktrace: ALWAYS + include-message: ALWAYS + # servlet: + # context-path: /jeecg-boot + compression: + enabled: true + min-response-size: 1024 + mime-types: application/javascript,application/json,application/xml,text/html,text/xml,text/plain,text/css,image/* + +management: + endpoints: + web: + exposure: + include: metrics,httptrace + +spring: + servlet: + multipart: + max-file-size: 1500000KB + max-request-size: 1500000KB + #json 时间戳统一转换 + jackson: + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 + jpa: + open-in-view: false + aop: + proxy-target-class: true + downloadPath: + #内网 + # intranet: http://192.168.10.223:8888/ + intranet: http://192.168.1.16:8888/ + #外网 == 暂时没有用 + outernet: https://vip.shuziweidao.com/appPackage/ + #配置freemarker + freemarker: + # 设置模板后缀名 + suffix: .ftl + # 设置文档类型 + content-type: text/html + # 设置页面编码格式 + charset: UTF-8 + # 设置页面缓存 + cache: false + prefer-file-system-access: false + # 设置ftl文件路径 + template-loader-path: + - classpath:/templates + # 设置静态文件路径,js,css等 + mvc: + static-path-pattern: /** + #Spring Boot 2.6+后映射匹配的默认策略已从AntPathMatcher更改为PathPatternParser,需要手动指定为ant-path-matcher + pathmatch: + matching-strategy: ant_path_matcher + resource: + static-locations: classpath:/static/,classpath:/public/ + autoconfigure: + exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure + datasource: + druid: + stat-view-servlet: + enabled: true + loginUsername: admin + loginPassword: 123456 + allow: + web-stat-filter: + enabled: true + dynamic: + druid: # 全局druid参数,绝大部分值和默认保持一致。(现已支持的参数如下,不清楚含义不要乱设置) + # 连接池的配置信息 + # 初始化大小,最小,最大 + initial-size: 5 + min-idle: 5 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + validationQuery: SELECT 1 + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + # 打开PSCache,并且指定每个连接上PSCache的大小 + poolPreparedStatements: true + maxPoolPreparedStatementPerConnectionSize: 20 + # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 + filters: stat,wall,slf4j + # 通过connectProperties属性来打开mergeSql功能;慢SQL记录 + connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 + datasource: + master: + url: jdbc:mysql://rm-uf6afe85n7b0l6034eo.mysql.rds.aliyuncs.com:3306/zhst_finance?Unicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai + username: zhct_root + password: Xulei150326 + driver-class-name: com.mysql.cj.jdbc.Driver + # 多数据源配置 + #multi-datasource1: + #url: jdbc:mysql://localhost:3306/jeecg-boot2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + #username: root + #password: root + #driver-class-name: com.mysql.cj.jdbc.Driver + #redis 配置 + redis: + database: 2 + host: r-bp100u5p8za5xul8y3pd.redis.rds.aliyuncs.com + port: 6379 + password: 'zhct:Xulei150326' +#mybatis plus 设置 +mybatis-plus: + mapper-locations: classpath*:org/jeecg/**/**/**/xml/*Mapper.xml + global-config: + # 关闭MP3.0自带的banner + banner: false + db-config: + #主键类型 0:"数据库ID自增",1:"该类型为未设置主键类型", 2:"用户输入ID",3:"全局唯一ID (数字类型唯一ID)", 4:"全局唯一ID UUID",5:"字符串全局唯一ID (idWorker 的字符串表示)"; + id-type: ASSIGN_ID + # 默认数据库表下划线命名 + table-underline: true + configuration: + # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用 + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + # 返回类型为Map,显示null对应的字段 + call-setters-on-nulls: true +#消息队列配置 +activemq: + device: + brokerUrl: failover:(tcp://device.shuziweidao.com:1883,tcp://127.0.0.1:1883)?randomize=false&priorityBackup=true&maxReconnectDelay=1000&startupMaxReconnectAttempts=3 + username: sw_mqtt + password: 150326sw@ + +#jeecg专用配置 +minidao: + base-package: org.jeecg.modules.jmreport.* +myuploadconfig: + # 文件服务器配置-此项目暂时没有用到 + url: http://192.168.1.205:9091 + token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJhZG1pbiJ9.tFLwb_2diqvbmQZSLuidMJIXq9CE8p3qzAJqMGvn-lk +jeecg: + # 是否启用安全模式 + safeMode: false + # 签名密钥串(前后端要一致,正式发布请自行修改) + signatureSecret: dd05f1c54d63749eda95f9fa6d49v442a + # 签名拦截接口 + signUrls: /sys/dict/loadDict/*,/sys/dict/loadDictOrderByValue/*,/sys/dict/loadDictItem/*,/sys/dict/loadTreeData,/sys/api/queryTableDictItemsByCode,/sys/api/queryFilterTableDictInfo,/sys/api/queryTableDictByKeys,/sys/api/translateDictFromTable,/sys/api/translateDictFromTableByKeys + #local、minio、alioss、myupload + uploadType: local + # 前端访问地址 + domainUrl: + pc: http://localhost:3100 + app: http://localhost:8051 + #服务部署的外网ip端口、域名地址:用于下载文件 + serverPublicIp: + # 文件访问地址 需配置 + ip: http://192.168.1.16:2333 + path: + #文件上传根目录 设置 需配置 + upload: /opt/upFiles + #webapp文件路径 + webapp: /opt/webapp + shiro: + excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/** + #阿里云oss存储和大鱼短信秘钥配置 + oss: + accessKey: ?? + secretKey: ?? + endpoint: oss-cn-beijing.aliyuncs.com + bucketName: jeecgdev + # ElasticSearch 6设置 + elasticsearch: + cluster-name: jeecg-ES + cluster-nodes: 127.0.0.1:9200 + check-enabled: false + # 在线预览文件服务器地址配置 + file-view-domain: 127.0.0.1:8012 + # minio文件上传 + minio: + minio_url: http://minio.jeecg.com + minio_name: ?? + minio_pass: ?? + bucketName: otatest + #大屏报表参数设置 + jmreport: + mode: dev + #数据字典是否进行saas数据隔离,自己看自己的字典 + saas: false + #是否需要校验token + is_verify_token: true + #必须校验方法 + verify_methods: remove,delete,save,add,update + #xxl-job配置 + xxljob: + enabled: false + adminAddresses: http://127.0.0.1:9080/xxl-job-admin + appname: ${spring.application.name} + accessToken: '' + address: 127.0.0.1:30007 + ip: 127.0.0.1 + port: 30007 + logPath: logs/jeecg/job/jobhandler/ + logRetentionDays: 30 + #分布式锁配置 + redisson: + address: 192.168.1.250:6379 + password: + type: STANDALONE + enabled: true + # myupload文件上传 + myupload: + mu_url: ${myuploadconfig.url} + mu_token: ${myuploadconfig.token} + mu_file_exist_path: fileManager/exists + mu_file_length_path: fileManager/getFileLength + mu_file_upload_path: fileManager/uploadFile + mu_file_list_upload_path: fileManager/uploadFileList + mu_file_down_path: fileManager/downFile +#cas单点登录 +cas: + prefixUrl: http://cas.example.org:8443/cas +#Mybatis输出sql日志 +logging: + level: + org.jeecg.modules.system.mapper: info +#swagger +knife4j: + #开启增强配置 + enable: true + #开启生产环境屏蔽 + production: true + basic: + enable: false + username: jeecg + password: jeecg1314 +#第三方登录 +justauth: + enabled: true + type: + GITHUB: + client-id: ?? + client-secret: ?? + redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/github/callback + WECHAT_ENTERPRISE: + client-id: ?? + client-secret: ?? + redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/wechat_enterprise/callback + agent-id: ?? + DINGTALK: + client-id: ?? + client-secret: ?? + redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/dingtalk/callback + WECHAT_OPEN: + client-id: ?? + client-secret: ?? + redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/wechat_open/callback + cache: + type: default + prefix: 'demo::' + timeout: 1h +#第三方APP对接 +third-app: + enabled: false + type: + #企业微信 + WECHAT_ENTERPRISE: + enabled: false + #CORP_ID + client-id: ?? + #SECRET + client-secret: ?? + #自建应用id + agent-id: ?? + #自建应用秘钥(新版企微需要配置) + # agent-app-secret: ?? + #钉钉 + DINGTALK: + enabled: false + # appKey + client-id: ?? + # appSecret + client-secret: ?? + agent-id: ?? +# 虹软人脸识别(WIN和UNIX的sdkKey不一致,需要切换) 激活码需要配置 +face: + # windows + appId: 3RX8bPDaEx3ZLYrxKEZ9F8hT5cqTma49AoeQhTMW8Wvg + sdkKey: CsNxFiPekJAyuQRjwY3c918ARVEEyCyzuvmMwWDUAjsF + activeKey: 86L1-11ZG-411K-8WAF + libPath: jeecg-module-system\jeecg-system-api\jeecg-system-local-api\facelib +detect: + pool: + size: 5 +compare: + pool: + size: 5 \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-start/src/main/resources/application-test.yml b/jeecg-module-system/jeecg-system-start/src/main/resources/application-test.yml new file mode 100644 index 0000000..43be83c --- /dev/null +++ b/jeecg-module-system/jeecg-system-start/src/main/resources/application-test.yml @@ -0,0 +1,334 @@ +server: + port: 8889 + tomcat: + max-swallow-size: -1 + error: + include-exception: true + include-stacktrace: ALWAYS + include-message: ALWAYS + # servlet: + # context-path: /jeecg-boot + compression: + enabled: true + min-response-size: 1024 + mime-types: application/javascript,application/json,application/xml,text/html,text/xml,text/plain,text/css,image/* + +management: + endpoints: + web: + exposure: + include: metrics,httptrace + +spring: + servlet: + multipart: + max-file-size: 1500000KB + max-request-size: 1500000KB + mail: + host: smtp.163.com + username: jeecgos@163.com + password: ?? + properties: + mail: + smtp: + auth: true + starttls: + enable: true + required: true + ## quartz定时任务,采用数据库方式 + quartz: + job-store-type: jdbc + initialize-schema: embedded + #定时任务启动开关,true-开 false-关 + auto-startup: true + #延迟1秒启动定时任务 + startup-delay: 1s + #启动时更新己存在的Job + overwrite-existing-jobs: true + properties: + org: + quartz: + scheduler: + instanceName: MyScheduler + instanceId: AUTO + jobStore: + class: org.springframework.scheduling.quartz.LocalDataSourceJobStore + driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate + tablePrefix: QRTZ_ + isClustered: true + misfireThreshold: 12000 + clusterCheckinInterval: 15000 + threadPool: + class: org.quartz.simpl.SimpleThreadPool + threadCount: 10 + threadPriority: 5 + threadsInheritContextClassLoaderOfInitializingThread: true + #json 时间戳统一转换 + jackson: + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 + jpa: + open-in-view: false + aop: + proxy-target-class: true + #配置freemarker + freemarker: + # 设置模板后缀名 + suffix: .ftl + # 设置文档类型 + content-type: text/html + # 设置页面编码格式 + charset: UTF-8 + # 设置页面缓存 + cache: false + prefer-file-system-access: false + # 设置ftl文件路径 + template-loader-path: + - classpath:/templates + # 设置静态文件路径,js,css等 + mvc: + static-path-pattern: /** + #Spring Boot 2.6+后映射匹配的默认策略已从AntPathMatcher更改为PathPatternParser,需要手动指定为ant-path-matcher + pathmatch: + matching-strategy: ant_path_matcher + resource: + static-locations: classpath:/static/,classpath:/public/ + autoconfigure: + exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure + datasource: + druid: + stat-view-servlet: + enabled: true + loginUsername: admin + loginPassword: 123456 + allow: + web-stat-filter: + enabled: true + dynamic: + druid: # 全局druid参数,绝大部分值和默认保持一致。(现已支持的参数如下,不清楚含义不要乱设置) + # 连接池的配置信息 + # 初始化大小,最小,最大 + initial-size: 5 + min-idle: 5 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + validationQuery: SELECT 1 + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + # 打开PSCache,并且指定每个连接上PSCache的大小 + poolPreparedStatements: true + maxPoolPreparedStatementPerConnectionSize: 20 + # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 + filters: stat,wall,slf4j + # 通过connectProperties属性来打开mergeSql功能;慢SQL记录 + connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 + datasource: + master: + url: jdbc:mysql://127.0.0.1:3306/device-jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + username: root + password: 123456 + driver-class-name: com.mysql.cj.jdbc.Driver + # 多数据源配置 + #multi-datasource1: + #url: jdbc:mysql://localhost:3306/jeecg-boot2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + #username: root + #password: root + #driver-class-name: com.mysql.cj.jdbc.Driver + #redis 配置 + redis: + database: 8 + host: 127.0.0.01 + port: 6379 + password: '123qwe!@#P@ssw0rd' +#mybatis plus 设置 +mybatis-plus: + mapper-locations: classpath*:org/jeecg/modules/**/xml/*Mapper.xml + global-config: + # 关闭MP3.0自带的banner + banner: false + db-config: + #主键类型 0:"数据库ID自增",1:"该类型为未设置主键类型", 2:"用户输入ID",3:"全局唯一ID (数字类型唯一ID)", 4:"全局唯一ID UUID",5:"字符串全局唯一ID (idWorker 的字符串表示)"; + id-type: ASSIGN_ID + # 默认数据库表下划线命名 + table-underline: true + configuration: + # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用 + #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + # 返回类型为Map,显示null对应的字段 + call-setters-on-nulls: true +#消息队列配置 +activemq: + device: + brokerUrl: failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61616)?randomize=false&priorityBackup=true&maxReconnectDelay=1000&startupMaxReconnectAttempts=3 + username: mtqquser + password: Mqtt@150326 + zhst: + brokerUrl: failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61616)?randomize=false&priorityBackup=true&maxReconnectDelay=1000&startupMaxReconnectAttempts=3 + username: mtqquser + password: Mqtt@150326 +#jeecg专用配置 +minidao: + base-package: org.jeecg.modules.jmreport.* +jeecg: + #乐福体重秤配置 + lefu: + appKey: lefudc91611833e18d94 + appSecret: eQ50JYimMp0X7uhNLj6D3lTEk4TUUvEG7dvaqKM9t1U= + #4in1推送体重数据 + jkcq: + #获取token + token: /sys/thirdLogin + #体重秤数据对接 + weighingPush: /health-system/remote/data/device/weighing/v2/push + #通过小秤snCode获取用户信息 + getUserInfoBySn: /health-system/api/sys/getUserInfoBySnCode + #通过工号/手机号/idCard获取用户信息 + getUserInfoByTypeCode: /health-system/api/sys/getUserInfoByTypeCode + #inBody体脂仪数据对接 + inBodyWeighingPush: /health-system/remote/data/device/weighing/v3/push + #inBody体脂仪数据对接=>报告单 + inBodyWeighingPushReport: /health-system/remote/data/device/weighing/v3/push/report + #cquser人脸初始化分页列表根据orgCode查询 + getUserFaceCacheByOrgCode: /stapi/cquser/getUserFaceCache/orgCode + #蚁熊人脸初始化分页列表根据 + getUserFaceCacheByOrgCodeYix: /stapi/cquser/getUserFaceCache/v2 + #system二级部门列表 + queryTreeListBy: /sys/sysDepart/queryTreeListBy + # 是否启用安全模式 + safeMode: false + # 签名密钥串(前后端要一致,正式发布请自行修改) + signatureSecret: dd05f1c54d63749eda95f9fa6d49v442a + # 签名拦截接口 + signUrls: /sys/dict/getDictItems/*,/sys/dict/loadDict/*,/sys/dict/loadDictOrderByValue/*,/sys/dict/loadDictItem/*,/sys/dict/loadTreeData,/sys/api/queryTableDictItemsByCode,/sys/api/queryFilterTableDictInfo,/sys/api/queryTableDictByKeys,/sys/api/translateDictFromTable,/sys/api/translateDictFromTableByKeys + #local、minio、alioss、myupload + uploadType: local + # 前端访问地址 + domainUrl: + pc: http://localhost:3100 + app: http://localhost:8051 + #服务部署的外网ip端口、域名地址:用于下载文件 + serverPublicIp: + ip: http://127.0.0.1:8889 + path: + #文件上传根目录 设置 + upload: /opt/upFiles + #webapp文件路径 + webapp: /opt/webapp + shiro: + excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/** + #阿里云oss存储和大鱼短信秘钥配置 + oss: + accessKey: ?? + secretKey: ?? + endpoint: oss-cn-beijing.aliyuncs.com + bucketName: jeecgdev + # ElasticSearch 6设置 + elasticsearch: + cluster-name: jeecg-ES + cluster-nodes: 127.0.0.1:9200 + check-enabled: false + # 在线预览文件服务器地址配置 + file-view-domain: 127.0.0.1:8012 + # minio文件上传 + minio: + minio_url: http://minio.jeecg.com + minio_name: ?? + minio_pass: ?? + bucketName: otatest + #大屏报表参数设置 + jmreport: + mode: dev + #数据字典是否进行saas数据隔离,自己看自己的字典 + saas: false + #是否需要校验token + is_verify_token: true + #必须校验方法 + verify_methods: remove,delete,save,add,update + #xxl-job配置 + xxljob: + enabled: false + adminAddresses: http://127.0.0.1:9080/xxl-job-admin + appname: ${spring.application.name} + accessToken: '' + address: 127.0.0.1:30007 + ip: 127.0.0.1 + port: 30007 + logPath: logs/jeecg/job/jobhandler/ + logRetentionDays: 30 + #分布式锁配置 + redisson: + address: 127.0.0.1:6379 + password: + type: STANDALONE + enabled: true +#cas单点登录 +cas: + prefixUrl: http://cas.example.org:8443/cas +#Mybatis输出sql日志 +logging: + level: + org.jeecg.modules.system.mapper: info +#swagger +knife4j: + #开启增强配置 + enable: true + #开启生产环境屏蔽 + production: false + basic: + enable: false + username: jeecg + password: jeecg1314 +#第三方登录 +justauth: + enabled: true + type: + GITHUB: + client-id: ?? + client-secret: ?? + redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/github/callback + WECHAT_ENTERPRISE: + client-id: ?? + client-secret: ?? + redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/wechat_enterprise/callback + agent-id: ?? + DINGTALK: + client-id: ?? + client-secret: ?? + redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/dingtalk/callback + WECHAT_OPEN: + client-id: ?? + client-secret: ?? + redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/wechat_open/callback + cache: + type: default + prefix: 'demo::' + timeout: 1h +#第三方APP对接 +third-app: + enabled: false + type: + #企业微信 + WECHAT_ENTERPRISE: + enabled: false + #CORP_ID + client-id: ?? + #SECRET + client-secret: ?? + #自建应用id + agent-id: ?? + #自建应用秘钥(新版企微需要配置) + # agent-app-secret: ?? + #钉钉 + DINGTALK: + enabled: false + # appKey + client-id: ?? + # appSecret + client-secret: ?? + agent-id: ?? diff --git a/jeecg-module-system/jeecg-system-start/src/main/resources/application.yml b/jeecg-module-system/jeecg-system-start/src/main/resources/application.yml new file mode 100644 index 0000000..67562f1 --- /dev/null +++ b/jeecg-module-system/jeecg-system-start/src/main/resources/application.yml @@ -0,0 +1,5 @@ +spring: + application: + name: jeecg-system + profiles: + active: '@profile.name@' \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-start/src/main/resources/banner.txt b/jeecg-module-system/jeecg-system-start/src/main/resources/banner.txt new file mode 100644 index 0000000..b363819 --- /dev/null +++ b/jeecg-module-system/jeecg-system-start/src/main/resources/banner.txt @@ -0,0 +1,14 @@ +${AnsiColor.BRIGHT_BLUE} + (_) | | | | + _ ___ ___ ___ __ _ ______| |__ ___ ___ | |_ + | |/ _ \/ _ \/ __/ _` |______| '_ \ / _ \ / _ \| __| + | | __/ __/ (_| (_| | | |_) | (_) | (_) | |_ + | |\___|\___|\___\__, | |_.__/ \___/ \___/ \__| + _/ | __/ | + |__/ |___/ + + +${AnsiColor.BRIGHT_GREEN} +Jeecg Boot Version: 3.4.0 +Spring Boot Version: ${spring-boot.version}${spring-boot.formatted-version} +${AnsiColor.BLACK} diff --git a/jeecg-module-system/jeecg-system-start/src/main/resources/jeecg/jeecg_config.properties b/jeecg-module-system/jeecg-system-start/src/main/resources/jeecg/jeecg_config.properties new file mode 100644 index 0000000..44618ab --- /dev/null +++ b/jeecg-module-system/jeecg-system-start/src/main/resources/jeecg/jeecg_config.properties @@ -0,0 +1,29 @@ +#code_generate_project_path +project_path=E:\\workspace\\jeecg-boot +#bussi_package[User defined] +bussi_package=org.jeecg.modules.demo + + +#default code path +#source_root_package=src +#webroot_package=WebRoot + +#maven code path +source_root_package=src.main.java +webroot_package=src.main.webapp + +#ftl resource url +templatepath=/jeecg/code-template +system_encoding=utf-8 + +#db Table id [User defined] +db_table_id=id + +#db convert flag[true/false] +db_filed_convert=true + +#page Search Field num [User defined] +page_search_filed_num=1 +#page_filter_fields +page_filter_fields=create_time,create_by,update_time,update_by +exclude_table=act_,ext_act_,design_,onl_,sys_,qrtz_ diff --git a/jeecg-module-system/jeecg-system-start/src/main/resources/jeecg/jeecg_database.properties b/jeecg-module-system/jeecg-system-start/src/main/resources/jeecg/jeecg_database.properties new file mode 100644 index 0000000..45b49f6 --- /dev/null +++ b/jeecg-module-system/jeecg-system-start/src/main/resources/jeecg/jeecg_database.properties @@ -0,0 +1,27 @@ +#mysql +diver_name=com.mysql.jdbc.Driver +url=jdbc:mysql://localhost:3306/jeecg-boot?useUnicode=true&characterEncoding=UTF-8 +username=root +password=root +database_name=jeecg-boot + +#oracle +#diver_name=oracle.jdbc.driver.OracleDriver +#url=jdbc:oracle:thin:@192.168.1.200:1521:ORCL +#username=scott +#password=tiger +#database_name=ORCL + +#postgre +#diver_name=org.postgresql.Driver +#url=jdbc:postgresql://localhost:5432/jeecg +#username=postgres +#password=postgres +#database_name=jeecg + +#SQLServer2005\u4ee5\u4e0a +#diver_name=org.hibernate.dialect.SQLServerDialect +#url=jdbc:sqlserver://192.168.1.200:1433;DatabaseName=jeecg +#username=sa +#password=SA +#database_name=jeecg \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-start/src/main/resources/logback-spring.xml b/jeecg-module-system/jeecg-system-start/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..acad24b --- /dev/null +++ b/jeecg-module-system/jeecg-system-start/src/main/resources/logback-spring.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n + + + + + + + + ${LOG_HOME}/jeecgboot-%d{yyyy-MM-dd}.%i.log + + 30 + 10MB + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n + + + + + + + + ERROR + + + + %p%d%msg%M%F{32}%L + + + ${LOG_HOME}/error-log.html + + + + + + + + ${LOG_HOME}/jeecgboot-%d{yyyy-MM-dd}.%i.html + + 30 + 10MB + + + + %p%d%msg%M%F{32}%L + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jeecg-module-system/pom.xml b/jeecg-module-system/pom.xml new file mode 100644 index 0000000..c8692e8 --- /dev/null +++ b/jeecg-module-system/pom.xml @@ -0,0 +1,22 @@ + + + + jeecg-boot-parent + org.jeecgframework.boot + 3.4.0 + + 4.0.0 + + jeecg-module-system + pom + + + jeecg-system-api + jeecg-system-biz + jeecg-system-start + + + + \ No newline at end of file diff --git a/jrebel-classpath-36080.jar b/jrebel-classpath-36080.jar new file mode 100644 index 0000000..c3bc1ad Binary files /dev/null and b/jrebel-classpath-36080.jar differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3c7a530 --- /dev/null +++ b/pom.xml @@ -0,0 +1,413 @@ + + 4.0.0 + org.jeecgframework.boot + jeecg-boot-parent + 3.4.0 + pom + JEECG BOOT ${project.version} + + + org.springframework.boot + spring-boot-starter-parent + 2.6.6 + + + + + 3.4.0 + 1.8 + UTF-8 + + + 2021.0.3 + 2021.0.1.0 + 2.0.4 + + 2.2.0 + 1.2.83 + 3.0.3 + 2.0.9 + + 42.2.25 + 11.2.0.3 + 4.0 + 8.0.27 + 5.3.8 + 3.16.1 + 1.9.4 + 29.0-jre + + + 3.5.1 + 3.2.0 + 1.1.22 + 1.9.0 + + + 1.5.2 + 2.6 + 2.1.0 + 3.11.2 + + 1.9.1 + 3.11.0 + 3.1.0 + 1.3.9 + 1.4.2 + 8.0.3 + 1.3.4 + 1.6.1 + 7.4.0 + + 2.17.0 + 1.2.9 + + + + jeecg-boot-base-core + jeecg-module-system + + + + + yixiong + dt-releases + http://maven.icdat.cn/repository/releases/ + + true + + + true + + + + aliyun + aliyun Repository + https://maven.aliyun.com/repository/public + + false + + + + jeecg + jeecg Repository + https://maven.jeecg.org/nexus/content/repositories/jeecg + + false + + + + jeecg-snapshots + jeecg-snapshots Repository + https://oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + junit + junit + test + + + + org.projectlombok + lombok + + + + com.alibaba + fastjson + ${fastjson.version} + + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + com.alibaba.cloud + spring-cloud-alibaba-dependencies + ${spring-cloud-alibaba.version} + pom + import + + + + + org.jeecgframework.boot + jeecg-system-biz + ${jeecgboot.version} + + + + + org.jeecgframework.boot + jeecg-boot-common + ${jeecgboot.version} + + + + org.jeecgframework.boot + jeecg-boot-base-core + ${jeecgboot.version} + + + + org.jeecgframework.boot + jeecg-system-local-api + ${jeecgboot.version} + + + + org.jeecgframework.boot + jeecg-system-cloud-api + ${jeecgboot.version} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.hibernate + hibernate-core + 5.6.7.Final + + + commons-collections + commons-collections + + + + + org.jeecgframework.boot + hibernate-re + 3.2.2 + + + + + + dom4j + dom4j + ${dom4j.version} + + + + commons-fileupload + commons-fileupload + 1.4 + + + commons-io + commons-io + + + + + + + com.xkcoding.justauth + justauth-spring-boot-starter + ${justauth-spring-boot-starter.version} + + + hutool-core + cn.hutool + + + fastjson + com.alibaba + + + + + + cn.hutool + hutool-all + 5.8.28 + + + com.squareup.okhttp3 + okhttp + 4.4.1 + + + + io.minio + minio + ${minio.version} + + + okio + com.squareup.okio + + + okhttp + com.squareup.okhttp3 + + + + + + + org.jeecgframework + jeewx-api + 1.4.8 + + + commons-beanutils + commons-beanutils + + + commons-lang + commons-lang + + + + + + org.jeecgframework.jimureport + jimureport-spring-boot-starter + ${jimureport-spring-boot-starter.version} + + + autopoi-web + org.jeecgframework + + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + UTF-8 + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.1.0 + + + woff + woff2 + eot + ttf + svg + + + + + + + src/main/resources + true + + + src/main/java + + **/*.xml + **/*.json + **/*.ftl + + + + + + + + + dev + + + true + + + + dev + + + + + test + + + test + + + + + prod + + + prod + + + + + \ No newline at end of file