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