diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..e93132812d439fbe4c588451e8c6ed2f45d69591 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,69 @@ +name: Actions + +on: [push, pull_request] + +jobs: + # Building and testing Java with Maven + # https://docs.github.com/en/actions/guides/building-and-testing-java-with-maven + Litemall-all: + runs-on: ubuntu-latest + strategy: + matrix: + java-version: + - 8 + - 8.0.192 + - 11 + - 11.0.3 + steps: + - uses: actions/checkout@v2 + - name: Set up JDK ${{ matrix.java-version }} + uses: actions/setup-java@v2 + with: + java-version: ${{ matrix.java-version }} + distribution: zulu + - name: Cache Maven packages + uses: actions/cache@v2 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + - name: Build with Maven + run: mvn --batch-mode --update-snapshots verify + + # Building and testing Node.js + # https://docs.github.com/en/actions/guides/building-and-testing-nodejs + Litemall-admin: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: + - 10.x + - 12.x + - 14.x + # - 15.x + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm --prefix litemall-admin install + - run: npm --prefix litemall-admin run test + + Litemall-vue: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: + - 10.x + - 12.x + - 14.x + # - 15.x + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm --prefix litemall-vue install + - run: npm --prefix litemall-vue run test diff --git a/.gitignore b/.gitignore index 0d23ed38aa67880693bf97ef5e6192bb05f17438..a38cc562d958239701795c71649c81f69080453e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,7 @@ .project .settings .springBeans -bin/ + ### IntelliJ IDEA ### /.idea/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 01dcd70daf734e5c0c802a8e5d22d8d47a2be092..58439f8875e39bca99bc07b5e7b760e425872eb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # 更新日志 +## V 1.8.0 + +*2021-01-10* 一些完善。。。 + ## V 1.7.0 *2020-02-15* 支持docker部署、售后管理、通知管理、数据库七天备份。 diff --git a/README.md b/README.md index 7110566704a60856672283321921625f98dec33b..6265d90bcbc245d86db1aa59da78c8727545c62e 100644 --- a/README.md +++ b/README.md @@ -13,17 +13,7 @@ litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端 ### 小商场实例 -* renard-wx模块实例 - -![](./doc/pics/readme/renard_wx_demo.png) - -> 注意:此实例是真实小商场,开发者可以购买商品和付款,但请不要尝试退款操作。 - -* litemall-wx模块实例 - -![](./doc/pics/readme/litemall_wx_demo.png) - -> 注意:此实例是测试小商场,开发者请不要尝试购买商品、付款、退款操作。 +暂无实例,建议开发者本地测试。 ### 轻商场实例 @@ -43,7 +33,7 @@ litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端 1. 浏览器打开,输入以下网址: [http://122.51.199.160:8080/#/login](http://122.51.199.160:8080/#/login) 2. 管理员用户名`admin123`,管理员密码`admin123` -> 注意:此实例只是测试管理后台,不是前两个小商城的管理后台。 +> 注意:此实例只是测试管理后台。 ## 项目代码 @@ -154,7 +144,7 @@ litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端 ## 开发计划 -当前版本[v1.7.0](https://linlinjava.gitbook.io/litemall/changelog) +当前版本[v1.8.0](https://linlinjava.gitbook.io/litemall/changelog) 目前项目开发中,存在诸多不足,以下是目前规划的开发计划。 @@ -233,7 +223,7 @@ V 3.0.0 完成以下目标: ## 问题 -![](doc/pics/readme/qq3.png) +![](doc/pics/readme/qq4.png) * 开发者有问题或者好的建议可以用Issues反馈交流,请给出详细信息 * 在开发交流群中应讨论开发、业务和合作问题 diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000000000000000000000000000000000..823fffb2287f0d9f874a48fdeb8dc8a1b85ae2fe --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +## Supported Versions + +Use this section to tell people about which versions of your project are +currently being supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| 1.8.x | :white_check_mark: | +| < 1.8 | :x: | + +## Reporting a Vulnerability + +Please report security issues to linlinjavaer@gmail.com diff --git a/deploy/bin/deploy.sh b/deploy/bin/deploy.sh index 4105e33cf450b7803f292732626d9aa02278c702..7f3dc7a2b4cf007ecc80a2811b6028159376f6b5 100644 --- a/deploy/bin/deploy.sh +++ b/deploy/bin/deploy.sh @@ -1,8 +1,12 @@ #!/bin/bash # 本脚本的作用是停止当前Spring Boot应用,然后再次部署 -sudo service litemall stop -sudo ln -f -s /home/ubuntu/deploy/litemall/litemall.jar /etc/init.d/litemall -sudo update-rc.d litemall defaults -sudo update-rc.d litemall enable -sudo service litemall start \ No newline at end of file +PID=$(ps -ef | grep litemall.jar | grep -v grep | awk '{ print $2 }') + +if [ ! -z "$PID" ] +then + kill $PID +fi + +cd /home/ubuntu/deploy/litemall || exit 2 +nohup java -jar litemall.jar > log.log 2>&1 & \ No newline at end of file diff --git a/deploy/bin/reset.sh b/deploy/bin/reset.sh index 4ac1a1a27c36a15a1fdd17a78599cf6010838803..aeea43a56a7a100317cc2d73830492f987eab3f8 100644 --- a/deploy/bin/reset.sh +++ b/deploy/bin/reset.sh @@ -28,4 +28,5 @@ rm -f ./** # 重新部署服务 cd /home/ubuntu/deploy/bin || exit 2 +sudo ./stop.sh sudo ./deploy.sh \ No newline at end of file diff --git a/deploy/bin/stop.sh b/deploy/bin/stop.sh new file mode 100644 index 0000000000000000000000000000000000000000..82ab3f5f3b67cc494f58199cd163d148634b80b1 --- /dev/null +++ b/deploy/bin/stop.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +PID=$(ps -ef | grep litemall.jar | grep -v grep | awk '{ print $2 }') + +if [ ! -z "$PID" ] +then + kill $PID +fi \ No newline at end of file diff --git a/deploy/litemall/application.yml b/deploy/litemall/application.yml index a3b9d922db0e60c5809ef1ea920e4391126e9c99..4ef7446b6a5f643f9061c273d29825891dcb4983 100644 --- a/deploy/litemall/application.yml +++ b/deploy/litemall/application.yml @@ -6,7 +6,7 @@ spring: datasource: druid: url: jdbc:mysql://localhost:3306/litemall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true&verifyServerCertificate=false&useSSL=false - driver-class-name: com.mysql.jdbc.Driver + driver-class-name: com.mysql.cj.jdbc.Driver username: litemall password: litemall123456 initial-size: 10 diff --git a/doc/admin.md b/doc/admin.md index 04efe1b0e674e2bea66be04da09d7c4907b6354a..73056ee70c2193865bba33de3c60deedd328baf1 100644 --- a/doc/admin.md +++ b/doc/admin.md @@ -8,7 +8,7 @@ * vue-router * axios * element - * vue-element-admin 4.2.1 + * vue-element-admin 4.3.0 * 其他,见package.json * 管理后台后端, 即litemall-admin-api模块 * Spring Boot 2.x @@ -18,9 +18,6 @@ * `缺失`首页中实现一些小组件,同时点击能够跳转相应页面 * `缺失`支持导出表所有数据 -* `改善`管理员登录页面打开慢,优化速度 -* `改善`地址优化,目前每一次点击都会请求后台,应该缓存已有的数据 -* `改善`vue和vue-element-admin等及时更新 ## 4.1 litemall-admin-api diff --git a/doc/how to implement best admin.md b/doc/how to implement best admin.md new file mode 100644 index 0000000000000000000000000000000000000000..b1b4b4048bc560d8c9a50015b0a7b2cd3b5664b5 --- /dev/null +++ b/doc/how to implement best admin.md @@ -0,0 +1,182 @@ +# 最佳管理后台开发实践 + +很多开源项目和商业项目本质上就是一个业务管理后台,因此开源项目或者商业项目都会开发一个剥离业务的通用管理后台。 +但是从本人开发和使用经验出发,希望解决以下问题: + +1. 什么是最佳管理后台 + + 目前有很多优秀的开源管理后台项目,文档都写了如何使用、如何开发,但是没有文档详细讨论为什么需要这个功能、 + 这个功能到底完成什么功能。当然,很多功能其实的确是不言自明的,但是本节希望从管理后台的使用者和管理员角度 + 来系统性地阐述讨论通用的管理后台的一些需求分析。 + +2. 如何开发最佳管理后台 + + 对于一个功能,不能的开源项目给出了不同的解决方案,有的解决方案基本是一样的,有的还是有些出入,不同的实现方案 + 中是否能够有最佳方案,以及不同的方案如何来评判优劣。本节希望从管理后台的入门开发者角度来讨论如何开发一个 + 最佳管理后台。 + +注意: + +1. 当然,在没有限定条件下来讨论最佳管理后台,最后可能是被人各种喷。本人也的确没有参与过大型项目或者互联网项目的管理后台开发, +给不出业界企业级的管理后台开发经验。因此本节仅仅给出一个中、小、微规模的最佳基础通用管理后台的纸上谈兵文档。 + +2. 本节不讨论任何技术细节,不限定具体的技术框架,而是讨论管理后台的业务需求。 + +## 1. 最佳管理后台是什么? + +* 用户管理(管理员管理) +* 部门管理 +* 角色管理 +* 权限管理 +* 配置管理 +* 定时任务 +* 消息管理 +* 操作日志 +* 系统日志 +* 菜单管理 +* 国际化 +* 代码生成 +* 字典管理 +* 上传下载 + +### 1.1 用户管理 + + +1. 不要以性能指标来评判管理后台 + +2. + +## 权限管理 + +权限管理是一个管理后台的核心功能,不同的人员存在不同的权限。 + +但是具体应该呈现什么效果? + +有没有遇到过这种场景: +> 某天业务人员找你授权,说别人存在某个页面“AA”或者某个按钮“BB”,他现在也需要; +> 作为系统管理员,你点击打开授权页面,然后看到了各种权限。 +> 然后问题来了,各种权限没有任何名字是和“AA”、“BB”相关的,请问系统管理员如何授权? +> 最后系统管理员只能从管理后台的开发或者维护工程师才能问到。 + +从功能上来说,这个管理后台的确实现了权限功能,但是从实际场景出发,这里的权限管理 +实现的非常糟糕。开发工程师或者产品经理根本没有从使用者角度出发来考虑权限管理。 + +原则1: +> 假定权限列表的每一项是(AA,BB),则AA是权限所对应的页面,BB是权限所对应页面的操作按钮。 + +管理员授权时看到(AA,BB),就非常直观地了解当前权限会产生的效果,即授权BB则AA页面的 +BB按钮出现,回收BB则AA页面的BB按钮会消失。 + + + +## 代码生产 + +## 参数管理 + +## 字典管理 + +字典是什么 + +## 菜单管理 + +很多管理后台都实现了菜单管理,从个人管理后台的使用角度出发,其实不是很理解菜单管理的意义。 +也许来一个调查,多少管理后台的管理员会使用或者使用过菜单管理功能。 + +当然,菜单管理可能想实现的功能是分配不同的角色不能的前端操作页面。 + +## 定时任务 + +定时任务需要吗? + +当然需要,很多功能都依赖定时任务定时完成。 + +但是管理后台的前端页面真的需要定时任务吗? + +很奇怪,很多项目都存在定时任务菜单,而具体的页面则提供定时任务的创建、修改、删除等功能。 + +仔细看一下定时任务的创建效果,需要管理员设置CLASS方法、cron表达式。 + +让我们思考一下, + +一个普通的业务管理员能看懂这个页面是做什么的吗,CLASS方法?,cron表达式? + +好吧,换成懂点技术的系统管理员,也许看的懂这个页面,只是他需要这个页面做什么?他怎么设置CLASS方法? + +个人觉得,如果从使用者角度来考虑,定时任务的设计有点伪需求,这个功能根本没有使用场景,或者说 +定时任务的创建、删除等功能没有使用场景。 + +进一步考虑,如果设计定时任务,那么管理后台应该如何设计,以下是个人的一些想法: + +1. 和需求方交流是否需要定时操作的一些使用场景,是否需要控制定时任务。 +2. + +## 国际化 + +国际化看起来似乎是必要的,毕竟说不定开发的管理后台有可能要被全世界的人使用。 +不过个人觉得这个概率是较小的,因此开发初期似乎是没有必要支持国际化。 + +此外,为了支持国际化,通常做法是代码中使用key,然后创建对应value的多个资源文件,最后根据配置 +动态解析国际化文字。这种两步骤的开发方式可以很好地支持国际化。但是,如果项目使用场景真的不需要 +国际化,那么默认内置国际化支持的管理后台其实反而降低了开发体验和速度。 + +* 开发时需要考虑key怎么合理化命名(有可能有上百个), +* 开发需要写一点包装key的代码,例如前端页面可能是`$t(key)` +* 调试也不直观,代码和页面中都是key + +因此,个人觉得一个最佳管理后台默认不支持国际化可能是最佳的。 + +## 代码生成 + + + +因此,经过以上讨论, + +* 用户管理:需开发 +* 部门管理:需开发 +* 角色管理:需开发 +* 权限管理:需开发 +* 配置管理:需开发 +* 定时任务:不开发 +* 消息管理:可开发 +* 操作日志:需开发 +* 系统日志:需开发 +* 菜单管理:不开发 +* 字典管理:不开发 +* 上传下载:需开发 + +## 2. 管理后台后端开发 + +* 文件上传下载 +* 事务管理 +* 分页管理 +* 异常处理 +* 操作权限 +* 数据权限 + +## 2.1 性能 + +首先提出的第一条规则就是: + +不要过早优化性能,也不要追求性能极限,或者说理解管理员的需求极限。 + +从一个管理平台的使用者角度出发,点击页面以后如果在秒级响应,本人是可以接收的,极限容忍时间可以是十秒(当然这样的页面不能太多)。 + +也就是和京东、天猫这些面向大众用户的系统不同,一个普通的业务管理系统使用者在使用时对系统的可容忍度较高。 + +当然,这里不是说性能不重要,而是觉得对性能的开发时间和精力可以分配较小。 + +## + +## 3. 管理后台前端开发 + +* 文件预览 +* 图片预览 + + +## 4. 业务管理模块 + +* 列表展示 +* 增删改查 +* 分页 +* Excel导入导出 +* 业务统计 \ No newline at end of file diff --git a/doc/pics/readme/qq4.png b/doc/pics/readme/qq4.png new file mode 100644 index 0000000000000000000000000000000000000000..19fa4c40a5abef31e13cf3f295f3af871def5b84 Binary files /dev/null and b/doc/pics/readme/qq4.png differ diff --git a/doc/project.md b/doc/project.md index fc9db2b8d8a802cb93a05d75d451df84519a7f24..21a5ad1e19f748a18764d774ae4ac73a4e542937 100644 --- a/doc/project.md +++ b/doc/project.md @@ -500,7 +500,7 @@ spring: datasource: druid: url: jdbc:mysql://localhost:3306/litemall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true&verifyServerCertificate=false&useSSL=false - driver-class-name: com.mysql.jdbc.Driver + driver-class-name: com.mysql.cj.jdbc.Driver username: litemall password: litemall123456 initial-size: 10 @@ -1339,11 +1339,11 @@ litemall-admin编译得到的前端文件在第一次加载时相当耗时,这 这里deploy部署方式比较简单不灵活,开发者可以参考开发自己的项目脚本。 -#### 1.7.2.2 .gitlab-ci.yml部署 +#### 1.7.2.2 docker部署 -目前不支持 +当前项目存在docker部署文件夹,这个是上述1.5.1节部署腾讯云服务器所采取的一些脚本。 -#### 1.7.2.3 docker部署 +#### 1.7.2.3 .gitlab-ci.yml部署 目前不支持 diff --git a/docker/litemall/application.yml b/docker/litemall/application.yml index a28dd8b5bc47ca0c794b9684b5262541bbc3fd23..9f4434b0b29f35bbe8456d37353bdb654e592eed 100644 --- a/docker/litemall/application.yml +++ b/docker/litemall/application.yml @@ -5,8 +5,8 @@ spring: encoding: UTF-8 datasource: druid: - url: jdbc:mysql://mysql:3306/litemall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true&verifyServerCertificate=false&useSSL=false - driver-class-name: com.mysql.jdbc.Driver + url: jdbc:mysql://mysql:3306/litemall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true&verifyServerCertificate=false&useSSL=true + driver-class-name: com.mysql.cj.jdbc.Driver username: litemall password: litemall123456 initial-size: 10 diff --git a/litemall-admin-api/pom.xml b/litemall-admin-api/pom.xml index 5afd40cc950ab2fb036f18768fa5005bc60f2d86..5e7f2757dedd12423eec6808773e9b06ea70fec3 100644 --- a/litemall-admin-api/pom.xml +++ b/litemall-admin-api/pom.xml @@ -40,6 +40,10 @@ org.apache.shiro shiro-spring-boot-web-starter + + com.github.penggle + kaptcha + com.github.xiaoymin swagger-bootstrap-ui diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/KaptchaConfig.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/KaptchaConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..3dad229dbca7ff41ccfa441c0f8e8449553c1b29 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/KaptchaConfig.java @@ -0,0 +1,31 @@ +package org.linlinjava.litemall.admin.config; + +import com.google.code.kaptcha.Producer; +import com.google.code.kaptcha.impl.DefaultKaptcha; +import com.google.code.kaptcha.util.Config; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Properties; + +@Configuration +public class KaptchaConfig { + + @Bean + public Producer kaptchaProducer() { + Properties properties = new Properties(); + properties.setProperty("kaptcha.image.width", "100"); + properties.setProperty("kaptcha.image.height", "40"); + properties.setProperty("kaptcha.textproducer.font.size", "32"); + properties.setProperty("kaptcha.textproducer.font.color", "0,0,0"); + properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYAZ"); + properties.setProperty("kaptcha.textproducer.char.length", "4"); + properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise"); + + DefaultKaptcha kaptcha = new DefaultKaptcha(); + Config config = new Config(properties); + kaptcha.setConfig(config); + return kaptcha; + } + +} \ No newline at end of file diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/ShiroConfig.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/ShiroConfig.java index 18b9b257629e20cf4b7eb4a31ed4aa275ee04a57..9ef972f5792f3e0660af0cc9466bdbb375d5e811 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/ShiroConfig.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/ShiroConfig.java @@ -29,6 +29,7 @@ public class ShiroConfig { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map filterChainDefinitionMap = new LinkedHashMap(); + filterChainDefinitionMap.put("/admin/auth/kaptcha", "anon"); filterChainDefinitionMap.put("/admin/auth/login", "anon"); filterChainDefinitionMap.put("/admin/auth/401", "anon"); filterChainDefinitionMap.put("/admin/auth/index", "anon"); diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/job/DbJob.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/job/DbJob.java index 351be5397777dda4afd7a76aff69f23d24532824..b4271283d2d728f034884069093ea5ddd880b5e1 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/job/DbJob.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/job/DbJob.java @@ -38,7 +38,7 @@ public class DbJob { String db = url.substring(index1+5, index2); LocalDate localDate = LocalDate.now(); - String fileName = localDate.toString(); + String fileName = localDate.toString() + ".sql"; File file = new File("backup", fileName); file.getParentFile().mkdirs(); file.createNewFile(); @@ -47,8 +47,11 @@ public class DbJob { DbUtil.backup(file, user, password, db); // 删除七天前数据库备份文件 LocalDate before = localDate.minusDays(7); - File fileBefore = new File("backup", fileName); - fileBefore.deleteOnExit(); + String fileBeforeName = before.toString()+".sql"; + File fileBefore = new File("backup", fileBeforeName); + if (fileBefore.exists()) { + fileBefore.delete(); + } logger.info("系统结束定时任务数据库备份"); } diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminOrderService.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminOrderService.java index daae6ee23d44b77950009ea7feaada7be94f138a..b05184733f0cc2efa7ce032115c5fb294045ee75 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminOrderService.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminOrderService.java @@ -1,5 +1,6 @@ package org.linlinjava.litemall.admin.service; +import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse; import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest; import com.github.binarywang.wxpay.bean.result.WxPayRefundResult; import com.github.binarywang.wxpay.exception.WxPayException; @@ -8,11 +9,13 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.linlinjava.litemall.core.notify.NotifyService; import org.linlinjava.litemall.core.notify.NotifyType; +import org.linlinjava.litemall.core.util.DateTimeUtil; import org.linlinjava.litemall.core.util.JacksonUtil; import org.linlinjava.litemall.core.util.ResponseUtil; import org.linlinjava.litemall.db.domain.*; import org.linlinjava.litemall.db.service.*; import org.linlinjava.litemall.db.util.CouponUserConstant; +import org.linlinjava.litemall.db.util.GrouponConstant; import org.linlinjava.litemall.db.util.OrderUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -26,6 +29,7 @@ import java.util.List; import java.util.Map; import static org.linlinjava.litemall.admin.util.AdminResponseCode.*; +import static org.linlinjava.litemall.admin.util.AdminResponseCode.ORDER_PAY_FAILED; @Service @@ -51,11 +55,10 @@ public class AdminOrderService { @Autowired private LitemallCouponUserService couponUserService; - public Object list(Integer userId, String orderSn, LocalDateTime start, LocalDateTime end, List orderStatusArray, + public Object list(String nickname, String consignee, String orderSn, LocalDateTime start, LocalDateTime end, List orderStatusArray, Integer page, Integer limit, String sort, String order) { - List orderList = orderService.querySelective(userId, orderSn, start, end, orderStatusArray, page, limit, - sort, order); - return ResponseUtil.okList(orderList); + Map data = (Map)orderService.queryVoSelective(nickname, consignee, orderSn, start, end, orderStatusArray, page, limit, sort, order); + return ResponseUtil.ok(data); } public Object detail(Integer id) { @@ -287,4 +290,29 @@ public class AdminOrderService { return ResponseUtil.ok(); } + public Object pay(String body) { + Integer orderId = JacksonUtil.parseInteger(body, "orderId"); + String newMoney = JacksonUtil.parseString(body, "newMoney"); + + if (orderId == null || StringUtils.isEmpty(newMoney)) { + return ResponseUtil.badArgument(); + } + BigDecimal actualPrice = new BigDecimal(newMoney); + + LitemallOrder order = orderService.findById(orderId); + if (order == null) { + return ResponseUtil.badArgument(); + } + if (!order.getOrderStatus().equals(OrderUtil.STATUS_CREATE)) { + return ResponseUtil.fail(ORDER_PAY_FAILED, "当前订单状态不支持线下收款"); + } + + order.setActualPrice(actualPrice); + order.setOrderStatus(OrderUtil.STATUS_PAY); + if (orderService.updateWithOptimisticLocker(order) == 0) { + return WxPayNotifyResponse.fail("更新数据已失效"); + } + + return ResponseUtil.ok(); + } } diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/shiro/AdminAuthorizingRealm.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/shiro/AdminAuthorizingRealm.java index 505f3ec453e52ef56f840d9a9cf383107a551567..9dc143f99ab3cd737ac610adf48a9f229e9a7d62 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/shiro/AdminAuthorizingRealm.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/shiro/AdminAuthorizingRealm.java @@ -13,19 +13,27 @@ import org.linlinjava.litemall.db.service.LitemallAdminService; import org.linlinjava.litemall.db.service.LitemallPermissionService; import org.linlinjava.litemall.db.service.LitemallRoleService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import java.util.List; import java.util.Set; +/** + * 下面三个Autowired注解需要配合Lazy注解使用,否则会导致这三个service相关的事务失效。 + * https://gitee.com/linlinjava/litemall/issues/I3I94X#note_4809495 + */ public class AdminAuthorizingRealm extends AuthorizingRealm { @Autowired + @Lazy private LitemallAdminService adminService; @Autowired + @Lazy private LitemallRoleService roleService; @Autowired + @Lazy private LitemallPermissionService permissionService; @Override diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/shiro/AdminWebSessionManager.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/shiro/AdminWebSessionManager.java index e2795bbb0ea25a155c6d84bee97fe8fb9bf0549b..3234a62f3d74e34e97ccb5794d3aed08a1bd9bf3 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/shiro/AdminWebSessionManager.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/shiro/AdminWebSessionManager.java @@ -14,6 +14,14 @@ public class AdminWebSessionManager extends DefaultWebSessionManager { public static final String LOGIN_TOKEN_KEY = "X-Litemall-Admin-Token"; private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request"; + + public AdminWebSessionManager() { + super(); + setGlobalSessionTimeout(MILLIS_PER_HOUR * 6); +// setSessionIdCookieEnabled(false); + setSessionIdUrlRewritingEnabled(false); + } + @Override protected Serializable getSessionId(ServletRequest request, ServletResponse response) { String id = WebUtils.toHttp(request).getHeader(LOGIN_TOKEN_KEY); diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/util/AdminResponseCode.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/util/AdminResponseCode.java index 3b926b30e6205f7ce9abe23ee6a0233be73556a9..8d7a02071d8cbeb2dc05f5b697196723e495e3e2 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/util/AdminResponseCode.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/util/AdminResponseCode.java @@ -7,13 +7,15 @@ public class AdminResponseCode { public static final Integer ADMIN_ALTER_NOT_ALLOWED = 603; public static final Integer ADMIN_DELETE_NOT_ALLOWED = 604; public static final Integer ADMIN_INVALID_ACCOUNT = 605; + public static final Integer ADMIN_INVALID_KAPTCHA = 606; + public static final Integer ADMIN_INVALID_KAPTCHA_REQUIRED = 607; public static final Integer GOODS_UPDATE_NOT_ALLOWED = 610; public static final Integer GOODS_NAME_EXIST = 611; public static final Integer ORDER_CONFIRM_NOT_ALLOWED = 620; public static final Integer ORDER_REFUND_FAILED = 621; public static final Integer ORDER_REPLY_EXIST = 622; public static final Integer ORDER_DELETE_FAILED = 623; - public static final Integer USER_INVALID_NAME = 630; + public static final Integer ORDER_PAY_FAILED = 624;public static final Integer USER_INVALID_NAME = 630; public static final Integer USER_INVALID_PASSWORD = 631; public static final Integer USER_INVALID_MOBILE = 632; public static final Integer USER_NAME_EXIST = 633; diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAftersaleController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAftersaleController.java index 8694625fc63d5a2e03508586d2a0465b27600e8d..55877548989e6dc72860671dcbf00914d127b78f 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAftersaleController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAftersaleController.java @@ -183,7 +183,7 @@ public class AdminAftersaleController { wxPayRefundRequest.setOutRefundNo("refund_" + order.getOrderSn()); // 元转成分 Integer totalFee = aftersaleOne.getAmount().multiply(new BigDecimal(100)).intValue(); - wxPayRefundRequest.setTotalFee(totalFee); + wxPayRefundRequest.setTotalFee(order.getActualPrice().multiply(new BigDecimal(100)).intValue()); wxPayRefundRequest.setRefundFee(totalFee); WxPayRefundResult wxPayRefundResult; diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAuthController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAuthController.java index 59becab5442224598f68ce7a7a02723bb062d425..3110fca392287504ecc36916448acc9141a4a1c0 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAuthController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAuthController.java @@ -1,5 +1,6 @@ package org.linlinjava.litemall.admin.web; +import com.google.code.kaptcha.Producer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.shiro.SecurityUtils; @@ -25,11 +26,16 @@ import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.time.LocalDateTime; import java.util.*; -import static org.linlinjava.litemall.admin.util.AdminResponseCode.ADMIN_INVALID_ACCOUNT; +import static org.linlinjava.litemall.admin.util.AdminResponseCode.*; @RestController @RequestMapping("/admin/auth") @@ -46,6 +52,35 @@ public class AdminAuthController { @Autowired private LogHelper logHelper; + @Autowired + private Producer kaptchaProducer; + + @GetMapping("/kaptcha") + public Object kaptcha(HttpServletRequest request) { + String kaptcha = doKaptcha(request); + if (kaptcha != null) { + return ResponseUtil.ok(kaptcha); + } + return ResponseUtil.fail(); + } + + private String doKaptcha(HttpServletRequest request) { + // 生成验证码 + String text = kaptchaProducer.createText(); + BufferedImage image = kaptchaProducer.createImage(text); + HttpSession session = request.getSession(); + session.setAttribute("kaptcha", text); + + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ImageIO.write(image, "jpeg", outputStream); + String base64 = Base64.getEncoder().encodeToString(outputStream.toByteArray()); + return "data:image/jpeg;base64," + base64.replaceAll("\r\n", ""); + } catch (IOException e) { + return null; + } + } + /* * { username : value, password : value } */ @@ -53,17 +88,27 @@ public class AdminAuthController { public Object login(@RequestBody String body, HttpServletRequest request) { String username = JacksonUtil.parseString(body, "username"); String password = JacksonUtil.parseString(body, "password"); +// String code = JacksonUtil.parseString(body, "code"); if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { return ResponseUtil.badArgument(); } +// if (StringUtils.isEmpty(code)) { +// return ResponseUtil.fail(ADMIN_INVALID_KAPTCHA_REQUIRED, "验证码不能空"); +// } + +// HttpSession session = request.getSession(); +// String kaptcha = (String)session.getAttribute("kaptcha"); +// if (Objects.requireNonNull(code).compareToIgnoreCase(kaptcha) != 0) { +// return ResponseUtil.fail(ADMIN_INVALID_KAPTCHA, "验证码不正确", doKaptcha(request)); +// } Subject currentUser = SecurityUtils.getSubject(); try { currentUser.login(new UsernamePasswordToken(username, password)); } catch (UnknownAccountException uae) { logHelper.logAuthFail("登录", "用户帐号或密码不正确"); - return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "用户帐号或密码不正确"); + return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "用户帐号或密码不正确", doKaptcha(request)); } catch (LockedAccountException lae) { logHelper.logAuthFail("登录", "用户帐号已锁定不可用"); return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "用户帐号已锁定不可用"); diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminOrderController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminOrderController.java index b5b4eea25c2ec5bd14a58064f94fd37fe698bd7d..ce9bb5c37203e4766cc4be91fb787c538f536cf7 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminOrderController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminOrderController.java @@ -33,7 +33,6 @@ public class AdminOrderController { /** * 查询订单 * - * @param userId * @param orderSn * @param orderStatusArray * @param page @@ -45,7 +44,7 @@ public class AdminOrderController { @RequiresPermissions("admin:order:list") @RequiresPermissionsDesc(menu = {"商场管理", "订单管理"}, button = "查询") @GetMapping("/list") - public Object list(Integer userId, String orderSn, + public Object list(String nickname, String consignee, String orderSn, @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime start, @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime end, @RequestParam(required = false) List orderStatusArray, @@ -53,7 +52,7 @@ public class AdminOrderController { @RequestParam(defaultValue = "10") Integer limit, @Sort @RequestParam(defaultValue = "add_time") String sort, @Order @RequestParam(defaultValue = "desc") String order) { - return adminOrderService.list(userId, orderSn, start, end, orderStatusArray, page, limit, sort, order); + return adminOrderService.list(nickname, consignee, orderSn, start, end, orderStatusArray, page, limit, sort, order); } /** @@ -105,6 +104,12 @@ public class AdminOrderController { return adminOrderService.ship(body); } + @RequiresPermissions("admin:order:pay") + @RequiresPermissionsDesc(menu = {"商场管理", "订单管理"}, button = "订单收款") + @PostMapping("/pay") + public Object pay(@RequestBody String body) { + return adminOrderService.pay(body); + } /** * 删除订单 diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminUserController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminUserController.java index 2052d616c183e3d2a62424b0768f600774eb8ccd..839a83d8397f0c2db3e7af9cb1b5a5bab0c06d1d 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminUserController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminUserController.java @@ -12,12 +12,16 @@ import org.linlinjava.litemall.db.service.LitemallUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; +import javax.validation.constraints.NotNull; + @RestController @RequestMapping("/admin/user") @Validated @@ -38,4 +42,17 @@ public class AdminUserController { List userList = userService.querySelective(username, mobile, page, limit, sort, order); return ResponseUtil.okList(userList); } + @RequiresPermissions("admin:user:list") + @RequiresPermissionsDesc(menu = {"用户管理", "会员管理"}, button = "详情") + @GetMapping("/detail") + public Object userDetail(@NotNull Integer id) { + LitemallUser user=userService.findById(id); + return ResponseUtil.ok(user); + } + @RequiresPermissions("admin:user:list") + @RequiresPermissionsDesc(menu = {"用户管理", "会员管理"}, button = "编辑") + @PostMapping("/update") + public Object userUpdate(@RequestBody LitemallUser user) { + return ResponseUtil.ok(userService.updateById(user)); + } } diff --git a/litemall-admin/package.json b/litemall-admin/package.json index ba0a1f8444527b1cc1e55aabaf977154985fed40..a8a30dd2e4dff460ebea333d062581535da2f329 100644 --- a/litemall-admin/package.json +++ b/litemall-admin/package.json @@ -1,7 +1,7 @@ { "name": "litemall-admin", "version": "1.0.0", - "description": "litemall-admin basing on vue-element-admin 4.2.1", + "description": "litemall-admin basing on vue-element-admin 4.3.0", "author": "linlinjava ", "license": "MIT", "scripts": { @@ -46,22 +46,23 @@ "@tinymce/tinymce-vue": "3.0.1", "lodash": "^4.17.11", "v-charts": "1.19.0", - "axios": "0.18.1", + "axios": ">=0.21.2", "clipboard": "2.0.4", "connect": "3.6.6", "echarts": "4.2.1", - "element-ui": "2.12.0", + "element-ui": "2.15.6", "file-saver": "1.3.8", "js-cookie": "2.2.0", "normalize.css": "7.0.0", "nprogress": "0.2.0", "path-to-regexp": "2.4.0", "screenfull": "4.2.0", + "script-loader": "0.7.2", "vue": "2.6.10", "vue-count-to": "1.0.13", "vue-router": "3.0.2", "vuex": "3.1.0", - "xlsx": "0.14.1" + "xlsx": ">=0.17.0" }, "devDependencies": { "@babel/core": "7.0.0", @@ -83,11 +84,10 @@ "html-webpack-plugin": "3.2.0", "husky": "1.3.1", "lint-staged": "8.1.5", - "node-sass": "^4.9.0", + "sass": "^1.26.2", "runjs": "^4.3.2", "sass-loader": "^7.1.0", "script-ext-html-webpack-plugin": "2.1.3", - "script-loader": "0.7.2", "serve-static": "^1.13.2", "svg-sprite-loader": "4.1.3", "svgo": "1.2.0", diff --git a/litemall-admin/src/api/login.js b/litemall-admin/src/api/login.js index 2d2261adaf6061c81479a7472086a6ff5ee967c3..dccd73ae2d69ba17fdcd3639eb49748253fda89d 100644 --- a/litemall-admin/src/api/login.js +++ b/litemall-admin/src/api/login.js @@ -1,9 +1,10 @@ import request from '@/utils/request' -export function loginByUsername(username, password) { +export function loginByUsername(username, password, code) { const data = { username, - password + password, + code } return request({ url: '/auth/login', @@ -27,3 +28,9 @@ export function getUserInfo(token) { }) } +export function getKaptcha() { + return request({ + url: '/auth/kaptcha', + method: 'get' + }) +} diff --git a/litemall-admin/src/api/order.js b/litemall-admin/src/api/order.js index 122fdcbf5c9630dde5269d2326eff4c239e5244f..6cf216362a6e222a4dec4d04e376f4d69d1dc48d 100644 --- a/litemall-admin/src/api/order.js +++ b/litemall-admin/src/api/order.js @@ -36,6 +36,14 @@ export function refundOrder(data) { }) } +export function payOrder(data) { + return request({ + url: '/order/pay', + method: 'post', + data + }) +} + export function deleteOrder(data) { return request({ url: '/order/delete', diff --git a/litemall-admin/src/api/user.js b/litemall-admin/src/api/user.js index a7b8b42e0ff0099ee8165cf8db4c28486ef1f6a6..33dc2f515bcf1bd53cb39bf7e863c9047789c1f3 100644 --- a/litemall-admin/src/api/user.js +++ b/litemall-admin/src/api/user.js @@ -8,6 +8,22 @@ export function fetchList(query) { }) } +export function userDetail(id) { + return request({ + url: '/user/detail', + method: 'get', + params: {id} + }) +} + +export function updateUser(data) { + return request({ + url: '/user/update', + method: 'post', + data + }) +} + export function listAddress(query) { return request({ url: '/address/list', @@ -47,4 +63,3 @@ export function listHistory(query) { params: query }) } - diff --git a/litemall-admin/src/components/Notice/index.vue b/litemall-admin/src/components/Notice/index.vue index 1b7499bede3d904f8fa8d1da9804f3e81c570b29..c131013c97e962cc6973aa42b3ab9b118734843c 100644 --- a/litemall-admin/src/components/Notice/index.vue +++ b/litemall-admin/src/components/Notice/index.vue @@ -42,7 +42,7 @@ export default { diff --git a/litemall-admin/src/views/layout/components/Sidebar/Link.vue b/litemall-admin/src/views/layout/components/Sidebar/Link.vue index 5d366f246c38d6a13dd50476a2ce88124cc13101..b66755dab0aa6acac7729ee70092535f9b7b0d2a 100644 --- a/litemall-admin/src/views/layout/components/Sidebar/Link.vue +++ b/litemall-admin/src/views/layout/components/Sidebar/Link.vue @@ -1,14 +1,11 @@ - diff --git a/litemall-admin/src/views/mall/brand.vue b/litemall-admin/src/views/mall/brand.vue index a13d8ddb27ac1becb10c1be620173adddc93948e..f26beb41690b9e12b2e6e4e37cad739a56eee5e8 100644 --- a/litemall-admin/src/views/mall/brand.vue +++ b/litemall-admin/src/views/mall/brand.vue @@ -3,8 +3,8 @@
- - + + 查找 添加 导出 @@ -13,19 +13,19 @@ - + - + - + - +