From 39f37d43762986bfd33bfe4fdd7fa2873e96dd15 Mon Sep 17 00:00:00 2001 From: lsh <275477265@qq.com> Date: Tue, 8 Aug 2023 21:22:37 +0800 Subject: [PATCH] =?UTF-8?q?pc=20web=E7=AB=AF=E6=8B=A6=E6=88=AA=E5=99=A8?= =?UTF-8?q?=EF=BC=8C=E7=99=BB=E5=BD=95=E4=BB=A3=E7=A0=81=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- psi-common/pom.xml | 407 ++++++++++++++++++ psi-web/App.sh | 78 ++++ psi-web/package.xml | 86 ++++ psi-web/pom.xml | 228 ++++++++++ .../web/controller/DashboardController.java | 337 +++++++++++++++ .../web/controller/ExportLogController.java | 49 +++ .../web/web/controller/UploadController.java | 55 +++ .../controller/account/AccountController.java | 170 ++++++++ .../controller/account/LoginController.java | 231 ++++++++++ .../web/interceptor/OperLogInterceptor.java | 117 +++++ .../interceptor/PermissionInterceptor.java | 77 ++++ .../interceptor/ViewContextInterceptor.java | 43 ++ 13 files changed, 1880 insertions(+), 2 deletions(-) create mode 100644 psi-common/pom.xml create mode 100644 psi-web/App.sh create mode 100644 psi-web/package.xml create mode 100644 psi-web/pom.xml create mode 100644 psi-web/src/main/java/com/bytechainx/psi/web/web/controller/DashboardController.java create mode 100644 psi-web/src/main/java/com/bytechainx/psi/web/web/controller/ExportLogController.java create mode 100644 psi-web/src/main/java/com/bytechainx/psi/web/web/controller/UploadController.java create mode 100644 psi-web/src/main/java/com/bytechainx/psi/web/web/controller/account/AccountController.java create mode 100644 psi-web/src/main/java/com/bytechainx/psi/web/web/controller/account/LoginController.java create mode 100644 psi-web/src/main/java/com/bytechainx/psi/web/web/interceptor/OperLogInterceptor.java create mode 100644 psi-web/src/main/java/com/bytechainx/psi/web/web/interceptor/PermissionInterceptor.java create mode 100644 psi-web/src/main/java/com/bytechainx/psi/web/web/interceptor/ViewContextInterceptor.java diff --git a/README.md b/README.md index 7b1a915..ce838de 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ **

【📣最近通知】还有什么想要产品/技术上友好的建议或意见,请跟我们联系,将纳入开源贡献者并官网致谢,还会收到社区奖励的小礼品哦!~

** -**

管店云 V2.0 正式发布啦,源代码会陆续发布出来,欢迎大家提前体验!点击立即体验~

** +**

管店云 V2.0 正式发布啦,源代码会陆续发布出来,欢迎大家提前体验!点击立即体验~

** **

如果您觉得我们的开源项目很有帮助,请点击 :star: Star 支持 管店云 开源团队:heart:

** @@ -81,7 +81,7 @@ ## 2.1 在线体验 -开源演示地址:[https://guanxdian.cn/demo](https://guanxdian.cn/demo) +开源演示地址:[https://www.guanxdian.com](https://www.guanxdian.com) 默认账号密码:添加微信领取 `guanxdian` diff --git a/psi-common/pom.xml b/psi-common/pom.xml new file mode 100644 index 0000000..0c961d7 --- /dev/null +++ b/psi-common/pom.xml @@ -0,0 +1,407 @@ + + 4.0.0 + + com.bytechainx.psi + psi-opensource + 2.1.0 + + + psi-common + jar + psi-common + + + UTF-8 + UTF-8 + + + + + + commons-io + commons-io + 2.11.0 + + + + + + + + + com.jfinal + jfinal + 5.1.1 + + + + + com.jfinal + jfinal-undertow + 2.8 + + + + + + com.jfinal + jfinal-weixin + 3.4 + + + + + com.jfinal + cos + 2022.2 + + + + + junit + junit + 3.8.1 + test + + + + + org.java-websocket + Java-WebSocket + 1.4.0 + + + + com.alibaba + fastjson + 2.0.6 + + + + + + log4j + log4j + 1.2.17 + + + commons-io + commons-io + + + + commons-lang + commons-lang + 2.6 + + + + commons-codec + commons-codec + 1.15 + + + + + net.sf.ehcache + ehcache-core + 2.6.11 + + + + + com.alibaba + druid + 1.0.29 + + + + + log4j + log4j + 1.2.17 + + + + + mysql + mysql-connector-java + 5.1.44 + + + + redis.clients + jedis + 3.7.0 + + + + de.ruedigermoeller + fst + 2.29 + + + + org.bouncycastle + bcprov-jdk15on + 1.68 + + + + cn.hutool + hutool-all + 4.6.8 + + + com.google.zxing + core + 3.1.0 + + + com.google.zxing + javase + 3.0.0 + + + commons-fileupload + commons-fileupload + 1.3.1 + + + + com.fasterxml.jackson.core + jackson-core + 2.10.0 + + + + org.quartz-scheduler + quartz + 2.2.1 + + + org.quartz-scheduler + quartz-jobs + 2.2.1 + compile + + + com.google.guava + guava + 17.0 + + + org.slf4j + slf4j-log4j12 + 1.7.28 + + + + com.belerweb + pinyin4j + 2.5.1 + + + + dom4j + dom4j + 1.6.1 + + + jaxen + jaxen + 1.1.1 + + + + com.github.heqiao2010 + lunar + 1.2 + + + + org.apache.poi + poi + 5.2.3 + + + org.apache.poi + poi-scratchpad + 5.2.3 + + + org.apache.poi + poi-ooxml + 5.2.3 + + + + + com.huifu.adapay.core + adapay-core-sdk + 1.2.10 + + + + + com.huifu.adapay + adapay-java-sdk + 1.2.10 + + + + com.huifu.adapay + adapay-sdk-merchant + 1.2.7 + system + D:/eclipse/Workspaces/Oomph/psi-common/src/main/resources/lib/adapay-sdk-merchant-1.2.7.jar + + + org.apache.httpcomponents + httpclient + 4.5.2 + + + org.apache.httpcomponents + httpcore + 4.4.5 + + + org.springframework + spring-context + 5.1.20.RELEASE + + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + 1.2.5 + + + + + + + + + target + target/test-classes + target/classes + + + src/main/resources + + + **/*.java + + true + + + + + src/test/resources + + **/*.java + + true + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + 1.8 + 1.8 + UTF-8 + csharp + + + + org.codehaus.plexus + plexus-compiler-csharp + 1.6 + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + once + -XX:PermSize=256M + true + + + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + UTF-8 + + + + org.apache.maven.plugins + maven-clean-plugin + 2.5 + + + + ${basedir}/classes/lib + + **/*.jar + + false + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.8 + + + add-source + generate-sources + + add-source + + + + + + + + + + + compile + + + + + + + ali-maven + https://maven.aliyun.com/repository/public + + true + + + true + always + fail + + + + + + + \ No newline at end of file diff --git a/psi-web/App.sh b/psi-web/App.sh new file mode 100644 index 0000000..063fec3 --- /dev/null +++ b/psi-web/App.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# ---------------------------------------------------------------------- +# +# 使用说明: +# 1: 该脚本使用前需要首先修改 MAIN_CLASS 值,使其指向实际的启动类 +# +# 2:使用命令行 ./jfinal.sh start | stop | restart 可启动/关闭/重启项目 +# +# 3: JAVA_OPTS 可通过 -D 传入 undertow.port 与 undertow.host 这类参数覆盖 +# 配置文件中的相同值此外还有 undertow.resourcePath、undertow.ioThreads、 +# undertow.workerThreads 共五个参数可通过 -D 进行传入,该功能尽可能减少了 +# 修改 undertow 配置文件的必要性 +# +# 4: JAVA_OPTS 可传入标准的 java 命令行参数,例如 -Xms256m -Xmx1024m 这类常用参数 +# +# 5: 函数 start() 给出了 4 种启动项目的命令行,根据注释中的提示自行选择合适的方式 +# +# ---------------------------------------------------------------------- + +# 启动入口类,该脚本文件用于别的项目时要改这里 +MAIN_CLASS=com.bytechainx.psi.core.App + +if [[ "$MAIN_CLASS" == "com.yourpackage.YourMainClass" ]]; then + echo "请先修改 MAIN_CLASS 的值为你自己项目启动Class,然后再执行此脚本。" + exit 0 +fi + +COMMAND="$1" + +if [[ "$COMMAND" != "start" ]] && [[ "$COMMAND" != "stop" ]] && [[ "$COMMAND" != "restart" ]]; then + echo "Usage: $0 start | stop | restart" + exit 0 +fi + + +# Java 命令行参数,根据需要开启下面的配置,改成自己需要的,注意等号前后不能有空格 +# JAVA_OPTS="-Xms256m -Xmx1024m -Dundertow.port=80 -Dundertow.host=0.0.0.0" +# JAVA_OPTS="-Dundertow.port=80 -Dundertow.host=0.0.0.0" + +# 生成 class path 值 +APP_BASE_PATH=$(cd `dirname $0`; pwd) +CP=${APP_BASE_PATH}/config:${APP_BASE_PATH}/lib/* + +function start() +{ + # 运行为后台进程,并在控制台输出信息 + java -Xverify:none ${JAVA_OPTS} -cp ${CP} ${MAIN_CLASS} & + + # 运行为后台进程,并且不在控制台输出信息 + # nohup java -Xverify:none ${JAVA_OPTS} -cp ${CP} ${MAIN_CLASS} >/dev/null 2>&1 & + + # 运行为后台进程,并且将信息输出到 output.log 文件 + # nohup java -Xverify:none ${JAVA_OPTS} -cp ${CP} ${MAIN_CLASS} > output.log & + + # 运行为非后台进程,多用于开发阶段,快捷键 ctrl + c 可停止服务 + # java -Xverify:none ${JAVA_OPTS} -cp ${CP} ${MAIN_CLASS} +} + +function stop() +{ + # 支持集群部署 + kill `pgrep -f ${APP_BASE_PATH}` 2>/dev/null + + # kill 命令不使用 -9 参数时,会回调 onStop() 方法,确定不需要此回调建议使用 -9 参数 + # kill `pgrep -f ${MAIN_CLASS}` 2>/dev/null + + # 以下代码与上述代码等价 + # kill $(pgrep -f ${MAIN_CLASS}) 2>/dev/null +} + +if [[ "$COMMAND" == "start" ]]; then + start +elif [[ "$COMMAND" == "stop" ]]; then + stop +else + stop + start +fi diff --git a/psi-web/package.xml b/psi-web/package.xml new file mode 100644 index 0000000..7404ff6 --- /dev/null +++ b/psi-web/package.xml @@ -0,0 +1,86 @@ + + + + + release + + + + dir + zip + + + + + true + + + + + ${basedir}/src/main/resources + config + + + + + ${basedir}/src/main/webapp + webapp + + + WEB-INF + WEB-INF/web.xml + + + + + + ${basedir} + + + 755 + unix + + *.sh + + + + ${basedir} + + 755 + windows + + *.bat + + + + + + + + + + + + + + lib + + + + + + + diff --git a/psi-web/pom.xml b/psi-web/pom.xml new file mode 100644 index 0000000..6e19ecc --- /dev/null +++ b/psi-web/pom.xml @@ -0,0 +1,228 @@ + + 4.0.0 + + com.bytechainx.psi + psi-opensource + 2.1.0 + + psi-web + jar + psi-web + + + UTF-8 + UTF-8 + + + + + com.bytechainx.psi + psi-fund + 2.1.0 + compile + + + com.bytechainx.psi + psi-sale + 2.1.0 + compile + + + com.bytechainx.psi + psi-purchase + 2.1.0 + compile + + + net.alchim31.maven + yuicompressor-maven-plugin + 1.5.1 + + + + + + + + + + src/main/java + + + **/*.sql + + + + + + src/main/resources + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + 1.8 + 1.8 + UTF-8 + + -parameters + + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + + *.* + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.1.0 + + + make-assembly + package + + single + + + + + ${project.artifactId} + + false + + true + + + package.xml + + + ${project.build.directory}/ + + + + + + + + com.samaxes.maven + minify-maven-plugin + 1.7.6 + + + default-minify + + ${project.build.directory}/classes + UTF-8 + + OFF + + src/main/webapp/resources + + ../static + + **/*.css + + + **/*.min.css + **/barQrCode/** + **/bootstrap/** + **/echarts/** + **/jquery/** + **/jquery-color/** + **/jQuery-contextMenu/** + **/layer/** + **/My97DatePicker/** + **/wangeditor/** + + + + src/main/webapp/resources + + ../static + + **/*.js + + + **/*.min.js + **/jquery-ui.js + **/barQrCode/** + **/bootstrap/** + **/echarts/** + **/jquery/** + **/jquery-color/** + **/jQuery-contextMenu/** + **/layer/** + **/My97DatePicker/** + **/wangeditor/** + + + + + + + + + SIMPLE_OPTIMIZATIONS + ${project.basedir} + + + + + CLOSURE + true + true + + + minify + + + + + + + + + + + + ali-maven + https://maven.aliyun.com/repository/public + + true + + + true + always + fail + + + + + \ No newline at end of file diff --git a/psi-web/src/main/java/com/bytechainx/psi/web/web/controller/DashboardController.java b/psi-web/src/main/java/com/bytechainx/psi/web/web/controller/DashboardController.java new file mode 100644 index 0000000..e2b815e --- /dev/null +++ b/psi-web/src/main/java/com/bytechainx/psi/web/web/controller/DashboardController.java @@ -0,0 +1,337 @@ +/** + * + */ +package com.bytechainx.psi.web.web.controller; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +import org.apache.commons.lang.StringUtils; + +import com.bytechainx.psi.common.CommonConstant; +import com.bytechainx.psi.common.EnumConstant.AuditStatusEnum; +import com.bytechainx.psi.common.EnumConstant.DataStatusEnum; +import com.bytechainx.psi.common.EnumConstant.MsgTypeEnum; +import com.bytechainx.psi.common.EnumConstant.OrderPayStatusEnum; +import com.bytechainx.psi.common.EnumConstant.OrderStatusEnum; +import com.bytechainx.psi.common.EnumConstant.StockWarnTypeEnum; +import com.bytechainx.psi.common.EnumConstant.UserActiveStatusEnum; +import com.bytechainx.psi.common.Permissions; +import com.bytechainx.psi.common.dto.ConditionFilter; +import com.bytechainx.psi.common.dto.ConditionFilter.Operator; +import com.bytechainx.psi.common.dto.UserSession; +import com.bytechainx.psi.common.kit.DateUtil; +import com.bytechainx.psi.common.model.GoodsPrice; +import com.bytechainx.psi.common.model.InventoryStock; +import com.bytechainx.psi.common.model.MsgNotice; +import com.bytechainx.psi.common.model.MsgNoticeSend; +import com.bytechainx.psi.common.model.PurchaseOrder; +import com.bytechainx.psi.common.model.SaleOrder; +import com.bytechainx.psi.common.model.SaleOrderGoods; +import com.bytechainx.psi.common.model.TenantAdmin; +import com.bytechainx.psi.common.model.TraderBalanceAccount; +import com.bytechainx.psi.common.service.msg.MsgNoticeService; +import com.bytechainx.psi.purchase.service.PurchaseBookOrderService; +import com.bytechainx.psi.purchase.service.PurchaseOrderService; +import com.bytechainx.psi.purchase.service.StatPurchaseService; +import com.bytechainx.psi.purchase.service.StockGoodsQualityService; +import com.bytechainx.psi.purchase.service.StockInfoService; +import com.bytechainx.psi.purchase.service.StockWarehouseService; +import com.bytechainx.psi.sale.service.SaleBookOrderService; +import com.bytechainx.psi.sale.service.SaleOrderService; +import com.bytechainx.psi.sale.service.StatHotSaleService; +import com.bytechainx.psi.sale.service.StatSaleService; +import com.bytechainx.psi.web.web.controller.base.BaseController; +import com.bytechainx.psi.web.web.interceptor.PermissionInterceptor; +import com.jfinal.aop.Clear; +import com.jfinal.aop.Inject; +import com.jfinal.core.Path; +import com.jfinal.kit.Kv; +import com.jfinal.kit.Ret; +import com.jfinal.plugin.activerecord.Page; +import com.jfinal.plugin.ehcache.CacheKit; +import com.jfinal.plugin.redis.Cache; +import com.jfinal.plugin.redis.Redis; + +/** + * 首页 + * @author defier + */ +@Path("/dashboard") +public class DashboardController extends BaseController { + + @Inject + private MsgNoticeService msgNoticeService; + @Inject + private StockWarehouseService warehouseService; + @Inject + private StatSaleService statSaleService; + @Inject + private SaleBookOrderService saleBookOrderService; + @Inject + private SaleOrderService saleOrderService; + @Inject + private PurchaseBookOrderService purchaseBookOrderService; + @Inject + private PurchaseOrderService purchaseOrderService; + @Inject + private StockInfoService stockInfoService; + @Inject + private StockGoodsQualityService stockGoodsQualityService; + @Inject + private StatPurchaseService statPurchaseService; + @Inject + private StatHotSaleService statHotSaleService; + + public void index() { + + } + + public void home() { + UserSession session = getUserSession(); + SaleOrder todaySaleStat = CacheKit.get(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.todaySaleStat"); + if(todaySaleStat == null) { + todaySaleStat = statSaleService.sumByCustomer(null, null, DateUtil.getDayStr(new Date())+" 00:00:00", DateUtil.getSecondStr(new Date())); + CacheKit.put(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.todaySaleStat", todaySaleStat); + } + SaleOrder yesterdaySaleStat = CacheKit.get(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.yesterdaySaleStat"); + if(yesterdaySaleStat == null) { + yesterdaySaleStat = statSaleService.sumByCustomer(null, null, DateUtil.getDayStr(DateUtil.daysAddOrSub(new Date(),-1))+" 00:00:00", DateUtil.getDayStr(DateUtil.daysAddOrSub(new Date(),-1))+" 23:59:59"); + CacheKit.put(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.yesterdaySaleStat", yesterdaySaleStat); + } + SaleOrder monthSaleStat = CacheKit.get(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.monthSaleStat"); + if(monthSaleStat == null) { + monthSaleStat = statSaleService.sumByCustomer(null, null, DateUtil.getMonthFirstDay()+" 00:00:00", DateUtil.getMonthLastDay()+" 23:59:59"); + CacheKit.put(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.monthSaleStat", monthSaleStat); + } + SaleOrder preMonthSaleStat = CacheKit.get(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.preMonthSaleStat"); + if(preMonthSaleStat == null) { + preMonthSaleStat = statSaleService.sumByCustomer(null, null, DateUtil.getPreMonthFirstDay()+" 00:00:00", DateUtil.getPreMonthLastDay()+" 23:59:59"); + CacheKit.put(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.preMonthSaleStat", preMonthSaleStat); + } + + setAttr("todaySaleStat", todaySaleStat); + setAttr("yesterdaySaleStat", yesterdaySaleStat); + setAttr("monthSaleStat", monthSaleStat); + setAttr("preMonthSaleStat", preMonthSaleStat); + + // 待收款销售单 + Page saleOrderPage = CacheKit.get(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.saleOrderPage"); + if(saleOrderPage == null) { + Kv condKv = Kv.create(); + conditionFilterStore(condKv, Permissions.sale_sale); // 添加门店过滤条件 + condKv.set("order_status", OrderStatusEnum.normal.getValue()); + condKv.set("audit_status", AuditStatusEnum.pass.getValue()); + ConditionFilter filter = new ConditionFilter(); + filter.setOperator(Operator.neq); + filter.setValue(OrderPayStatusEnum.finish.getValue()); + condKv.set("pay_status", filter); + saleOrderPage = saleOrderService.paginate(null, null, condKv, 1, pageSize); + + CacheKit.put(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.saleOrderPage", saleOrderPage); + } + + // 待付款进货单 + Page purchaseOrderPage = CacheKit.get(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.purchaseOrderPage"); + if(purchaseOrderPage == null) { + Kv condKv = Kv.create(); + conditionFilterStore(condKv, Permissions.sale_sale); // 添加门店过滤条件 + condKv.set("order_status", OrderStatusEnum.normal.getValue()); + condKv.set("audit_status", AuditStatusEnum.pass.getValue()); + ConditionFilter filter = new ConditionFilter(); + filter.setOperator(Operator.neq); + filter.setValue(OrderPayStatusEnum.finish.getValue()); + condKv.set("pay_status", filter); + purchaseOrderPage = purchaseOrderService.paginate(null, null, condKv, 1, pageSize); + + CacheKit.put(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.purchaseOrderPage", purchaseOrderPage); + } + + // 库存预警 + Page stockInfoPage = CacheKit.get(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.stockInfoPage"); + if(stockInfoPage == null) { + Kv condKv = Kv.create(); + condKv.set("data_status", DataStatusEnum.enable.getValue()); + ConditionFilter filter = new ConditionFilter(); + filter.setOperator(Operator.neq); + filter.setValue(StockWarnTypeEnum.ok.getValue()); + condKv.set("warn_type", filter); + stockInfoPage = stockInfoService.paginate(condKv, 1, pageSize); + + CacheKit.put(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.stockInfoPage", stockInfoPage); + } + + if(session.hasOper(Permissions.sensitiveData_account_stat)) { + //账户余额 + TraderBalanceAccount sumTraderBalanceAccount = TraderBalanceAccount.dao.findFirst("select sum(balance) as balance from trader_balance_account where data_status = ?", DataStatusEnum.enable.getValue()); + // 库存总成本 + BigDecimal sumStockAmount = CacheKit.get(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.sumStockAmount"); + if(sumStockAmount == null) { + List sumInventoryStock = InventoryStock.dao.find("select goods_info_id,spec_1_id,spec_option_1_id,spec_2_id,spec_option_2_id,spec_3_id,spec_option_3_id,unit_id,sum(stock) as stock from inventory_stock where data_status = ? group by goods_info_id,spec_1_id,spec_option_1_id,spec_2_id,spec_option_2_id,spec_3_id,spec_option_3_id,unit_id", DataStatusEnum.enable.getValue()); + sumStockAmount = BigDecimal.ZERO; // 库存成本 + for (InventoryStock inventoryStock : sumInventoryStock) { + GoodsPrice price = inventoryStock.getGoodsPrice(); + if(price == null) { + continue; + } + sumStockAmount = sumStockAmount.add(price.getAvgCostPrice().multiply(inventoryStock.getStock())); + } + CacheKit.put(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.sumStockAmount", sumStockAmount); + } + + // 应收欠款 + SaleOrder sumSaleReceipt = CacheKit.get(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.sumSaleReceipt"); + if(sumSaleReceipt == null) { + sumSaleReceipt = statSaleService.sumByCustomer(null, null, null, null); + CacheKit.put(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.sumSaleReceipt", sumSaleReceipt); + } + // 应付欠款 + PurchaseOrder sumPurchasePay = CacheKit.get(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.sumPurchasePay"); + if(sumPurchasePay == null) { + sumPurchasePay = statPurchaseService.sumBySupplier(null, null, null, null); + CacheKit.put(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.sumPurchasePay", sumPurchasePay); + } + + setAttr("sumBalance", sumTraderBalanceAccount.getBalance()); + setAttr("sumBalance", sumTraderBalanceAccount.getBalance()); + setAttr("sumStockAmount", sumStockAmount); + setAttr("sumSaleReceipt", sumSaleReceipt); + setAttr("sumPurchasePay", sumPurchasePay); + } + + setAttr("waitingPaySaleOrderCount", saleOrderPage.getTotalRow()); + setAttr("waitingPayPurchaseOrderCount", purchaseOrderPage.getTotalRow()); + setAttr("stockInfoWarnCount", stockInfoPage.getTotalRow()); + } + + /** + * 统计销售量,按天统计,年度按月统计 + */ + public void loadSaleStatByDay() { + Integer days = getInt("days"); // 30:近30天,90:近一季度, 365:近1年 + if(days == null) { + renderJson(Ret.fail("参数非法")); + } + List saleStatList = CacheKit.get(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.loadSaleStatByDay.saleStatList."+days); + List saleStatDayList = CacheKit.get(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.loadSaleStatByDay.saleStatDayList."+days); + if(saleStatList == null) { + saleStatList = new ArrayList<>(); + saleStatDayList = new ArrayList<>(); + int time = days; + if(days == 365) { + time = 12;// 12个月,按月统计 + } + for (int beforeDay = time-1; beforeDay >= 0; beforeDay--) { + Date dayDate = DateUtil.daysAddOrSubNow(-beforeDay); + String startTime = DateUtil.getDayStr(dayDate)+" 00:00:00"; + String endTime = DateUtil.getDayStr(dayDate)+" 23:59:59"; + if(days == 365) { + dayDate = DateUtil.getYearMonthDate(DateUtil.monthsAddOrSub(new Date(), -beforeDay)); + startTime = DateUtil.getMonthFirstDay(dayDate)+" 00:00:00"; + endTime = DateUtil.getMonthLastDay(dayDate)+" 23:59:59"; + + } + + SaleOrder saleStat = statSaleService.sumByCustomer(null, null, startTime, endTime); + + saleStatList.add(saleStat); + saleStatDayList.add(dayDate); + } + CacheKit.put(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.loadSaleStatByDay.saleStatList."+days, saleStatList); + CacheKit.put(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.loadSaleStatByDay.saleStatDayList."+days, saleStatDayList); + } + setAttr("saleStatList", saleStatList); + setAttr("saleStatDayList", saleStatDayList); + setAttr("days", days); + } + + /** + * 统计销售量,按天统计,年度按月统计 + */ + public void loadGoodsStatByDay() { + Integer days = getInt("days"); // 30:近30天,90:近一季度, 365:近1年 + if(days == null || days > 365) { + renderJson(Ret.fail("参数非法")); + } + Page goodsStatPage = CacheKit.get(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.loadGoodsStatByDay.goodsStatList."+days); + if(goodsStatPage == null) { + Date dayDate = DateUtil.daysAddOrSubNow(-days); + String startTime = DateUtil.getDayStr(dayDate)+" 00:00:00"; + String endTime = DateUtil.getDayStr(new Date())+" 23:59:59"; + + goodsStatPage = statHotSaleService.paginate(null, startTime, endTime, 1, 16); + // 升序排序 + Collections.sort(goodsStatPage.getList(), Comparator.comparing(SaleOrderGoods::getBuyNumber)); + + CacheKit.put(CommonConstant.CACHE_NAME_ONE_MINUTE_STORE, "home.stat.loadGoodsStatByDay.goodsStatList."+days, goodsStatPage); + } + setAttr("goodsStatList", goodsStatPage.getList()); + } + + /** + * 检测请求执行结果 + */ + public void checkRequestResult() { + String requestId = get("request_id"); + if(StringUtils.isEmpty(requestId)) { + renderJson(Ret.fail("非法请求...")); + return; + } + Cache redis = Redis.use(); + String response = redis.lpop(requestId); + if(response == null) { + renderJson(Ret.fail("waiting")); + return; + } + renderJson(response); + } + + /** + * 心跳 + */ + public void heartBeat() { + TenantAdmin tenantAdmin = getCurrentAdmin(); + if(tenantAdmin.getActiveStatus() != UserActiveStatusEnum.enable.getValue()) { + tenantAdmin = TenantAdmin.dao.findById(tenantAdmin.getId()); // 避免缓存没有更新 + if(tenantAdmin.getActiveStatus() != UserActiveStatusEnum.enable.getValue()) { + removeSessionAttr(CommonConstant.SESSION_ID_KEY); + renderJson(Ret.fail("用户状态异常")); + return; + } + } + UserSession session = getUserSession(); + session.setHeartTime(new Date()); + + // 重要的消息需要再右下角弹窗,紧急的直接居中弹窗。 + Page noticePage = msgNoticeService.paginate(getAdminId(), false, 1, 10); + Ret ret = Ret.ok(); + if(noticePage.getTotalRow() > 0) { + for (MsgNoticeSend msg : noticePage.getList()) { + MsgNotice msgNotice = msg.getMsgNotice(); + msg.put("msg_level", msgNotice.getMsgLevel()); + msg.put("msg_type_name", MsgTypeEnum.getEnum(msgNotice.getMsgType()).getName()); + msg.put("title", msgNotice.getTitle()); + } + ret.set("noticePage", noticePage);// FIXME 单独一个待办事项???待审核销售单、进货单,销售订单预计发货到期、这些都要且只要生成一条消息到消息通知表 + } + + tenantAdmin.setOnlineTimes(tenantAdmin.getOnlineTimes()+2); // 心跳2分钟一次 + tenantAdmin.setUpdatedAt(new Date()); + tenantAdmin.update(); + + renderJson(ret); + } + + /** + * 客服 + */ + @Clear(PermissionInterceptor.class) + public void service() { + + } + +} diff --git a/psi-web/src/main/java/com/bytechainx/psi/web/web/controller/ExportLogController.java b/psi-web/src/main/java/com/bytechainx/psi/web/web/controller/ExportLogController.java new file mode 100644 index 0000000..112911d --- /dev/null +++ b/psi-web/src/main/java/com/bytechainx/psi/web/web/controller/ExportLogController.java @@ -0,0 +1,49 @@ +/** + * + */ +package com.bytechainx.psi.web.web.controller; + +import java.util.Date; + +import com.bytechainx.psi.common.EnumConstant.ExportStatusEnum; +import com.bytechainx.psi.common.model.TenantExportLog; +import com.bytechainx.psi.common.service.export.ExportLogService; +import com.bytechainx.psi.web.web.controller.base.BaseController; +import com.jfinal.aop.Inject; +import com.jfinal.core.Path; +import com.jfinal.plugin.activerecord.Page; + +/** + * 导出任务信息 + * @author defier + */ +@Path("/export/log") +public class ExportLogController extends BaseController { + + @Inject + private ExportLogService exportLogService; + + public void index() { + keepPara("targetId"); + } + + public void list() { + int pageNumber = getInt("pageNumber", 1); + pageSize = getPageSize(); + + Page page = exportLogService.paginate(getAdminId(), pageNumber, pageSize); + for (TenantExportLog exportLog : page.getList()) { + if(exportLog.getExportStatus() == ExportStatusEnum.ing.getValue() && exportLog.getCreatedAt().getTime() + (10*60*1000) <= System.currentTimeMillis()) { + exportLog.setExportStatus(ExportStatusEnum.fail.getValue()); + exportLog.setErrorDesc("导出超时"); + exportLog.setUpdatedAt(new Date()); + exportLog.update(); + } + } + + setAttr("page", page); + keepPara("targetId"); + } + + +} diff --git a/psi-web/src/main/java/com/bytechainx/psi/web/web/controller/UploadController.java b/psi-web/src/main/java/com/bytechainx/psi/web/web/controller/UploadController.java new file mode 100644 index 0000000..b88307e --- /dev/null +++ b/psi-web/src/main/java/com/bytechainx/psi/web/web/controller/UploadController.java @@ -0,0 +1,55 @@ +/** + * + */ +package com.bytechainx.psi.web.web.controller; + +import com.bytechainx.psi.common.kit.FileKit; +import com.bytechainx.psi.web.config.AppConfig; +import com.bytechainx.psi.web.web.controller.base.BaseController; +import com.jfinal.core.Path; +import com.jfinal.kit.Kv; +import com.jfinal.kit.Ret; +import com.jfinal.upload.UploadFile; + +/** + * 上传文件 + * @author defier + */ +@Path("/upload") +public class UploadController extends BaseController { + + public void index() { + UploadFile uploadFile = getFile(); + if(uploadFile == null) { + renderJson(Ret.fail("上传文件不存在")); + return; + } + Ret ret = FileKit.uploadImage(uploadFile, AppConfig.resourceUploadPath); + //return Ret.ok().set("filePath", filePath).set("thumbPath", thumbPath); + renderJson(ret); + } + + /** + * wangeditor编辑器文件上传接口 + */ + public void wangeditorApi() { + UploadFile uploadFile = getFile(); + if(uploadFile == null) { + renderJson(Ret.fail("上传文件不存在")); + return; + } + Ret ret = FileKit.uploadImage(uploadFile, AppConfig.resourceUploadPath); + //return Ret.ok().set("filePath", filePath).set("thumbPath", thumbPath); + Kv _ret = Kv.create(); + if(ret.isOk()) { + String imgUrl = AppConfig.resourceUploadDomain + ret.getStr("thumbPath"); + _ret.set("errno", 0); // 成功 + _ret.set("data", Kv.by("url", imgUrl).set("href", imgUrl)); + } else { + _ret.set("errno", 1); // 失败 + _ret.set("message", ret.getStr("MSG")); + } + renderJson(_ret); + } + +} diff --git a/psi-web/src/main/java/com/bytechainx/psi/web/web/controller/account/AccountController.java b/psi-web/src/main/java/com/bytechainx/psi/web/web/controller/account/AccountController.java new file mode 100644 index 0000000..0deb0cd --- /dev/null +++ b/psi-web/src/main/java/com/bytechainx/psi/web/web/controller/account/AccountController.java @@ -0,0 +1,170 @@ +package com.bytechainx.psi.web.web.controller.account; + +import java.util.Date; + +import org.apache.commons.lang.StringUtils; + +import com.bytechainx.psi.common.CommonConstant; +import com.bytechainx.psi.common.EnumConstant.UserActiveStatusEnum; +import com.bytechainx.psi.common.dto.SmsCodeDto; +import com.bytechainx.psi.common.kit.CipherkeyUtil; +import com.bytechainx.psi.common.kit.SmsKit; +import com.bytechainx.psi.common.model.TenantAdmin; +import com.bytechainx.psi.common.service.setting.TenantAdminService; +import com.bytechainx.psi.web.web.controller.base.BaseController; +import com.bytechainx.psi.web.web.interceptor.PermissionInterceptor; +import com.jfinal.aop.Before; +import com.jfinal.aop.Clear; +import com.jfinal.aop.Inject; +import com.jfinal.core.Path; +import com.jfinal.kit.Ret; +import com.jfinal.log.Log; +import com.jfinal.plugin.activerecord.tx.Tx; + +/** + * 帐户登录 + */ + +@Path("/account") +public class AccountController extends BaseController { + + private static final Log LOG = Log.getLog(AccountController.class); + + @Inject + private TenantAdminService tenantAdminService; + + /** + * 查看账户 + */ + public void edit() { + TenantAdmin currentAdmin = getCurrentAdmin(); + currentAdmin.remove("password", "encrypt"); + setAttr("account", currentAdmin); + } + + /** + * 修改 + */ + @Before(Tx.class) + public void update() { + String realName = get("real_name"); + String mobile = get("mobile"); + if(StringUtils.isEmpty(realName)) { + renderJson(Ret.fail("真实姓名不能为空")); + } + if(StringUtils.isEmpty(mobile)) { + renderJson(Ret.fail("手机号不能为空")); + } + TenantAdmin currentAdmin = getCurrentAdmin(); + currentAdmin.setRealName(realName); + currentAdmin.setMobile(mobile); + Ret ret = tenantAdminService.update(currentAdmin); + if(ret.isOk()) { + getUserSession().setRealName(realName); + } + + renderJson(ret); + } + + /** + * 修改密码 + */ + public void editPwd() { + + } + + /** + * 修改密码 + */ + public void updatePwd() { + Ret ret = tenantAdminService.updatePwd(getAdminId(), getPara("old_password"), getPara("new_password")); + renderJson(ret); + } + + /** + * 退出 + */ + @Clear(PermissionInterceptor.class) + public void logout() { + removeSessionAttr(CommonConstant.SESSION_ID_KEY); + String url = getCookie(CommonConstant.COOKIE_TENANT_URL_CODE); + redirect(url); + } + + + /** + * 激活帐户 + */ + public void activeIndex() { + TenantAdmin currentAdmin = getCurrentAdmin(); + currentAdmin.remove("password", "encrypt"); + setAttr("currentAdmin", currentAdmin); + } + + /** + * 激活帐户 + */ + public void active() { + try { + String smsCode = getPara("smsCode"); + if (StringUtils.isEmpty(smsCode)) { + renderJson(Ret.fail("验证码不能为空")); + return; + } + SmsCodeDto smsCodeDto = (SmsCodeDto) getSessionAttr(CommonConstant.SESSION_SMS_CODE); + if (smsCodeDto == null) { + renderJson(Ret.fail("无效的短信验证码")); + return; + } + long currentTime = System.currentTimeMillis(); + long sendTime = smsCodeDto.getSendTime().getTime(); + boolean isOutTime = currentTime - sendTime > 5 * 60 * 1000; // 5分钟超时 + if (isOutTime) { + removeSessionAttr(CommonConstant.SESSION_SMS_CODE); + renderJson(Ret.fail("短信验证码超时")); + return; + } + if (!StringUtils.equals(smsCode, smsCodeDto.getCode())) { + renderJson(Ret.fail("短信验证码输入不正确")); + return; + } + String newPwd = getPara("newPwd"); + if (StringUtils.isEmpty(newPwd)) { + renderJson(Ret.fail("新密码不能为空")); + return; + } + removeSessionAttr(CommonConstant.SESSION_SMS_CODE); + + TenantAdmin admin = TenantAdmin.dao.findById(getAdminId()); + String encodePassword = CipherkeyUtil.encodePassword(newPwd, admin.getEncrypt()); + admin.setActiveStatus(UserActiveStatusEnum.enable.getValue()); + admin.setPassword(encodePassword); + admin.setUpdatedAt(new Date()); + admin.update(); + + renderJson(Ret.ok("激活帐户成功")); + + } catch (Exception e) { + LOG.error("激活帐户异常", e); + renderJson(Ret.fail("激活帐户异常:"+e.getMessage())); + } + } + + + /** + * 发送手机验证码 + */ + public void sendActiveCode() { + TenantAdmin admin = getCurrentAdmin(); + if (admin == null) { + renderJson(Ret.fail("手机号输入错误")); + return; + } + Ret ret = SmsKit.activeCode(admin.getMobile()); + if(ret.isOk()) { + setSessionAttr(CommonConstant.SESSION_SMS_CODE, new SmsCodeDto(ret.getStr("smsCode"), new Date(), admin.getMobile())); + } + renderJson(ret); + } + +} diff --git a/psi-web/src/main/java/com/bytechainx/psi/web/web/controller/account/LoginController.java b/psi-web/src/main/java/com/bytechainx/psi/web/web/controller/account/LoginController.java new file mode 100644 index 0000000..c14b314 --- /dev/null +++ b/psi-web/src/main/java/com/bytechainx/psi/web/web/controller/account/LoginController.java @@ -0,0 +1,231 @@ +package com.bytechainx.psi.web.web.controller.account; + +import java.util.Date; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; + +import com.bytechainx.psi.common.CommonConstant; +import com.bytechainx.psi.common.EnumConstant.FlagEnum; +import com.bytechainx.psi.common.EnumConstant.UserActiveStatusEnum; +import com.bytechainx.psi.common.dto.SmsCodeDto; +import com.bytechainx.psi.common.dto.UserSession; +import com.bytechainx.psi.common.kit.CipherkeyUtil; +import com.bytechainx.psi.common.kit.SmsKit; +import com.bytechainx.psi.common.model.TenantAdmin; +import com.bytechainx.psi.common.model.TenantRole; +import com.bytechainx.psi.common.service.setting.TenantRoleService; +import com.bytechainx.psi.web.config.AppConfig; +import com.bytechainx.psi.web.web.controller.base.BaseController; +import com.bytechainx.psi.web.web.interceptor.PermissionInterceptor; +import com.jfinal.aop.Clear; +import com.jfinal.aop.Inject; +import com.jfinal.core.Path; +import com.jfinal.kit.Ret; +import com.jfinal.log.Log; + +import cn.hutool.extra.servlet.ServletUtil; +import cn.hutool.http.HttpStatus; + +/** + * 登录 + * @author defier + * + */ +@Clear({PermissionInterceptor.class}) +@Path("/") +public class LoginController extends BaseController { + + private static final Log LOG = Log.getLog(LoginController.class); + + @Inject + private TenantRoleService roleService; + + public void index() { + try { + Integer adminId = getAdminId(); + if(adminId != null) { // 登录状态,但不是同一个租户,则退出 + render("dashboard/index.html"); + return; + } + removeSessionAttr(CommonConstant.SESSION_ID_KEY); + + setAttr("tenantOrg", getCurrentTenant()); + setAttr("version", AppConfig.version); + + } catch (Exception e) { + LOG.error("加载租户登录界面异常", e); + renderError(HttpStatus.HTTP_INTERNAL_ERROR); + } + } + + /** + * 登录 + */ + public void login() { + try { + String mobile = getPara("mobile"); + String password = getPara("password");// 密码 + String mac = getPara("mac"); + + if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) { + renderJson(Ret.fail("手机号或者密码不能为空")); + return; + } + + TenantAdmin admin = TenantAdmin.dao.findBy(mobile); + if (admin == null) { + renderJson(Ret.fail("手机号或者密码不正确")); + return; + } + if(admin.getLoginFlag() == FlagEnum.NO.getValue()) { + renderJson(Ret.fail("用户不可登录")); + return; + } + if(admin.getActiveStatus() == UserActiveStatusEnum.disable.getValue()) { + renderJson(Ret.fail("用户已被禁用,无法登录")); + return; + } + if(StringUtils.isNotEmpty(admin.getMac()) && !StringUtils.equalsIgnoreCase(admin.getMac(), mac)) { + renderJson(Ret.fail("当前电脑禁止登录系统")); + return; + } + + // 验证密码 + String encodePassword = CipherkeyUtil.encodePassword(password, admin.getEncrypt()); + if (!StringUtils.equals(admin.getPassword(), encodePassword)) { + renderJson(Ret.fail("手机号或者密码不正确")); + return; + } + // 登录数据换成分两个数据,一个是以用户ID为key,value为sessionid的,一个是以sessionid为key,value为usersession的两个数据 + removeOnlineSession(CommonConstant.ONLINE_USER_ID_PC_CACHE, admin.getId()); + + String ip = ServletUtil.getClientIP(getRequest()); + if(admin.getLoginCount() <= 0) { + admin.setFirstLoginIp(ip); + admin.setFirstLoginTime(new Date()); + } + admin.setUpdatedAt(new Date()); + admin.setLastLoginIp(ip); + admin.setLastLoginTime(new Date()); + admin.setLoginCount(admin.getLoginCount()+1); + admin.update(); + + TenantRole role = admin.getRole(); + UserSession session = new UserSession(); + session.setTenantAdminId(admin.getId()); + session.setSuperFlag(role.getSuperFlag()); + session.setHeartTime(new Date()); + session.setLoginIp(ip); + session.setRealName(admin.getRealName()); + + if(!role.getSuperFlag()) { // 非超级管理员,加载权限 + Set operCodes = roleService.findOperByRoleId(admin.getRoleId()); + session.setOperCodeSet(operCodes); + } + // 用户放入redis + addOnlineSession(CommonConstant.ONLINE_USER_ID_PC_CACHE, admin.getId(), getSession().getId()); + + setSessionAttr(CommonConstant.SESSION_ID_KEY, session); + + LOG.info("用户登录成功, adminId:%s", admin.getId()); + + if(admin.getActiveStatus() == UserActiveStatusEnum.waiting.getValue()) { // 待激活 + renderJson(Ret.ok("首次登录,进入帐户激活...").set("status", UserActiveStatusEnum.waiting.getValue())); + } else { + renderJson(Ret.ok("登录成功,欢迎使用『管店云』进销存系统...")); + } + + } catch (Exception e) { + LOG.error("用户登录异常", e); + renderJson(Ret.fail("用户登录异常:"+e.getMessage())); + } + + } + + /** + * 忘记密码 + */ + public void forgetPwd() { + + } + + /** + * 发送手机验证码 + */ + public void sendSmsCode() { + String mobile = getPara("mobile"); + if(StringUtils.isEmpty(mobile)) { + renderJson(Ret.fail("手机号不能为空")); + return; + } + TenantAdmin admin = TenantAdmin.dao.findBy(mobile); + if (admin == null) { + renderJson(Ret.fail("手机号输入错误")); + return; + } + Ret ret = SmsKit.forgetPwd(mobile); + if(ret.isOk()) { + setSessionAttr(CommonConstant.SESSION_SMS_CODE, new SmsCodeDto(ret.getStr("smsCode"), new Date(), mobile)); + } + renderJson(ret); + } + + /** + * 更新密码 + */ + public void updatePwd() { + try { + String mobile = getPara("mobile"); + if (StringUtils.isEmpty(mobile)) { + renderJson(Ret.fail("手机号不能为空")); + return; + } + String smsCode = getPara("smsCode"); + if (StringUtils.isEmpty(smsCode)) { + renderJson(Ret.fail("验证码不能为空")); + return; + } + SmsCodeDto smsCodeDto = (SmsCodeDto) getSessionAttr(CommonConstant.SESSION_SMS_CODE); + if (smsCodeDto == null) { + renderJson(Ret.fail("无效的短信验证码")); + return; + } + long currentTime = System.currentTimeMillis(); + long sendTime = smsCodeDto.getSendTime().getTime(); + boolean isOutTime = currentTime - sendTime > 5 * 60 * 1000; // 5分钟超时 + if (isOutTime) { + removeSessionAttr(CommonConstant.SESSION_SMS_CODE); + renderJson(Ret.fail("短信验证码超时")); + return; + } + if (!StringUtils.equals(smsCode, smsCodeDto.getCode())) { + renderJson(Ret.fail("短信验证码输入不正确")); + return; + } + String newPwd = getPara("newPwd"); + if (StringUtils.isEmpty(newPwd)) { + renderJson(Ret.fail("新密码不能为空")); + return; + } + TenantAdmin admin = TenantAdmin.dao.findBy(mobile); + if (admin == null) { + renderJson(Ret.fail("手机号输入错误")); + return; + } + removeSessionAttr(CommonConstant.SESSION_SMS_CODE); + + String encodePassword = CipherkeyUtil.encodePassword(newPwd, admin.getEncrypt()); + admin.setPassword(encodePassword); + admin.setUpdatedAt(new Date()); + admin.update(); + + renderJson(Ret.ok("修改密码成功")); + } catch (Exception e) { + LOG.error("修改用户密码异常", e); + renderJson(Ret.fail("修改用户密码异常:"+e.getMessage())); + } + + } + +} diff --git a/psi-web/src/main/java/com/bytechainx/psi/web/web/interceptor/OperLogInterceptor.java b/psi-web/src/main/java/com/bytechainx/psi/web/web/interceptor/OperLogInterceptor.java new file mode 100644 index 0000000..cf664cb --- /dev/null +++ b/psi-web/src/main/java/com/bytechainx/psi/web/web/interceptor/OperLogInterceptor.java @@ -0,0 +1,117 @@ +package com.bytechainx.psi.web.web.interceptor; + +import java.lang.reflect.Method; +import java.util.Date; + +import org.apache.commons.lang.StringUtils; + +import com.bytechainx.psi.common.EnumConstant.OperLogTypeEnum; +import com.bytechainx.psi.common.EnumConstant.PlatformTypeEnum; +import com.bytechainx.psi.common.Permissions; +import com.bytechainx.psi.common.annotation.Permission; +import com.bytechainx.psi.common.model.TenantAdmin; +import com.bytechainx.psi.common.model.TenantOperLog; +import com.bytechainx.psi.web.web.controller.base.BaseController; +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; + +import cn.hutool.extra.servlet.ServletUtil; + +/** + * 操作日志拦截器,统一在这里记录日志 + * + * @author defier + * + */ +public class OperLogInterceptor implements Interceptor { + + @Override + public void intercept(Invocation ai) { + ai.invoke(); + // ACTION执行后记录日志,多线程记录,提升访问性能 + new Thread(() -> writeOperLog(ai)).start(); + } + + /** + * 记录操作日志 + * @param ai + */ + private void writeOperLog(Invocation ai) { + BaseController controller = (BaseController) ai.getController(); + String methodName = ai.getMethodName(); + TenantAdmin admin = null; + Integer logType = null; + if (StringUtils.contains(methodName, "delete")) { + logType = OperLogTypeEnum.delete.getValue(); + + } else if (StringUtils.contains(methodName, "create")) { + logType = OperLogTypeEnum.create.getValue(); + + } else if (StringUtils.contains(methodName, "update")) { + logType = OperLogTypeEnum.update.getValue(); + + } else if (StringUtils.contains(methodName, "disable")) { + logType = OperLogTypeEnum.setting.getValue(); + + } else if (StringUtils.contains(methodName, "enable")) { + logType = OperLogTypeEnum.setting.getValue(); + + } else if (StringUtils.contains(methodName, "close")) { + logType = OperLogTypeEnum.setting.getValue(); + + } else if (StringUtils.contains(methodName, "audit")) { + logType = OperLogTypeEnum.setting.getValue(); + + } else if (StringUtils.contains(methodName, "login")) { + logType = OperLogTypeEnum.login.getValue(); + admin = TenantAdmin.dao.findBy(controller.getPara("mobile")); + + } else if (StringUtils.contains(methodName, "logout")) { + logType = OperLogTypeEnum.login.getValue(); + } + // 没有日志类型,则不记录 + if(logType == null) { + return; + } + if (admin == null) { + admin = controller.getCurrentAdmin(); + } + if (admin == null) { + return; + } + Permissions permissionCode = null; + Method method = ai.getMethod(); + Permission permission = method.getAnnotation(Permission.class); + if (permission != null && permission.value() != null) { + permissionCode = permission.value()[0]; + } + StringBuffer operDesc = new StringBuffer(); // 操作描述 + if(permissionCode != null) { + Permissions parentPermission = Permissions.getEnum(permissionCode.name().substring(0, permissionCode.name().lastIndexOf("_"))); + if(StringUtils.contains(methodName, "enable")) { // 在权限里面的名称不管是启用还是停用,都是停用,使用的同一个权限,这里特殊判断下。 + operDesc.append(parentPermission.getName()+":启用"); + } else { + operDesc.append(parentPermission.getName()+":"+permissionCode.getName()); + } + String dataId = controller.getPara("id") == null ? controller.getPara("ids") : controller.getPara("id");; // 修改删除等数据的ID + if(StringUtils.isNotEmpty(dataId)) { + operDesc.append(", id:"+dataId); + } + } + if(StringUtils.contains(methodName, "login")) { + operDesc.append("登录"); + } else if(StringUtils.contains(methodName, "logout")) { + operDesc.append("退出"); + } + TenantOperLog log = new TenantOperLog(); + log.setLogType(logType); + log.setOperPerson(admin.getRealName()); + log.setTenantAdminId(admin.getId()); + log.setOperDesc(operDesc.toString()); + log.setOperTime(new Date()); + log.setPlatformType(PlatformTypeEnum.web.getValue()); + log.setOperIp(ServletUtil.getClientIP(controller.getRequest())); + log.save(); + } + +} diff --git a/psi-web/src/main/java/com/bytechainx/psi/web/web/interceptor/PermissionInterceptor.java b/psi-web/src/main/java/com/bytechainx/psi/web/web/interceptor/PermissionInterceptor.java new file mode 100644 index 0000000..bae7e0d --- /dev/null +++ b/psi-web/src/main/java/com/bytechainx/psi/web/web/interceptor/PermissionInterceptor.java @@ -0,0 +1,77 @@ +package com.bytechainx.psi.web.web.interceptor; + +import java.lang.reflect.Method; + +import com.bytechainx.psi.common.CommonConstant; +import com.bytechainx.psi.common.Permissions; +import com.bytechainx.psi.common.annotation.Permission; +import com.bytechainx.psi.common.dto.UserSession; +import com.bytechainx.psi.web.config.AppConfig; +import com.bytechainx.psi.web.web.controller.base.BaseController; +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import com.jfinal.kit.Ret; + +/** + * 需要登录权限才能访问 + * + * @author defier + * + */ +public class PermissionInterceptor implements Interceptor { + + @Override + public void intercept(Invocation inv) { + BaseController controller = (BaseController) inv.getController(); + // 判断是否登录 + UserSession session = controller.getSessionAttr(CommonConstant.SESSION_ID_KEY); + if (session == null) { + redirectLogin(controller); + return; + } + // 判断租户是否到期 + Integer tenantId = controller.getSessionAttr(CommonConstant.SESSION_TENANT_ID); + if(tenantId == null) { + redirectLogin(controller); + return; + } + // 判断是否有权限 + Permissions[] operCodes = {}; + Method method = inv.getMethod(); + Permission permission = method.getAnnotation(Permission.class); + if (permission != null && permission.value() != null) { + operCodes = permission.value(); + } + if(!session.hasAnyOper(operCodes)) { // 无权限 + forward(controller); + return; + } + + inv.invoke(); + } + + /** + * @param controller + */ + public void redirectLogin(BaseController controller) { + String url = controller.getCookie(CommonConstant.COOKIE_TENANT_URL_CODE); + if(controller.isJsonRequest()) { + controller.renderJson(Ret.fail("登录超时,请重新登录").set("loginUrl", url)); + } else if (controller.isAjaxHtmlRequest()) { + controller.render(AppConfig.noLoginViews); + } else { + controller.redirect(url); + } + } + + private void forward(BaseController controller) { + if (controller.isJsonRequest()) { + controller.renderJson(Ret.fail("没有操作权限")); + } else if (controller.isAjaxHtmlRequest()) { + controller.render(AppConfig.noPermissionViews); + } else { // 正常的http请求 + controller.renderHtml(""); + } + } + +} diff --git a/psi-web/src/main/java/com/bytechainx/psi/web/web/interceptor/ViewContextInterceptor.java b/psi-web/src/main/java/com/bytechainx/psi/web/web/interceptor/ViewContextInterceptor.java new file mode 100644 index 0000000..a34c891 --- /dev/null +++ b/psi-web/src/main/java/com/bytechainx/psi/web/web/interceptor/ViewContextInterceptor.java @@ -0,0 +1,43 @@ +package com.bytechainx.psi.web.web.interceptor; + +import com.bytechainx.psi.common.CommonConstant; +import com.bytechainx.psi.common.model.TenantOrg; +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import com.jfinal.core.Controller; + +/** + * 一些公共数据,在此类中处理 + * @author defier + * + */ +public class ViewContextInterceptor implements Interceptor { + +// private final static String TOKEN_NAME = "_mvc_token"; + + @Override + public void intercept(Invocation ai) { + Controller controller = ai.getController(); + controller.setAttr("session", controller.getSessionAttr(CommonConstant.SESSION_ID_KEY)); + Integer tenantId = controller.getSessionAttr(CommonConstant.SESSION_TENANT_ID); + if(tenantId != null && tenantId > 0) { + TenantOrg currentTenant = TenantOrg.dao.findCacheById(); + controller.setAttr("currentTenant", currentTenant); + } + /** + String methodName = ai.getMethodName(); + if (StringUtils.contains(methodName, "add") || StringUtils.contains(methodName, "edit")) { + controller.createToken(TOKEN_NAME); + } + if (StringUtils.contains(methodName, "create") || StringUtils.contains(methodName, "update")) { + if(!controller.validateToken(TOKEN_NAME)) { + controller.renderJson(Ret.fail("操作过于频繁")); + return; + } + } + **/ + + ai.invoke(); + } + +} -- GitLab