Browse Source

Signed-off-by: ljx <809268652@qq.com>

ljx 9 months ago
parent
commit
745f1bd3e9
100 changed files with 6405 additions and 0 deletions
  1. BIN
      litemall-master.zip
  2. 2 0
      litemall-master/.gitbook.yaml
  3. 54 0
      litemall-master/.gitignore
  4. 21 0
      litemall-master/LICENSE
  5. 0 0
      litemall-master/README.md
  6. 3 0
      litemall-master/deploy/.gitignore
  7. 104 0
      litemall-master/deploy/README.md
  8. 0 0
      litemall-master/deploy/db/.gitkeep
  9. 148 0
      litemall-master/deploy/litemall/application.yml
  10. 52 0
      litemall-master/deploy/util/lazy.sh
  11. 34 0
      litemall-master/deploy/util/package.sh
  12. 3 0
      litemall-master/docker/.gitignore
  13. 0 0
      litemall-master/docker/README.md
  14. 6 0
      litemall-master/docker/db/conf.d/my.cnf
  15. 0 0
      litemall-master/docker/db/data/.gitkeep
  16. 0 0
      litemall-master/docker/db/init-sql/.gitkeep
  17. 35 0
      litemall-master/docker/docker-compose.yml
  18. 0 0
      litemall-master/docker/litemall/.gitkeep
  19. 4 0
      litemall-master/docker/litemall/Dockerfile
  20. 148 0
      litemall-master/docker/litemall/application.yml
  21. 52 0
      litemall-master/docker/util/lazy.sh
  22. 34 0
      litemall-master/docker/util/package.sh
  23. 3 0
      litemall-master/litemall-admin-api/.gitignore
  24. 67 0
      litemall-master/litemall-admin-api/pom.xml
  25. 20 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/Application.java
  26. 14 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/annotation/RequiresPermissionsDesc.java
  27. 48 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/AdminSwagger2Configuration.java
  28. 75 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/ShiroConfig.java
  29. 34 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/ShiroExceptionHandler.java
  30. 46 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/dto/GoodsAllinone.java
  31. 53 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/job/CouponJob.java
  32. 56 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/job/DbJob.java
  33. 91 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/job/OrderJob.java
  34. 355 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminGoodsService.java
  35. 290 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminOrderService.java
  36. 113 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/LogHelper.java
  37. 76 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/shiro/AdminAuthorizingRealm.java
  38. 29 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/shiro/AdminWebSessionManager.java
  39. 41 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/task/AdminTaskStartupRunner.java
  40. 68 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/task/GrouponRuleExpiredTask.java
  41. 30 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/util/AdminResponseCode.java
  42. 34 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/util/Permission.java
  43. 152 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/util/PermissionUtil.java
  44. 33 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/vo/CatVo.java
  45. 78 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/vo/CategoryVo.java
  46. 43 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/vo/PermVo.java
  47. 52 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/vo/RegionVo.java
  48. 31 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/vo/StatVo.java
  49. 100 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAdController.java
  50. 45 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAddressController.java
  51. 142 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAdminController.java
  52. 232 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAftersaleController.java
  53. 175 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAuthController.java
  54. 106 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminBrandController.java
  55. 153 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCategoryController.java
  56. 42 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCollectController.java
  57. 51 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCommentController.java
  58. 96 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminConfigController.java
  59. 116 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCouponController.java
  60. 49 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminDashbordController.java
  61. 46 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminFeedbackController.java
  62. 42 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminFootprintController.java
  63. 108 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGoodsController.java
  64. 190 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGrouponController.java
  65. 40 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminHistoryController.java
  66. 67 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminIndexController.java
  67. 99 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminIssueController.java
  68. 95 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminKeywordController.java
  69. 41 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminLogController.java
  70. 153 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminNoticeController.java
  71. 134 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminOrderController.java
  72. 159 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminProfileController.java
  73. 87 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminRegionController.java
  74. 227 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminRoleController.java
  75. 65 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminStatController.java
  76. 89 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminStorageController.java
  77. 127 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminTopicController.java
  78. 58 0
      litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminUserController.java
  79. 0 0
      litemall-master/litemall-admin-api/src/main/resources/application-admin.yml
  80. 21 0
      litemall-master/litemall-admin-api/src/main/resources/application.yml
  81. BIN
      litemall-master/litemall-admin-api/src/main/resources/static/litemall.png
  82. 30 0
      litemall-master/litemall-admin-api/src/test/java/org/linlinjava/litemall/admin/AdminConfigTest.java
  83. 25 0
      litemall-master/litemall-admin-api/src/test/java/org/linlinjava/litemall/admin/BcryptTest.java
  84. 27 0
      litemall-master/litemall-admin-api/src/test/java/org/linlinjava/litemall/admin/CreateShareImageTest.java
  85. 55 0
      litemall-master/litemall-admin-api/src/test/java/org/linlinjava/litemall/admin/DbTest.java
  86. 31 0
      litemall-master/litemall-admin-api/src/test/java/org/linlinjava/litemall/admin/PermissionTest.java
  87. 14 0
      litemall-master/litemall-admin/.editorconfig
  88. 8 0
      litemall-master/litemall-admin/.env.deployment
  89. 14 0
      litemall-master/litemall-admin/.env.development
  90. 8 0
      litemall-master/litemall-admin/.env.production
  91. 4 0
      litemall-master/litemall-admin/.eslintignore
  92. 198 0
      litemall-master/litemall-admin/.eslintrc.js
  93. 23 0
      litemall-master/litemall-admin/.gitignore
  94. 5 0
      litemall-master/litemall-admin/babel.config.js
  95. 34 0
      litemall-master/litemall-admin/build/index.js
  96. 24 0
      litemall-master/litemall-admin/jest.config.js
  97. 9 0
      litemall-master/litemall-admin/jsconfig.json
  98. 104 0
      litemall-master/litemall-admin/package.json
  99. 5 0
      litemall-master/litemall-admin/postcss.config.js
  100. BIN
      litemall-master/litemall-admin/public/favicon.ico

BIN
litemall-master.zip


+ 2 - 0
litemall-master/.gitbook.yaml

@@ -0,0 +1,2 @@
+structure:
+  summary: doc/README.md

+ 54 - 0
litemall-master/.gitignore

@@ -0,0 +1,54 @@
+### gradle ###
+.gradle
+/build/
+!gradle/wrapper/gradle-wrapper.jar
+
+### STS ###
+.settings/
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+bin/
+
+### IntelliJ IDEA ###
+/.idea/
+/private/
+/storage/
+/litemall.iml
+.checkstyle
+.idea
+*.iws
+*.iml
+*.ipr
+rebel.xml
+### maven ###
+target/
+*.war
+*.ear
+*.zip
+*.tar
+*.tar.gz
+
+### logs ####
+/logs/
+*.log
+
+### temp ignore ###
+*.cache
+*.diff
+*.patch
+*.tmp
+*.java~
+*.properties~
+*.xml~
+
+### system ignore ###
+.DS_Store
+Thumbs.db
+Servers
+.metadata
+upload
+gen_code

+ 21 - 0
litemall-master/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 linlinjava(linlinjava@163.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 0 - 0
litemall-master/README.md


+ 3 - 0
litemall-master/deploy/.gitignore

@@ -0,0 +1,3 @@
+
+/db/litemall.sql
+/litemall/litemall.jar

+ 104 - 0
litemall-master/deploy/README.md

@@ -0,0 +1,104 @@
+## deploy
+
+### 项目打包
+
+1. 在服务器或者开发机打包项目到deploy;
+    ```
+    cd litemall
+    cat ./litemall-db/sql/litemall_schema.sql > ./deploy/db/litemall.sql
+    cat ./litemall-db/sql/litemall_table.sql >> ./deploy/db/litemall.sql
+    cat ./litemall-db/sql/litemall_data.sql >> ./deploy/db/litemall.sql
+    
+    cd ./litemall-admin
+    cnpm install
+    cnpm run build:dep
+    
+    cd ..
+    mvn clean package
+    cp -f ./litemall-all/target/litemall-all-*-exec.jar ./deploy/litemall/litemall.jar
+    ```
+    这里的工作是:
+    1. 把数据库文件拷贝到deploy/db文件夹
+    2. 编译litemall-admin项目
+    3. 编译litemall-all模块,同时把litemall-admin编译得到的静态文件拷贝到
+       litemall-all模块的static目录
+       
+2. 修改litemall文件夹下面的*.yml外部配置文件,当litemall-all模块启动时会
+    加载外部配置文件,而覆盖默认jar包内部的配置文件。
+    例如,配置文件中一些地方需要设置成远程服务器的IP地址
+    
+此时deploy部署包结构如下:
+
+* bin
+
+存放远程服务器运行的脚本,包括deploy.sh脚本和reset.sh脚本
+
+* db
+
+存放litemall数据库文件
+
+* litemall
+
+存放远程服务器运行的代码,包括litemall-all二进制可执行包和litemall外部配置文件
+
+* util
+存放开发服务器运行的脚本,包括package.sh脚本和lazy.sh脚本。
+由于是本地开发服务器运行,因此开发者可以不用上传到远程服务器。
+
+### 项目部署
+
+1. 远程服务器环境(MySQL和JDK1.8)已经安装好,请确保云服务器的安全组已经允许相应的端口。
+2. 导入db/litemall.sql
+    ```bash
+    cd /home/ubuntu/deploy/db
+    mysql -h localhost -u $ROOT -p$PASSWORD < litemall.sql
+    ```
+3. 启动服务
+    ```bash
+    sudo service litemall stop
+    sudo ln -f -s /home/ubuntu/deploy/litemall/litemall.jar /etc/init.d/litemall
+    sudo service litemall start
+    ```
+4. 测试是否部署成功(xxx.xxx.xxx.xxx是云服务器IP):
+    ```
+    http://xxx.xxx.xxx.xxx:8080/wx/index/index
+    http://xxx.xxx.xxx.xxx:8080/admin/index/index
+    http://xxx.xxx.xxx.xxx:8080/#/login
+    ```
+
+### 项目辅助脚本
+
+在前面的项目打包和项目部署中都是采用手动命令来部署。
+这里可以写一些脚本简化:
+
+* util/packet.sh
+
+在开发服务器运行可以自动项目打包
+
+* util/lazy.sh
+
+在开发服务器运行可以自动项目打包、项目上传远程服务器、自动登录系统执行项目部署脚本。
+    
+注意:
+> 1. 开发者需要在util/lazy.sh中设置相应的远程服务器登录账号和密钥文件路径。
+> 2. 开发者需要在bin/reset.sh设置远程服务器的MySQL的root登录账户。
+    
+* bin/deploy.sh
+
+在远程服务器运行可以自动部署服务
+
+* bin/reset.sh
+
+在远程服务器运行可以自动项目导入数据、删除本地上传图片、再执行bin/deploy.sh部署服务。
+
+注意:
+> 开发者需要在bin/reset.sh设置远程服务器的MySQL的root登录账户。
+
+总结,当开发者设置好配置信息以后,可以在本地运行lazy.sh脚本自动一键部署:
+```bash
+cd litemall
+./deploy/util/lazy.sh
+```
+
+不过由于需要设置的信息会包含敏感安全信息,强烈建议开发者参考这里的deploy文件夹,
+然后实现自己的deploy文件夹,妥善处置外部配置文件和脚本中的敏感安全信息!!!

+ 0 - 0
litemall-master/deploy/db/.gitkeep


+ 148 - 0
litemall-master/deploy/litemall/application.yml

@@ -0,0 +1,148 @@
+spring:
+  profiles:
+    active: none
+  message:
+    encoding: UTF-8
+  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
+      username:  litemall
+      password:  litemall123456
+      initial-size:  10
+      max-active:  50
+      min-idle:  10
+      max-wait:  60000
+      pool-prepared-statements:  true
+      max-pool-prepared-statement-per-connection-size:  20
+      validation-query:  SELECT 1 FROM DUAL
+      test-on-borrow:  false
+      test-on-return:  false
+      test-while-idle:  true
+      time-between-eviction-runs-millis:  60000
+      filters:  stat,wall
+
+server:
+  port: 18080
+
+logging:
+  config: classpath:logback-spring.xml
+
+pagehelper:
+  helperDialect:  mysql
+  reasonable: true
+  supportMethodsArguments:  true
+  params: count=countSql
+
+litemall:
+  # 开发者应该设置成自己的wx相关信息
+  wx:
+    app-id: wxa5b486c6b918ecfb
+    app-secret: e04004829d4c383b4db7769d88dfbca1
+    mch-id: 111111
+    mch-key: xxxxxx
+    notify-url: http://122.51.199.160:8080/wx/order/pay-notify
+    # 商户证书文件路径
+    # 请参考“商户证书”一节 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3
+    key-path: xxxxx
+
+  #通知相关配置
+  notify:
+    mail:
+      # 邮件通知配置,邮箱一般用于接收业务通知例如收到新的订单,sendto 定义邮件接收者,通常为商城运营人员
+      enable: false
+      host: smtp.exmail.qq.com
+      username: ex@ex.com.cn
+      password: XXXXXXXXXXXXX
+      sendfrom: ex@ex.com.cn
+      sendto: ex@qq.com
+      port: 465
+
+    # 短消息模版通知配置
+    # 短信息用于通知客户,例如发货短信通知,注意配置格式;template-name,template-templateId 请参考 NotifyType 枚举值
+    sms:
+      enable: false
+      # 如果是腾讯云短信,则设置active的值tencent
+      # 如果是阿里云短信,则设置active的值aliyun
+      active: tencent
+      sign: litemall
+      template:
+        - name: paySucceed
+          templateId: 156349
+        - name: captcha
+          templateId: 156433
+        - name: ship
+          templateId: 158002
+        - name: refund
+          templateId: 159447
+      tencent:
+        appid: 111111111
+        appkey: xxxxxxxxxxxxxx
+      aliyun:
+        regionId: xxx
+        accessKeyId: xxx
+        accessKeySecret: xxx
+
+  # 快鸟物流查询配置
+  express:
+    enable: false
+    appId: "XXXXXXXXX"
+    appKey: "XXXXXXXXXXXXXXXXXXXXXXXXX"
+    vendors:
+      - code: "ZTO"
+        name: "中通快递"
+      - code: "YTO"
+        name: "圆通速递"
+      - code: "YD"
+        name: "韵达速递"
+      - code: "YZPY"
+        name: "邮政快递包裹"
+      - code: "EMS"
+        name: "EMS"
+      - code: "DBL"
+        name: "德邦快递"
+      - code: "FAST"
+        name: "快捷快递"
+      - code: "ZJS"
+        name: "宅急送"
+      - code: "TNT"
+        name: "TNT快递"
+      - code: "UPS"
+        name: "UPS"
+      - code: "DHL"
+        name: "DHL"
+      - code: "FEDEX"
+        name: "FEDEX联邦(国内件)"
+      - code: "FEDEX_GJ"
+        name: "FEDEX联邦(国际件)"
+
+  # 对象存储配置
+  storage:
+    # 当前工作的对象存储模式,分别是local、aliyun、tencent
+    active: local
+    # 本地对象存储配置信息
+    local:
+      storagePath: storage
+      address: http://122.51.199.160:8080/wx/storage/fetch/
+    # 阿里云对象存储配置信息
+    aliyun:
+      endpoint: oss-cn-shenzhen.aliyuncs.com
+      accessKeyId: 111111
+      accessKeySecret: xxxxxx
+      bucketName: xxxxxx
+    # 腾讯对象存储配置信息
+    # 请参考 https://cloud.tencent.com/document/product/436/6249
+    tencent:
+      secretId: 111111
+      secretKey: xxxxxx
+      region: xxxxxx
+      bucketName: xxxxxx
+    # 七牛云对象存储配置信息
+    qiniu:
+      endpoint: http://pd5cb6ulu.bkt.clouddn.com
+      accessKey: 111111
+      secretKey: xxxxxx
+      bucketName: litemall
+
+swagger:
+  production: true

+ 52 - 0
litemall-master/deploy/util/lazy.sh

@@ -0,0 +1,52 @@
+#!/bin/bash
+
+# 本脚本的作用是
+# 1. 项目打包
+# 2. 上传云服务器
+# 3. 远程登录云服务器并执行reset脚本
+
+# 请设置云服务器的IP地址和账户
+# 例如 ubuntu@122.51.199.160
+REMOTE=
+# 请设置本地SSH私钥文件id_rsa路径
+# 例如 /home/litemall/id_rsa
+ID_RSA=
+
+if test -z "$REMOTE"
+then
+  echo "请设置云服务器登录IP地址和账户"
+  exit 1
+fi
+
+if test -z "$ID_RSA"
+then
+  echo "请设置云服务器登录IP地址和账户"
+  exit 1
+fi
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+cd $DIR/../.. || exit 2
+LITEMALL_HOME=$PWD
+echo "LITEMALL_HOME $LITEMALL_HOME"
+
+# 项目打包
+cd $LITEMALL_HOME || exit 2
+./deploy/util/package.sh
+
+# 上传云服务器
+cd $LITEMALL_HOME || exit 2
+scp -i $ID_RSA -r  ./deploy $REMOTE:/home/ubuntu/
+
+# 远程登录云服务器并执行reset脚本
+# 这里使用tr命令,因为有可能deploy.sh和reset.sh的换行格式是CRLF,而LINUX环境应该是LF
+ssh $REMOTE -i $ID_RSA << eeooff
+cd /home/ubuntu/deploy/bin
+cat deploy.sh | tr -d '\r' > deploy2.sh
+mv deploy2.sh deploy.sh
+chmod +x deploy.sh
+cat reset.sh | tr -d '\r' > reset2.sh
+mv reset2.sh reset.sh
+chmod +x reset.sh
+sudo ./reset.sh
+exit
+eeooff

+ 34 - 0
litemall-master/deploy/util/package.sh

@@ -0,0 +1,34 @@
+#!/bin/bash
+
+# 请注意
+# 本脚本的作用是把本项目编译的结果保存到deploy文件夹中
+# 1. 把项目数据库文件拷贝到deploy/db
+# 2. 编译litemall-admin
+# 3. 编译litemall-all模块,然后拷贝到deploy/litemall
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+cd $DIR/../..
+LITEMALL_HOME=$PWD
+echo "LITEMALL_HOME $LITEMALL_HOME"
+
+# 复制数据库
+cat $LITEMALL_HOME/litemall-db/sql/litemall_schema.sql > $LITEMALL_HOME/deploy/db/litemall.sql
+cat $LITEMALL_HOME/litemall-db/sql/litemall_table.sql >> $LITEMALL_HOME/deploy/db/litemall.sql
+cat $LITEMALL_HOME/litemall-db/sql/litemall_data.sql >> $LITEMALL_HOME/deploy/db/litemall.sql
+
+# 安装阿里node镜像工具
+npm install -g cnpm --registry=https://registry.npm.taobao.org
+
+# 打包litemall-admin
+cd $LITEMALL_HOME/litemall-admin
+cnpm install
+cnpm run build:dep
+
+# 打包litemall-vue
+cd $LITEMALL_HOME/litemall-vue
+cnpm install
+cnpm run build:dep
+
+cd $LITEMALL_HOME
+mvn clean package
+cp -f $LITEMALL_HOME/litemall-all/target/litemall-all-*-exec.jar $LITEMALL_HOME/deploy/litemall/litemall.jar

+ 3 - 0
litemall-master/docker/.gitignore

@@ -0,0 +1,3 @@
+
+/litemall/litemall.jar
+/db/init-sql/litemall.sql

+ 0 - 0
litemall-master/docker/README.md


+ 6 - 0
litemall-master/docker/db/conf.d/my.cnf

@@ -0,0 +1,6 @@
+[mysqld]
+wait_timeout=1814400
+max_allowed_packet = 100M
+default-time_zone = '+8:00'
+character-set-server=utf8mb4
+collation-server=utf8mb4_unicode_ci

+ 0 - 0
litemall-master/docker/db/data/.gitkeep


+ 0 - 0
litemall-master/docker/db/init-sql/.gitkeep


+ 35 - 0
litemall-master/docker/docker-compose.yml

@@ -0,0 +1,35 @@
+version: '3'
+services:
+  mysql57:
+    image: mysql:5.7
+    container_name: mysql
+    ports:
+      - "3306:3306"
+    command:
+      --character-set-server=utf8
+      --collation-server=utf8_general_ci
+      --default-authentication-plugin=mysql_native_password
+    volumes:
+      - ./db/conf.d:/etc/mysql/conf.d
+      - ./db/data:/var/lib/mysql
+      - ./db/init-sql:/docker-entrypoint-initdb.d
+    environment:
+      MYSQL_ROOT_PASSWORD: litemall123456
+    restart: always
+  litemall:
+    build:
+      context: litemall
+      dockerfile: Dockerfile
+    container_name: litemall
+    ports:
+      - "8080:8080"
+    volumes:
+      - ./litemall/storage:/storage
+      - ./litemall/logs:/logs
+      - ./litemall/backup:/backup
+      - /etc/localtime:/etc/localtime
+    environment:
+      - TZ=Asia/Shanghai
+    depends_on:
+      - mysql57
+    restart: always

+ 0 - 0
litemall-master/docker/litemall/.gitkeep


+ 4 - 0
litemall-master/docker/litemall/Dockerfile

@@ -0,0 +1,4 @@
+FROM openjdk:8-jre
+COPY application.yml application.yml
+COPY litemall.jar litemall.jar
+ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","litemall.jar"]

+ 148 - 0
litemall-master/docker/litemall/application.yml

@@ -0,0 +1,148 @@
+spring:
+  profiles:
+    active: none
+  message:
+    encoding: UTF-8
+  datasource:
+    druid:
+      url:  jdbc:mysql://mysql:3306/litemall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true&verifyServerCertificate=false&useSSL=true
+      driver-class-name:  com.mysql.jdbc.Driver
+      username:  litemall
+      password:  litemall123456
+      initial-size:  10
+      max-active:  50
+      min-idle:  10
+      max-wait:  60000
+      pool-prepared-statements:  true
+      max-pool-prepared-statement-per-connection-size:  20
+      validation-query:  SELECT 1 FROM DUAL
+      test-on-borrow:  false
+      test-on-return:  false
+      test-while-idle:  true
+      time-between-eviction-runs-millis:  60000
+      filters:  stat,wall
+
+server:
+  port: 18080
+
+logging:
+  config: classpath:logback-spring.xml
+
+pagehelper:
+  helperDialect:  mysql
+  reasonable: true
+  supportMethodsArguments:  true
+  params: count=countSql
+
+litemall:
+  # 开发者应该设置成自己的wx相关信息
+  wx:
+    app-id: wxa5b486c6b918ecfb
+    app-secret: e04004829d4c383b4db7769d88dfbca1
+    mch-id: 111111
+    mch-key: xxxxxx
+    notify-url: http://122.51.199.160:8080/wx/order/pay-notify
+    # 商户证书文件路径
+    # 请参考“商户证书”一节 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3
+    key-path: xxxxx
+
+  #通知相关配置
+  notify:
+    mail:
+      # 邮件通知配置,邮箱一般用于接收业务通知例如收到新的订单,sendto 定义邮件接收者,通常为商城运营人员
+      enable: false
+      host: smtp.exmail.qq.com
+      username: ex@ex.com.cn
+      password: XXXXXXXXXXXXX
+      sendfrom: ex@ex.com.cn
+      sendto: ex@qq.com
+      port: 465
+
+    # 短消息模版通知配置
+    # 短信息用于通知客户,例如发货短信通知,注意配置格式;template-name,template-templateId 请参考 NotifyType 枚举值
+    sms:
+      enable: false
+      # 如果是腾讯云短信,则设置active的值tencent
+      # 如果是阿里云短信,则设置active的值aliyun
+      active: tencent
+      sign: litemall
+      template:
+        - name: paySucceed
+          templateId: 156349
+        - name: captcha
+          templateId: 156433
+        - name: ship
+          templateId: 158002
+        - name: refund
+          templateId: 159447
+      tencent:
+        appid: 111111111
+        appkey: xxxxxxxxxxxxxx
+      aliyun:
+        regionId: xxx
+        accessKeyId: xxx
+        accessKeySecret: xxx
+
+  # 快鸟物流查询配置
+  express:
+    enable: false
+    appId: "XXXXXXXXX"
+    appKey: "XXXXXXXXXXXXXXXXXXXXXXXXX"
+    vendors:
+      - code: "ZTO"
+        name: "中通快递"
+      - code: "YTO"
+        name: "圆通速递"
+      - code: "YD"
+        name: "韵达速递"
+      - code: "YZPY"
+        name: "邮政快递包裹"
+      - code: "EMS"
+        name: "EMS"
+      - code: "DBL"
+        name: "德邦快递"
+      - code: "FAST"
+        name: "快捷快递"
+      - code: "ZJS"
+        name: "宅急送"
+      - code: "TNT"
+        name: "TNT快递"
+      - code: "UPS"
+        name: "UPS"
+      - code: "DHL"
+        name: "DHL"
+      - code: "FEDEX"
+        name: "FEDEX联邦(国内件)"
+      - code: "FEDEX_GJ"
+        name: "FEDEX联邦(国际件)"
+
+  # 对象存储配置
+  storage:
+    # 当前工作的对象存储模式,分别是local、aliyun、tencent
+    active: local
+    # 本地对象存储配置信息
+    local:
+      storagePath: storage
+      address: http://122.51.199.160:8080/wx/storage/fetch/
+    # 阿里云对象存储配置信息
+    aliyun:
+      endpoint: oss-cn-shenzhen.aliyuncs.com
+      accessKeyId: 111111
+      accessKeySecret: xxxxxx
+      bucketName: xxxxxx
+    # 腾讯对象存储配置信息
+    # 请参考 https://cloud.tencent.com/document/product/436/6249
+    tencent:
+      secretId: 111111
+      secretKey: xxxxxx
+      region: xxxxxx
+      bucketName: xxxxxx
+    # 七牛云对象存储配置信息
+    qiniu:
+      endpoint: http://pd5cb6ulu.bkt.clouddn.com
+      accessKey: 111111
+      secretKey: xxxxxx
+      bucketName: litemall
+
+swagger:
+  production: true

+ 52 - 0
litemall-master/docker/util/lazy.sh

@@ -0,0 +1,52 @@
+#!/bin/bash
+
+# 本脚本的作用是
+# 1. 项目打包
+# 2. 上传云服务器
+# 3. 远程登录云服务器并执行reset脚本
+
+# 请设置云服务器的IP地址和账户
+# 例如 ubuntu@122.51.199.160
+REMOTE=ubuntu@122.51.199.160
+# 请设置本地SSH私钥文件id_rsa路径
+# 例如 /home/litemall/id_rsa
+ID_RSA=/d/00/cloud/litemall.txt
+
+if test -z "$REMOTE"
+then
+  echo "请设置云服务器登录IP地址和账户"
+  exit 1
+fi
+
+if test -z "$ID_RSA"
+then
+  echo "请设置云服务器登录IP地址和账户"
+  exit 1
+fi
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+cd $DIR/../.. || exit 2
+LITEMALL_HOME=$PWD
+echo "LITEMALL_HOME $LITEMALL_HOME"
+
+# 项目打包
+cd $LITEMALL_HOME || exit 2
+./docker/util/package.sh
+
+# 上传云服务器
+cd $LITEMALL_HOME || exit 2
+scp -i $ID_RSA -r  ./docker $REMOTE:/home/ubuntu/
+
+# 远程登录云服务器并执行reset脚本
+# 这里使用tr命令,因为有可能deploy.sh和reset.sh的换行格式是CRLF,而LINUX环境应该是LF
+ssh $REMOTE -i $ID_RSA << eeooff
+cd /home/ubuntu/docker/bin
+cat deploy.sh | tr -d '\r' > deploy2.sh
+mv deploy2.sh deploy.sh
+chmod +x deploy.sh
+cat reset.sh | tr -d '\r' > reset2.sh
+mv reset2.sh reset.sh
+chmod +x reset.sh
+sudo ./reset.sh
+exit
+eeooff

+ 34 - 0
litemall-master/docker/util/package.sh

@@ -0,0 +1,34 @@
+#!/bin/bash
+
+# 请注意
+# 本脚本的作用是把本项目编译的结果保存到deploy文件夹中
+# 1. 把项目数据库文件拷贝到docker/db/init-sql
+# 2. 编译litemall-admin
+# 3. 编译litemall-all模块,然后拷贝到docker/litemall
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+cd $DIR/../..
+LITEMALL_HOME=$PWD
+echo "LITEMALL_HOME $LITEMALL_HOME"
+
+# 复制数据库
+cat $LITEMALL_HOME/litemall-db/sql/litemall_schema.sql > $LITEMALL_HOME/docker/db/init-sql/litemall.sql
+cat $LITEMALL_HOME/litemall-db/sql/litemall_table.sql >> $LITEMALL_HOME/docker/db/init-sql/litemall.sql
+cat $LITEMALL_HOME/litemall-db/sql/litemall_data.sql >> $LITEMALL_HOME/docker/db/init-sql/litemall.sql
+
+# 安装阿里node镜像工具
+npm install -g cnpm --registry=https://registry.npm.taobao.org
+
+# 打包litemall-admin
+cd $LITEMALL_HOME/litemall-admin
+cnpm install
+cnpm run build:dep
+
+# 打包litemall-vue
+cd $LITEMALL_HOME/litemall-vue
+cnpm install
+cnpm run build:dep
+
+cd $LITEMALL_HOME
+mvn clean package
+cp -f $LITEMALL_HOME/litemall-all/target/litemall-all-*-exec.jar $LITEMALL_HOME/docker/litemall/litemall.jar

+ 3 - 0
litemall-master/litemall-admin-api/.gitignore

@@ -0,0 +1,3 @@
+
+/target/
+/litemall-admin-api.iml

+ 67 - 0
litemall-master/litemall-admin-api/pom.xml

@@ -0,0 +1,67 @@
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>litemall-admin-api</artifactId>
+    <packaging>jar</packaging>
+
+    <parent>
+        <groupId>org.linlinjava</groupId>
+        <artifactId>litemall</artifactId>
+        <version>0.1.0</version>
+    </parent>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    </properties>
+    <dependencies>
+
+        <dependency>
+            <groupId>org.linlinjava</groupId>
+            <artifactId>litemall-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.linlinjava</groupId>
+            <artifactId>litemall-db</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.binarywang</groupId>
+            <artifactId>weixin-java-miniapp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-spring-boot-web-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>swagger-bootstrap-ui</artifactId>
+            <version>1.9.6</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>repackage</id>
+                        <configuration>
+                            <executable>true</executable>
+                            <classifier>exec</classifier>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 20 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/Application.java

@@ -0,0 +1,20 @@
+package org.linlinjava.litemall.admin;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+@SpringBootApplication(scanBasePackages = {"org.linlinjava.litemall.db", "org.linlinjava.litemall.core",
+        "org.linlinjava.litemall.admin"})
+@MapperScan("org.linlinjava.litemall.db.dao")
+@EnableTransactionManagement
+@EnableScheduling
+public class Application {
+
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+
+}

+ 14 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/annotation/RequiresPermissionsDesc.java

@@ -0,0 +1,14 @@
+package org.linlinjava.litemall.admin.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RequiresPermissionsDesc {
+    String[] menu();
+
+    String button();
+}

+ 48 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/AdminSwagger2Configuration.java

@@ -0,0 +1,48 @@
+package org.linlinjava.litemall.admin.config;
+
+import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+/**
+ * swagger在线文档配置<br>
+ * 项目启动后可通过地址:http://host:ip/swagger-ui.html 查看在线文档
+ *
+ * @author enilu
+ * @version 2018-07-24
+ */
+
+@Configuration
+@EnableSwagger2
+@EnableSwaggerBootstrapUI
+public class AdminSwagger2Configuration {
+    @Bean
+    public Docket adminDocket() {
+
+        return new Docket(DocumentationType.SWAGGER_2)
+                .groupName("admin")
+                .apiInfo(adminApiInfo())
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("org.linlinjava.litemall.admin.web"))
+                .paths(PathSelectors.any())
+                .build();
+    }
+
+    private ApiInfo adminApiInfo() {
+        return new ApiInfoBuilder()
+                .title("litemall-admin API")
+                .description("litemall管理后台API")
+                .termsOfServiceUrl("https://github.com/linlinjava/litemall")
+                .contact("https://github.com/linlinjava/litemall")
+                .version("1.0")
+                .build();
+    }
+}

+ 75 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/ShiroConfig.java

@@ -0,0 +1,75 @@
+package org.linlinjava.litemall.admin.config;
+
+import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.realm.Realm;
+import org.apache.shiro.session.mgt.SessionManager;
+import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
+import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
+import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
+import org.linlinjava.litemall.admin.shiro.AdminAuthorizingRealm;
+import org.linlinjava.litemall.admin.shiro.AdminWebSessionManager;
+import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.DependsOn;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+@Configuration
+public class ShiroConfig {
+
+    @Bean
+    public Realm realm() {
+        return new AdminAuthorizingRealm();
+    }
+
+    @Bean
+    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
+        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
+        shiroFilterFactoryBean.setSecurityManager(securityManager);
+        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
+        filterChainDefinitionMap.put("/admin/auth/login", "anon");
+        filterChainDefinitionMap.put("/admin/auth/401", "anon");
+        filterChainDefinitionMap.put("/admin/auth/index", "anon");
+        filterChainDefinitionMap.put("/admin/auth/403", "anon");
+        filterChainDefinitionMap.put("/admin/index/*", "anon");
+
+        filterChainDefinitionMap.put("/admin/**", "authc");
+        shiroFilterFactoryBean.setLoginUrl("/admin/auth/401");
+        shiroFilterFactoryBean.setSuccessUrl("/admin/auth/index");
+        shiroFilterFactoryBean.setUnauthorizedUrl("/admin/auth/403");
+        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
+        return shiroFilterFactoryBean;
+    }
+
+    @Bean
+    public SessionManager sessionManager() {
+
+        return new AdminWebSessionManager();
+    }
+
+    @Bean
+    public DefaultWebSecurityManager defaultWebSecurityManager() {
+        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
+        securityManager.setRealm(realm());
+        securityManager.setSessionManager(sessionManager());
+        return securityManager;
+    }
+
+    @Bean
+    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
+        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
+                new AuthorizationAttributeSourceAdvisor();
+        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
+        return authorizationAttributeSourceAdvisor;
+    }
+
+    @Bean
+    @DependsOn("lifecycleBeanPostProcessor")
+    public static DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
+        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
+        creator.setProxyTargetClass(true);
+        return creator;
+    }
+}

+ 34 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/ShiroExceptionHandler.java

@@ -0,0 +1,34 @@
+package org.linlinjava.litemall.admin.config;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authz.AuthorizationException;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@ControllerAdvice
+@Order(value = Ordered.HIGHEST_PRECEDENCE)
+public class ShiroExceptionHandler {
+
+    private final Log logger = LogFactory.getLog(ShiroExceptionHandler.class);
+
+    @ExceptionHandler(AuthenticationException.class)
+    @ResponseBody
+    public Object unauthenticatedHandler(AuthenticationException e) {
+        logger.warn(e.getMessage(), e);
+        return ResponseUtil.unlogin();
+    }
+
+    @ExceptionHandler(AuthorizationException.class)
+    @ResponseBody
+    public Object unauthorizedHandler(AuthorizationException e) {
+        logger.warn(e.getMessage(), e);
+        return ResponseUtil.unauthz();
+    }
+
+}

+ 46 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/dto/GoodsAllinone.java

@@ -0,0 +1,46 @@
+package org.linlinjava.litemall.admin.dto;
+
+import org.linlinjava.litemall.db.domain.LitemallGoods;
+import org.linlinjava.litemall.db.domain.LitemallGoodsAttribute;
+import org.linlinjava.litemall.db.domain.LitemallGoodsProduct;
+import org.linlinjava.litemall.db.domain.LitemallGoodsSpecification;
+
+public class GoodsAllinone {
+    LitemallGoods goods;
+    LitemallGoodsSpecification[] specifications;
+    LitemallGoodsAttribute[] attributes;
+    LitemallGoodsProduct[] products;
+
+    public LitemallGoods getGoods() {
+        return goods;
+    }
+
+    public void setGoods(LitemallGoods goods) {
+        this.goods = goods;
+    }
+
+    public LitemallGoodsProduct[] getProducts() {
+        return products;
+    }
+
+    public void setProducts(LitemallGoodsProduct[] products) {
+        this.products = products;
+    }
+
+    public LitemallGoodsSpecification[] getSpecifications() {
+        return specifications;
+    }
+
+    public void setSpecifications(LitemallGoodsSpecification[] specifications) {
+        this.specifications = specifications;
+    }
+
+    public LitemallGoodsAttribute[] getAttributes() {
+        return attributes;
+    }
+
+    public void setAttributes(LitemallGoodsAttribute[] attributes) {
+        this.attributes = attributes;
+    }
+
+}

+ 53 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/job/CouponJob.java

@@ -0,0 +1,53 @@
+package org.linlinjava.litemall.admin.job;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.linlinjava.litemall.db.domain.LitemallCoupon;
+import org.linlinjava.litemall.db.domain.LitemallCouponUser;
+import org.linlinjava.litemall.db.service.LitemallCouponService;
+import org.linlinjava.litemall.db.service.LitemallCouponUserService;
+import org.linlinjava.litemall.db.util.CouponConstant;
+import org.linlinjava.litemall.db.util.CouponUserConstant;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 检测优惠券过期情况
+ */
+@Component
+public class CouponJob {
+    private final Log logger = LogFactory.getLog(CouponJob.class);
+
+    @Autowired
+    private LitemallCouponService couponService;
+    @Autowired
+    private LitemallCouponUserService couponUserService;
+
+    /**
+     * 每隔一个小时检查
+     * TODO
+     * 注意,因为是相隔一个小时检查,因此导致优惠券真正超时时间可能比设定时间延迟1个小时
+     */
+//    @Scheduled(fixedDelay = 60 * 60 * 1000)
+    public void checkCouponExpired() {
+        logger.info("系统开启任务检查优惠券是否已经过期");
+
+        List<LitemallCoupon> couponList = couponService.queryExpired();
+        for (LitemallCoupon coupon : couponList) {
+            coupon.setStatus(CouponConstant.STATUS_EXPIRED);
+            couponService.updateById(coupon);
+        }
+
+        List<LitemallCouponUser> couponUserList = couponUserService.queryExpired();
+        for (LitemallCouponUser couponUser : couponUserList) {
+            couponUser.setStatus(CouponUserConstant.STATUS_EXPIRED);
+            couponUserService.update(couponUser);
+        }
+
+        logger.info("系统结束任务检查优惠券是否已经过期");
+    }
+
+}

+ 56 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/job/DbJob.java

@@ -0,0 +1,56 @@
+package org.linlinjava.litemall.admin.job;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.linlinjava.litemall.db.util.DbUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.LocalDate;
+
+/**
+ * 数据库定时备份任务
+ * 在backup文件夹中备份最近七日的数据库文件
+ */
+@Component
+public class DbJob {
+    private final Log logger = LogFactory.getLog(DbJob.class);
+
+    @Autowired
+    private Environment environment;
+
+    /*
+     * 定时时间是每天凌晨5点。
+     */
+//    @Scheduled(cron = "0 0 5 * * ?")
+    public void backup() throws IOException {
+        logger.info("系统开启定时任务数据库备份");
+
+        String user = environment.getProperty("spring.datasource.druid.username");
+        String password = environment.getProperty("spring.datasource.druid.password");
+        String url = environment.getProperty("spring.datasource.druid.url");
+        int index1 = url.indexOf("3306/");
+        int index2 = url.indexOf("?");
+        String db = url.substring(index1+5, index2);
+
+        LocalDate localDate = LocalDate.now();
+        String fileName = localDate.toString();
+        File file = new File("backup", fileName);
+        file.getParentFile().mkdirs();
+        file.createNewFile();
+
+        // 备份今天数据库
+        DbUtil.backup(file, user, password, db);
+        // 删除七天前数据库备份文件
+        LocalDate before = localDate.minusDays(7);
+        File fileBefore = new File("backup", fileName);
+        fileBefore.deleteOnExit();
+
+        logger.info("系统结束定时任务数据库备份");
+    }
+
+}

+ 91 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/job/OrderJob.java

@@ -0,0 +1,91 @@
+package org.linlinjava.litemall.admin.job;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.linlinjava.litemall.core.system.SystemConfig;
+import org.linlinjava.litemall.db.domain.*;
+import org.linlinjava.litemall.db.service.*;
+import org.linlinjava.litemall.db.util.OrderUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 检测订单状态
+ */
+@Component
+public class OrderJob {
+    private final Log logger = LogFactory.getLog(OrderJob.class);
+
+    @Autowired
+    private LitemallOrderGoodsService orderGoodsService;
+    @Autowired
+    private LitemallOrderService orderService;
+    @Autowired
+    private LitemallGoodsProductService productService;
+    @Autowired
+    private LitemallGrouponService grouponService;
+    @Autowired
+    private LitemallGrouponRulesService rulesService;
+
+    /**
+     * 自动确认订单
+     * <p>
+     * 定时检查订单未确认情况,如果超时 LITEMALL_ORDER_UNCONFIRM 天则自动确认订单
+     * 定时时间是每天凌晨3点。
+     * <p>
+     * TODO
+     * 注意,因为是相隔一天检查,因此导致订单真正超时时间是 [LITEMALL_ORDER_UNCONFIRM, 1 + LITEMALL_ORDER_UNCONFIRM]
+     */
+//    @Scheduled(cron = "0 0 3 * * ?")
+    public void checkOrderUnconfirm() {
+        logger.info("系统开启定时任务检查订单是否已经超期自动确认收货");
+
+        List<LitemallOrder> orderList = orderService.queryUnconfirm(SystemConfig.getOrderUnconfirm());
+        for (LitemallOrder order : orderList) {
+
+            // 设置订单已取消状态
+            order.setOrderStatus(OrderUtil.STATUS_AUTO_CONFIRM);
+            order.setConfirmTime(LocalDateTime.now());
+            if (orderService.updateWithOptimisticLocker(order) == 0) {
+                logger.info("订单 ID=" + order.getId() + " 数据已经更新,放弃自动确认收货");
+            } else {
+                logger.info("订单 ID=" + order.getId() + " 已经超期自动确认收货");
+            }
+        }
+
+        logger.info("系统结束定时任务检查订单是否已经超期自动确认收货");
+    }
+
+    /**
+     * 可评价订单商品超期
+     * <p>
+     * 定时检查订单商品评价情况,如果确认商品超时 LITEMALL_ORDER_COMMENT 天则取消可评价状态
+     * 定时时间是每天凌晨4点。
+     * <p>
+     * TODO
+     * 注意,因为是相隔一天检查,因此导致订单真正超时时间是 [LITEMALL_ORDER_COMMENT, 1 + LITEMALL_ORDER_COMMENT]
+     */
+//    @Scheduled(cron = "0 0 4 * * ?")
+    public void checkOrderComment() {
+        logger.info("系统开启任务检查订单是否已经超期未评价");
+
+        List<LitemallOrder> orderList = orderService.queryComment(SystemConfig.getOrderComment());
+        for (LitemallOrder order : orderList) {
+            order.setComments((short) 0);
+            orderService.updateWithOptimisticLocker(order);
+
+            List<LitemallOrderGoods> orderGoodsList = orderGoodsService.queryByOid(order.getId());
+            for (LitemallOrderGoods orderGoods : orderGoodsList) {
+                orderGoods.setComment(-1);
+                orderGoodsService.updateById(orderGoods);
+            }
+        }
+
+        logger.info("系统结束任务检查订单是否已经超期未评价");
+    }
+}

+ 355 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminGoodsService.java

@@ -0,0 +1,355 @@
+package org.linlinjava.litemall.admin.service;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.linlinjava.litemall.admin.dto.GoodsAllinone;
+import org.linlinjava.litemall.admin.vo.CatVo;
+import org.linlinjava.litemall.core.qcode.QCodeService;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.db.domain.*;
+import org.linlinjava.litemall.db.service.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.linlinjava.litemall.admin.util.AdminResponseCode.GOODS_NAME_EXIST;
+
+@Service
+public class AdminGoodsService {
+    private final Log logger = LogFactory.getLog(AdminGoodsService.class);
+
+    @Autowired
+    private LitemallGoodsService goodsService;
+    @Autowired
+    private LitemallGoodsSpecificationService specificationService;
+    @Autowired
+    private LitemallGoodsAttributeService attributeService;
+    @Autowired
+    private LitemallGoodsProductService productService;
+    @Autowired
+    private LitemallCategoryService categoryService;
+    @Autowired
+    private LitemallBrandService brandService;
+    @Autowired
+    private LitemallCartService cartService;
+    @Autowired
+    private QCodeService qCodeService;
+
+    public Object list(Integer goodsId, String goodsSn, String name,
+                       Integer page, Integer limit, String sort, String order) {
+        List<LitemallGoods> goodsList = goodsService.querySelective(goodsId, goodsSn, name, page, limit, sort, order);
+        return ResponseUtil.okList(goodsList);
+    }
+
+    private Object validate(GoodsAllinone goodsAllinone) {
+        LitemallGoods goods = goodsAllinone.getGoods();
+        String name = goods.getName();
+        if (StringUtils.isEmpty(name)) {
+            return ResponseUtil.badArgument();
+        }
+        String goodsSn = goods.getGoodsSn();
+        if (StringUtils.isEmpty(goodsSn)) {
+            return ResponseUtil.badArgument();
+        }
+        // 品牌商可以不设置,如果设置则需要验证品牌商存在
+        Integer brandId = goods.getBrandId();
+        if (brandId != null && brandId != 0) {
+            if (brandService.findById(brandId) == null) {
+                return ResponseUtil.badArgumentValue();
+            }
+        }
+        // 分类可以不设置,如果设置则需要验证分类存在
+        Integer categoryId = goods.getCategoryId();
+        if (categoryId != null && categoryId != 0) {
+            if (categoryService.findById(categoryId) == null) {
+                return ResponseUtil.badArgumentValue();
+            }
+        }
+
+        LitemallGoodsAttribute[] attributes = goodsAllinone.getAttributes();
+        for (LitemallGoodsAttribute attribute : attributes) {
+            String attr = attribute.getAttribute();
+            if (StringUtils.isEmpty(attr)) {
+                return ResponseUtil.badArgument();
+            }
+            String value = attribute.getValue();
+            if (StringUtils.isEmpty(value)) {
+                return ResponseUtil.badArgument();
+            }
+        }
+
+        LitemallGoodsSpecification[] specifications = goodsAllinone.getSpecifications();
+        for (LitemallGoodsSpecification specification : specifications) {
+            String spec = specification.getSpecification();
+            if (StringUtils.isEmpty(spec)) {
+                return ResponseUtil.badArgument();
+            }
+            String value = specification.getValue();
+            if (StringUtils.isEmpty(value)) {
+                return ResponseUtil.badArgument();
+            }
+        }
+
+        LitemallGoodsProduct[] products = goodsAllinone.getProducts();
+        for (LitemallGoodsProduct product : products) {
+            Integer number = product.getNumber();
+            if (number == null || number < 0) {
+                return ResponseUtil.badArgument();
+            }
+
+            BigDecimal price = product.getPrice();
+            if (price == null) {
+                return ResponseUtil.badArgument();
+            }
+
+            String[] productSpecifications = product.getSpecifications();
+            if (productSpecifications.length == 0) {
+                return ResponseUtil.badArgument();
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * 编辑商品
+     *
+     * NOTE:
+     * 由于商品涉及到四个表,特别是litemall_goods_product表依赖litemall_goods_specification表,
+     * 这导致允许所有字段都是可编辑会带来一些问题,因此这里商品编辑功能是受限制:
+     * (1)litemall_goods表可以编辑字段;
+     * (2)litemall_goods_specification表只能编辑pic_url字段,其他操作不支持;
+     * (3)litemall_goods_product表只能编辑price, number和url字段,其他操作不支持;
+     * (4)litemall_goods_attribute表支持编辑、添加和删除操作。
+     *
+     * NOTE2:
+     * 前后端这里使用了一个小技巧:
+     * 如果前端传来的update_time字段是空,则说明前端已经更新了某个记录,则这个记录会更新;
+     * 否则说明这个记录没有编辑过,无需更新该记录。
+     *
+     * NOTE3:
+     * (1)购物车缓存了一些商品信息,因此需要及时更新。
+     * 目前这些字段是goods_sn, goods_name, price, pic_url。
+     * (2)但是订单里面的商品信息则是不会更新。
+     * 如果订单是未支付订单,此时仍然以旧的价格支付。
+     */
+    @Transactional
+    public Object update(GoodsAllinone goodsAllinone) {
+        Object error = validate(goodsAllinone);
+        if (error != null) {
+            return error;
+        }
+
+        LitemallGoods goods = goodsAllinone.getGoods();
+        LitemallGoodsAttribute[] attributes = goodsAllinone.getAttributes();
+        LitemallGoodsSpecification[] specifications = goodsAllinone.getSpecifications();
+        LitemallGoodsProduct[] products = goodsAllinone.getProducts();
+
+        //将生成的分享图片地址写入数据库
+        String url = qCodeService.createGoodShareImage(goods.getId().toString(), goods.getPicUrl(), goods.getName());
+        goods.setShareUrl(url);
+
+        // 商品表里面有一个字段retailPrice记录当前商品的最低价
+        BigDecimal retailPrice = new BigDecimal(Integer.MAX_VALUE);
+        for (LitemallGoodsProduct product : products) {
+            BigDecimal productPrice = product.getPrice();
+            if(retailPrice.compareTo(productPrice) == 1){
+                retailPrice = productPrice;
+            }
+        }
+        goods.setRetailPrice(retailPrice);
+        
+        // 商品基本信息表litemall_goods
+        if (goodsService.updateById(goods) == 0) {
+            throw new RuntimeException("更新数据失败");
+        }
+
+        Integer gid = goods.getId();
+
+        // 商品规格表litemall_goods_specification
+        for (LitemallGoodsSpecification specification : specifications) {
+            // 目前只支持更新规格表的图片字段
+            if(specification.getUpdateTime() == null){
+                specification.setSpecification(null);
+                specification.setValue(null);
+                specificationService.updateById(specification);
+            }
+        }
+
+        // 商品货品表litemall_product
+        for (LitemallGoodsProduct product : products) {
+            if(product.getUpdateTime() == null) {
+                productService.updateById(product);
+            }
+        }
+
+        // 商品参数表litemall_goods_attribute
+        for (LitemallGoodsAttribute attribute : attributes) {
+            if (attribute.getId() == null || attribute.getId().equals(0)){
+                attribute.setGoodsId(goods.getId());
+                attributeService.add(attribute);
+            }
+            else if(attribute.getDeleted()){
+                attributeService.deleteById(attribute.getId());
+            }
+            else if(attribute.getUpdateTime() == null){
+                attributeService.updateById(attribute);
+            }
+        }
+
+        // 这里需要注意的是购物车litemall_cart有些字段是拷贝商品的一些字段,因此需要及时更新
+        // 目前这些字段是goods_sn, goods_name, price, pic_url
+        for (LitemallGoodsProduct product : products) {
+            cartService.updateProduct(product.getId(), goods.getGoodsSn(), goods.getName(), product.getPrice(), product.getUrl());
+        }
+
+        return ResponseUtil.ok();
+    }
+
+    @Transactional
+    public Object delete(LitemallGoods goods) {
+        Integer id = goods.getId();
+        if (id == null) {
+            return ResponseUtil.badArgument();
+        }
+
+        Integer gid = goods.getId();
+        goodsService.deleteById(gid);
+        specificationService.deleteByGid(gid);
+        attributeService.deleteByGid(gid);
+        productService.deleteByGid(gid);
+        return ResponseUtil.ok();
+    }
+
+    @Transactional
+    public Object create(GoodsAllinone goodsAllinone) {
+        Object error = validate(goodsAllinone);
+        if (error != null) {
+            return error;
+        }
+
+        LitemallGoods goods = goodsAllinone.getGoods();
+        LitemallGoodsAttribute[] attributes = goodsAllinone.getAttributes();
+        LitemallGoodsSpecification[] specifications = goodsAllinone.getSpecifications();
+        LitemallGoodsProduct[] products = goodsAllinone.getProducts();
+
+        String name = goods.getName();
+        if (goodsService.checkExistByName(name)) {
+            return ResponseUtil.fail(GOODS_NAME_EXIST, "商品名已经存在");
+        }
+
+        // 商品表里面有一个字段retailPrice记录当前商品的最低价
+        BigDecimal retailPrice = new BigDecimal(Integer.MAX_VALUE);
+        for (LitemallGoodsProduct product : products) {
+            BigDecimal productPrice = product.getPrice();
+            if(retailPrice.compareTo(productPrice) == 1){
+                retailPrice = productPrice;
+            }
+        }
+        goods.setRetailPrice(retailPrice);
+
+        // 商品基本信息表litemall_goods
+        goodsService.add(goods);
+
+        //将生成的分享图片地址写入数据库
+        String url = qCodeService.createGoodShareImage(goods.getId().toString(), goods.getPicUrl(), goods.getName());
+        if (!StringUtils.isEmpty(url)) {
+            goods.setShareUrl(url);
+            if (goodsService.updateById(goods) == 0) {
+                throw new RuntimeException("更新数据失败");
+            }
+        }
+
+        // 商品规格表litemall_goods_specification
+        for (LitemallGoodsSpecification specification : specifications) {
+            specification.setGoodsId(goods.getId());
+            specificationService.add(specification);
+        }
+
+        // 商品参数表litemall_goods_attribute
+        for (LitemallGoodsAttribute attribute : attributes) {
+            attribute.setGoodsId(goods.getId());
+            attributeService.add(attribute);
+        }
+
+        // 商品货品表litemall_product
+        for (LitemallGoodsProduct product : products) {
+            product.setGoodsId(goods.getId());
+            productService.add(product);
+        }
+        return ResponseUtil.ok();
+    }
+
+    public Object list2() {
+        // http://element-cn.eleme.io/#/zh-CN/component/cascader
+        // 管理员设置“所属分类”
+        List<LitemallCategory> l1CatList = categoryService.queryL1();
+        List<CatVo> categoryList = new ArrayList<>(l1CatList.size());
+
+        for (LitemallCategory l1 : l1CatList) {
+            CatVo l1CatVo = new CatVo();
+            l1CatVo.setValue(l1.getId());
+            l1CatVo.setLabel(l1.getName());
+
+            List<LitemallCategory> l2CatList = categoryService.queryByPid(l1.getId());
+            List<CatVo> children = new ArrayList<>(l2CatList.size());
+            for (LitemallCategory l2 : l2CatList) {
+                CatVo l2CatVo = new CatVo();
+                l2CatVo.setValue(l2.getId());
+                l2CatVo.setLabel(l2.getName());
+                children.add(l2CatVo);
+            }
+            l1CatVo.setChildren(children);
+
+            categoryList.add(l1CatVo);
+        }
+
+        // http://element-cn.eleme.io/#/zh-CN/component/select
+        // 管理员设置“所属品牌商”
+        List<LitemallBrand> list = brandService.all();
+        List<Map<String, Object>> brandList = new ArrayList<>(l1CatList.size());
+        for (LitemallBrand brand : list) {
+            Map<String, Object> b = new HashMap<>(2);
+            b.put("value", brand.getId());
+            b.put("label", brand.getName());
+            brandList.add(b);
+        }
+
+        Map<String, Object> data = new HashMap<>();
+        data.put("categoryList", categoryList);
+        data.put("brandList", brandList);
+        return ResponseUtil.ok(data);
+    }
+
+    public Object detail(Integer id) {
+        LitemallGoods goods = goodsService.findById(id);
+        List<LitemallGoodsProduct> products = productService.queryByGid(id);
+        List<LitemallGoodsSpecification> specifications = specificationService.queryByGid(id);
+        List<LitemallGoodsAttribute> attributes = attributeService.queryByGid(id);
+
+        Integer categoryId = goods.getCategoryId();
+        LitemallCategory category = categoryService.findById(categoryId);
+        Integer[] categoryIds = new Integer[]{};
+        if (category != null) {
+            Integer parentCategoryId = category.getPid();
+            categoryIds = new Integer[]{parentCategoryId, categoryId};
+        }
+
+        Map<String, Object> data = new HashMap<>();
+        data.put("goods", goods);
+        data.put("specifications", specifications);
+        data.put("products", products);
+        data.put("attributes", attributes);
+        data.put("categoryIds", categoryIds);
+
+        return ResponseUtil.ok(data);
+    }
+
+}

+ 290 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminOrderService.java

@@ -0,0 +1,290 @@
+package org.linlinjava.litemall.admin.service;
+
+import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
+import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.WxPayService;
+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.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.OrderUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.linlinjava.litemall.admin.util.AdminResponseCode.*;
+
+@Service
+
+public class AdminOrderService {
+    private final Log logger = LogFactory.getLog(AdminOrderService.class);
+
+    @Autowired
+    private LitemallOrderGoodsService orderGoodsService;
+    @Autowired
+    private LitemallOrderService orderService;
+    @Autowired
+    private LitemallGoodsProductService productService;
+    @Autowired
+    private LitemallUserService userService;
+    @Autowired
+    private LitemallCommentService commentService;
+    @Autowired
+    private WxPayService wxPayService;
+    @Autowired
+    private NotifyService notifyService;
+    @Autowired
+    private LogHelper logHelper;
+    @Autowired
+    private LitemallCouponUserService couponUserService;
+
+    public Object list(Integer userId, String orderSn, LocalDateTime start, LocalDateTime end, List<Short> orderStatusArray,
+                       Integer page, Integer limit, String sort, String order) {
+        List<LitemallOrder> orderList = orderService.querySelective(userId, orderSn, start, end, orderStatusArray, page, limit,
+                sort, order);
+        return ResponseUtil.okList(orderList);
+    }
+
+    public Object detail(Integer id) {
+        LitemallOrder order = orderService.findById(id);
+        List<LitemallOrderGoods> orderGoods = orderGoodsService.queryByOid(id);
+        UserVo user = userService.findUserVoById(order.getUserId());
+        Map<String, Object> data = new HashMap<>();
+        data.put("order", order);
+        data.put("orderGoods", orderGoods);
+        data.put("user", user);
+
+        return ResponseUtil.ok(data);
+    }
+
+    /**
+     * 订单退款
+     * <p>
+     * 1. 检测当前订单是否能够退款;
+     * 2. 微信退款操作;
+     * 3. 设置订单退款确认状态;
+     * 4. 订单商品库存回库。
+     * <p>
+     * TODO
+     * 虽然接入了微信退款API,但是从安全角度考虑,建议开发者删除这里微信退款代码,采用以下两步走步骤:
+     * 1. 管理员登录微信官方支付平台点击退款操作进行退款
+     * 2. 管理员登录litemall管理后台点击退款操作进行订单状态修改和商品库存回库
+     *
+     * @param body 订单信息,{ orderId:xxx }
+     * @return 订单退款操作结果
+     */
+    @Transactional
+    public Object refund(String body) {
+        Integer orderId = JacksonUtil.parseInteger(body, "orderId");
+        String refundMoney = JacksonUtil.parseString(body, "refundMoney");
+        if (orderId == null) {
+            return ResponseUtil.badArgument();
+        }
+        if (StringUtils.isEmpty(refundMoney)) {
+            return ResponseUtil.badArgument();
+        }
+
+        LitemallOrder order = orderService.findById(orderId);
+        if (order == null) {
+            return ResponseUtil.badArgument();
+        }
+
+        if (order.getActualPrice().compareTo(new BigDecimal(refundMoney)) != 0) {
+            return ResponseUtil.badArgumentValue();
+        }
+
+        // 如果订单不是退款状态,则不能退款
+        if (!order.getOrderStatus().equals(OrderUtil.STATUS_REFUND)) {
+            return ResponseUtil.fail(ORDER_CONFIRM_NOT_ALLOWED, "订单不能确认收货");
+        }
+
+        // 微信退款
+        WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest();
+        wxPayRefundRequest.setOutTradeNo(order.getOrderSn());
+        wxPayRefundRequest.setOutRefundNo("refund_" + order.getOrderSn());
+        // 元转成分
+        Integer totalFee = order.getActualPrice().multiply(new BigDecimal(100)).intValue();
+        wxPayRefundRequest.setTotalFee(totalFee);
+        wxPayRefundRequest.setRefundFee(totalFee);
+
+        WxPayRefundResult wxPayRefundResult;
+        try {
+            wxPayRefundResult = wxPayService.refund(wxPayRefundRequest);
+        } catch (WxPayException e) {
+            logger.error(e.getMessage(), e);
+            return ResponseUtil.fail(ORDER_REFUND_FAILED, "订单退款失败");
+        }
+        if (!wxPayRefundResult.getReturnCode().equals("SUCCESS")) {
+            logger.warn("refund fail: " + wxPayRefundResult.getReturnMsg());
+            return ResponseUtil.fail(ORDER_REFUND_FAILED, "订单退款失败");
+        }
+        if (!wxPayRefundResult.getResultCode().equals("SUCCESS")) {
+            logger.warn("refund fail: " + wxPayRefundResult.getReturnMsg());
+            return ResponseUtil.fail(ORDER_REFUND_FAILED, "订单退款失败");
+        }
+
+        LocalDateTime now = LocalDateTime.now();
+        // 设置订单取消状态
+        order.setOrderStatus(OrderUtil.STATUS_REFUND_CONFIRM);
+        order.setEndTime(now);
+        // 记录订单退款相关信息
+        order.setRefundAmount(order.getActualPrice());
+        order.setRefundType("微信退款接口");
+        order.setRefundContent(wxPayRefundResult.getRefundId());
+        order.setRefundTime(now);
+        if (orderService.updateWithOptimisticLocker(order) == 0) {
+            throw new RuntimeException("更新数据已失效");
+        }
+
+        // 商品货品数量增加
+        List<LitemallOrderGoods> orderGoodsList = orderGoodsService.queryByOid(orderId);
+        for (LitemallOrderGoods orderGoods : orderGoodsList) {
+            Integer productId = orderGoods.getProductId();
+            Short number = orderGoods.getNumber();
+            if (productService.addStock(productId, number) == 0) {
+                throw new RuntimeException("商品货品库存增加失败");
+            }
+        }
+
+        // 返还优惠券
+        List<LitemallCouponUser> couponUsers = couponUserService.findByOid(orderId);
+        for (LitemallCouponUser couponUser: couponUsers) {
+            // 优惠券状态设置为可使用
+            couponUser.setStatus(CouponUserConstant.STATUS_USABLE);
+            couponUser.setUpdateTime(LocalDateTime.now());
+            couponUserService.update(couponUser);
+        }
+
+        //TODO 发送邮件和短信通知,这里采用异步发送
+        // 退款成功通知用户, 例如“您申请的订单退款 [ 单号:{1} ] 已成功,请耐心等待到账。”
+        // 注意订单号只发后6位
+        notifyService.notifySmsTemplate(order.getMobile(), NotifyType.REFUND,
+                new String[]{order.getOrderSn().substring(8, 14)});
+
+        logHelper.logOrderSucceed("退款", "订单编号 " + order.getOrderSn());
+        return ResponseUtil.ok();
+    }
+
+    /**
+     * 发货
+     * 1. 检测当前订单是否能够发货
+     * 2. 设置订单发货状态
+     *
+     * @param body 订单信息,{ orderId:xxx, shipSn: xxx, shipChannel: xxx }
+     * @return 订单操作结果
+     * 成功则 { errno: 0, errmsg: '成功' }
+     * 失败则 { errno: XXX, errmsg: XXX }
+     */
+    public Object ship(String body) {
+        Integer orderId = JacksonUtil.parseInteger(body, "orderId");
+        String shipSn = JacksonUtil.parseString(body, "shipSn");
+        String shipChannel = JacksonUtil.parseString(body, "shipChannel");
+        if (orderId == null || shipSn == null || shipChannel == null) {
+            return ResponseUtil.badArgument();
+        }
+
+        LitemallOrder order = orderService.findById(orderId);
+        if (order == null) {
+            return ResponseUtil.badArgument();
+        }
+
+        // 如果订单不是已付款状态,则不能发货
+        if (!order.getOrderStatus().equals(OrderUtil.STATUS_PAY)) {
+            return ResponseUtil.fail(ORDER_CONFIRM_NOT_ALLOWED, "订单不能确认收货");
+        }
+
+        order.setOrderStatus(OrderUtil.STATUS_SHIP);
+        order.setShipSn(shipSn);
+        order.setShipChannel(shipChannel);
+        order.setShipTime(LocalDateTime.now());
+        if (orderService.updateWithOptimisticLocker(order) == 0) {
+            return ResponseUtil.updatedDateExpired();
+        }
+
+        //TODO 发送邮件和短信通知,这里采用异步发送
+        // 发货会发送通知短信给用户:          *
+        // "您的订单已经发货,快递公司 {1},快递单 {2} ,请注意查收"
+        notifyService.notifySmsTemplate(order.getMobile(), NotifyType.SHIP, new String[]{shipChannel, shipSn});
+
+        logHelper.logOrderSucceed("发货", "订单编号 " + order.getOrderSn());
+        return ResponseUtil.ok();
+    }
+
+    /**
+     * 删除订单
+     * 1. 检测当前订单是否能够删除
+     * 2. 删除订单
+     *
+     * @param body 订单信息,{ orderId:xxx }
+     * @return 订单操作结果
+     * 成功则 { errno: 0, errmsg: '成功' }
+     * 失败则 { errno: XXX, errmsg: XXX }
+     */
+    public Object delete(String body) {
+        Integer orderId = JacksonUtil.parseInteger(body, "orderId");
+        LitemallOrder order = orderService.findById(orderId);
+        if (order == null) {
+            return ResponseUtil.badArgument();
+        }
+
+        // 如果订单不是关闭状态(已取消、系统取消、已退款、用户已确认、系统已确认),则不能删除
+        Short status = order.getOrderStatus();
+        if (!status.equals(OrderUtil.STATUS_CANCEL) && !status.equals(OrderUtil.STATUS_AUTO_CANCEL) &&
+                !status.equals(OrderUtil.STATUS_CONFIRM) &&!status.equals(OrderUtil.STATUS_AUTO_CONFIRM) &&
+                !status.equals(OrderUtil.STATUS_REFUND_CONFIRM)) {
+            return ResponseUtil.fail(ORDER_DELETE_FAILED, "订单不能删除");
+        }
+        // 删除订单
+        orderService.deleteById(orderId);
+        // 删除订单商品
+        orderGoodsService.deleteByOrderId(orderId);
+        logHelper.logOrderSucceed("删除", "订单编号 " + order.getOrderSn());
+        return ResponseUtil.ok();
+    }
+
+    /**
+     * 回复订单商品
+     *
+     * @param body 订单信息,{ orderId:xxx }
+     * @return 订单操作结果
+     * 成功则 { errno: 0, errmsg: '成功' }
+     * 失败则 { errno: XXX, errmsg: XXX }
+     */
+    public Object reply(String body) {
+        Integer commentId = JacksonUtil.parseInteger(body, "commentId");
+        if (commentId == null || commentId == 0) {
+            return ResponseUtil.badArgument();
+        }
+        // 目前只支持回复一次
+        LitemallComment comment = commentService.findById(commentId);
+        if(comment == null){
+            return ResponseUtil.badArgument();
+        }
+        if (!StringUtils.isEmpty(comment.getAdminContent())) {
+            return ResponseUtil.fail(ORDER_REPLY_EXIST, "订单商品已回复!");
+        }
+        String content = JacksonUtil.parseString(body, "content");
+        if (StringUtils.isEmpty(content)) {
+            return ResponseUtil.badArgument();
+        }
+        // 更新评价回复
+        comment.setAdminContent(content);
+        commentService.updateById(comment);
+
+        return ResponseUtil.ok();
+    }
+
+}

+ 113 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/LogHelper.java

@@ -0,0 +1,113 @@
+package org.linlinjava.litemall.admin.service;
+
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.subject.Subject;
+import org.linlinjava.litemall.core.util.IpUtil;
+import org.linlinjava.litemall.db.domain.LitemallAdmin;
+import org.linlinjava.litemall.db.domain.LitemallLog;
+import org.linlinjava.litemall.db.service.LitemallLogService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 这里的日志类型设计成四种(当然开发者需要可以自己扩展)
+ * 一般日志:用户觉得需要查看的一般操作日志,建议是默认的日志级别
+ * 安全日志:用户安全相关的操作日志,例如登录、删除管理员
+ * 订单日志:用户交易相关的操作日志,例如订单发货、退款
+ * 其他日志:如果以上三种不合适,可以选择其他日志,建议是优先级最低的日志级别
+ * <p>
+ * 当然可能很多操作是不需要记录到数据库的,例如编辑商品、编辑广告品之类。
+ */
+@Component
+public class LogHelper {
+    public static final  Integer LOG_TYPE_GENERAL = 0;
+    public static final  Integer LOG_TYPE_AUTH = 1;
+    public static final  Integer LOG_TYPE_ORDER = 2;
+    public static final  Integer LOG_TYPE_OTHER = 3;
+
+    @Autowired
+    private LitemallLogService logService;
+
+    public void logGeneralSucceed(String action) {
+        logAdmin(LOG_TYPE_GENERAL, action, true, "", "");
+    }
+
+    public void logGeneralSucceed(String action, String result) {
+        logAdmin(LOG_TYPE_GENERAL, action, true, result, "");
+    }
+
+    public void logGeneralFail(String action, String error) {
+        logAdmin(LOG_TYPE_GENERAL, action, false, error, "");
+    }
+
+    public void logAuthSucceed(String action) {
+        logAdmin(LOG_TYPE_AUTH, action, true, "", "");
+    }
+
+    public void logAuthSucceed(String action, String result) {
+        logAdmin(LOG_TYPE_AUTH, action, true, result, "");
+    }
+
+    public void logAuthFail(String action, String error) {
+        logAdmin(LOG_TYPE_AUTH, action, false, error, "");
+    }
+
+    public void logOrderSucceed(String action) {
+        logAdmin(LOG_TYPE_ORDER, action, true, "", "");
+    }
+
+    public void logOrderSucceed(String action, String result) {
+        logAdmin(LOG_TYPE_ORDER, action, true, result, "");
+    }
+
+    public void logOrderFail(String action, String error) {
+        logAdmin(LOG_TYPE_ORDER, action, false, error, "");
+    }
+
+    public void logOtherSucceed(String action) {
+        logAdmin(LOG_TYPE_OTHER, action, true, "", "");
+    }
+
+    public void logOtherSucceed(String action, String result) {
+        logAdmin(LOG_TYPE_OTHER, action, true, result, "");
+    }
+
+
+    public void logOtherFail(String action, String error) {
+        logAdmin(LOG_TYPE_OTHER, action, false, error, "");
+    }
+
+    public void logAdmin(Integer type, String action, Boolean succeed, String result, String comment) {
+        LitemallLog log = new LitemallLog();
+
+        Subject currentUser = SecurityUtils.getSubject();
+        if (currentUser != null) {
+            LitemallAdmin admin = (LitemallAdmin) currentUser.getPrincipal();
+            if (admin != null) {
+                log.setAdmin(admin.getUsername());
+            } else {
+                log.setAdmin("匿名用户");
+            }
+        } else {
+            log.setAdmin("匿名用户");
+        }
+
+        HttpServletRequest request =
+                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+        if (request != null) {
+            log.setIp(IpUtil.getIpAddr(request));
+        }
+
+        log.setType(type);
+        log.setAction(action);
+        log.setStatus(succeed);
+        log.setResult(result);
+        log.setComment(comment);
+        logService.add(log);
+    }
+
+}

+ 76 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/shiro/AdminAuthorizingRealm.java

@@ -0,0 +1,76 @@
+package org.linlinjava.litemall.admin.shiro;
+
+
+import org.apache.shiro.authc.*;
+import org.apache.shiro.authz.AuthorizationException;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.linlinjava.litemall.core.util.bcrypt.BCryptPasswordEncoder;
+import org.linlinjava.litemall.db.domain.LitemallAdmin;
+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.util.Assert;
+import org.springframework.util.StringUtils;
+
+import java.util.List;
+import java.util.Set;
+
+public class AdminAuthorizingRealm extends AuthorizingRealm {
+
+    @Autowired
+    private LitemallAdminService adminService;
+    @Autowired
+    private LitemallRoleService roleService;
+    @Autowired
+    private LitemallPermissionService permissionService;
+
+    @Override
+    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+        if (principals == null) {
+            throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
+        }
+
+        LitemallAdmin admin = (LitemallAdmin) getAvailablePrincipal(principals);
+        Integer[] roleIds = admin.getRoleIds();
+        Set<String> roles = roleService.queryByIds(roleIds);
+        Set<String> permissions = permissionService.queryByRoleIds(roleIds);
+        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
+        info.setRoles(roles);
+        info.setStringPermissions(permissions);
+        return info;
+    }
+
+    @Override
+    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
+
+        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
+        String username = upToken.getUsername();
+        String password = new String(upToken.getPassword());
+
+        if (StringUtils.isEmpty(username)) {
+            throw new AccountException("用户名不能为空");
+        }
+        if (StringUtils.isEmpty(password)) {
+            throw new AccountException("密码不能为空");
+        }
+
+        List<LitemallAdmin> adminList = adminService.findAdmin(username);
+        Assert.state(adminList.size() < 2, "同一个用户名存在两个账户");
+        if (adminList.size() == 0) {
+            throw new UnknownAccountException("找不到用户(" + username + ")的帐号信息");
+        }
+        LitemallAdmin admin = adminList.get(0);
+
+        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+        if (!encoder.matches(password, admin.getPassword())) {
+            throw new UnknownAccountException("找不到用户(" + username + ")的帐号信息");
+        }
+
+        return new SimpleAuthenticationInfo(admin, password, getName());
+    }
+
+}

+ 29 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/shiro/AdminWebSessionManager.java

@@ -0,0 +1,29 @@
+package org.linlinjava.litemall.admin.shiro;
+
+import com.alibaba.druid.util.StringUtils;
+import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
+import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
+import org.apache.shiro.web.util.WebUtils;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import java.io.Serializable;
+
+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";
+
+    @Override
+    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
+        String id = WebUtils.toHttp(request).getHeader(LOGIN_TOKEN_KEY);
+        if (!StringUtils.isEmpty(id)) {
+            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
+            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
+            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
+            return id;
+        } else {
+            return super.getSessionId(request, response);
+        }
+    }
+}

+ 41 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/task/AdminTaskStartupRunner.java

@@ -0,0 +1,41 @@
+package org.linlinjava.litemall.admin.task;
+
+import org.linlinjava.litemall.core.task.TaskService;
+import org.linlinjava.litemall.db.domain.LitemallGrouponRules;
+import org.linlinjava.litemall.db.service.LitemallGrouponRulesService;
+import org.linlinjava.litemall.db.util.GrouponConstant;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
+
+@Component
+public class AdminTaskStartupRunner implements ApplicationRunner {
+
+    @Autowired
+    private LitemallGrouponRulesService rulesService;
+    @Autowired
+    private TaskService taskService;
+
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+        List<LitemallGrouponRules> grouponRulesList = rulesService.queryByStatus(GrouponConstant.RULE_STATUS_ON);
+        for(LitemallGrouponRules grouponRules : grouponRulesList){
+            LocalDateTime now = LocalDateTime.now();
+            LocalDateTime expire =  grouponRules.getExpireTime();
+            if(expire.isBefore(now)) {
+                // 已经过期,则加入延迟队列
+                taskService.addTask(new GrouponRuleExpiredTask(grouponRules.getId(), 0));
+            }
+            else{
+                // 还没过期,则加入延迟队列
+                long delay = ChronoUnit.MILLIS.between(now, expire);
+                taskService.addTask(new GrouponRuleExpiredTask(grouponRules.getId(), delay));
+            }
+        }
+    }
+}

+ 68 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/task/GrouponRuleExpiredTask.java

@@ -0,0 +1,68 @@
+package org.linlinjava.litemall.admin.task;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.linlinjava.litemall.core.task.Task;
+import org.linlinjava.litemall.core.util.BeanUtil;
+import org.linlinjava.litemall.db.domain.LitemallGroupon;
+import org.linlinjava.litemall.db.domain.LitemallGrouponRules;
+import org.linlinjava.litemall.db.domain.LitemallOrder;
+import org.linlinjava.litemall.db.service.*;
+import org.linlinjava.litemall.db.util.GrouponConstant;
+import org.linlinjava.litemall.db.util.OrderUtil;
+
+import java.util.List;
+
+public class GrouponRuleExpiredTask extends Task {
+    private final Log logger = LogFactory.getLog(GrouponRuleExpiredTask.class);
+    private int grouponRuleId = -1;
+
+    public GrouponRuleExpiredTask(Integer grouponRuleId, long delayInMilliseconds){
+        super("GrouponRuleExpiredTask-" + grouponRuleId, delayInMilliseconds);
+        this.grouponRuleId = grouponRuleId;
+    }
+
+    @Override
+    public void run() {
+        logger.info("系统开始处理延时任务---团购规则过期---" + this.grouponRuleId);
+
+        LitemallOrderService orderService = BeanUtil.getBean(LitemallOrderService.class);
+        LitemallGrouponService grouponService = BeanUtil.getBean(LitemallGrouponService.class);
+        LitemallGrouponRulesService grouponRulesService = BeanUtil.getBean(LitemallGrouponRulesService.class);
+
+        LitemallGrouponRules grouponRules = grouponRulesService.findById(grouponRuleId);
+        if(grouponRules == null){
+            return;
+        }
+        if(!grouponRules.getStatus().equals(GrouponConstant.RULE_STATUS_ON)){
+            return;
+        }
+
+        // 团购活动取消
+        grouponRules.setStatus(GrouponConstant.RULE_STATUS_DOWN_EXPIRE);
+        grouponRulesService.updateById(grouponRules);
+
+        List<LitemallGroupon> grouponList = grouponService.queryByRuleId(grouponRuleId);
+        // 用户团购处理
+        for(LitemallGroupon groupon : grouponList){
+            Short status = groupon.getStatus();
+            LitemallOrder order = orderService.findById(groupon.getOrderId());
+            if(status.equals(GrouponConstant.STATUS_NONE)){
+                groupon.setStatus(GrouponConstant.STATUS_FAIL);
+                grouponService.updateById(groupon);
+            }
+            else if(status.equals(GrouponConstant.STATUS_ON)){
+                // 如果团购进行中
+                // (1) 团购设置团购失败等待退款状态
+                groupon.setStatus(GrouponConstant.STATUS_FAIL);
+                grouponService.updateById(groupon);
+                // (2) 团购订单申请退款
+                if(OrderUtil.isPayStatus(order)) {
+                    order.setOrderStatus(OrderUtil.STATUS_REFUND);
+                    orderService.updateWithOptimisticLocker(order);
+                }
+            }
+        }
+        logger.info("系统结束处理延时任务---团购规则过期---" + this.grouponRuleId);
+    }
+}

+ 30 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/util/AdminResponseCode.java

@@ -0,0 +1,30 @@
+package org.linlinjava.litemall.admin.util;
+
+public class AdminResponseCode {
+    public static final Integer ADMIN_INVALID_NAME = 601;
+    public static final Integer ADMIN_INVALID_PASSWORD = 602;
+    public static final Integer ADMIN_NAME_EXIST = 602;
+    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 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 USER_INVALID_PASSWORD = 631;
+    public static final Integer USER_INVALID_MOBILE = 632;
+    public static final Integer USER_NAME_EXIST = 633;
+    public static final Integer USER_MOBILE_EXIST = 634;
+    public static final Integer ROLE_NAME_EXIST = 640;
+    public static final Integer ROLE_SUPER_SUPERMISSION = 641;
+    public static final Integer ROLE_USER_EXIST = 642;
+    public static final Integer GROUPON_GOODS_UNKNOWN = 650;
+    public static final Integer GROUPON_GOODS_EXISTED = 651;
+    public static final Integer GROUPON_GOODS_OFFLINE = 652;
+    public static final Integer NOTICE_UPDATE_NOT_ALLOWED = 660;
+    public static final Integer AFTERSALE_NOT_ALLOWED = 670;
+
+}

+ 34 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/util/Permission.java

@@ -0,0 +1,34 @@
+package org.linlinjava.litemall.admin.util;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+
+public class Permission {
+    private RequiresPermissions requiresPermissions;
+    private RequiresPermissionsDesc requiresPermissionsDesc;
+    private String api;
+
+    public RequiresPermissions getRequiresPermissions() {
+        return requiresPermissions;
+    }
+
+    public RequiresPermissionsDesc getRequiresPermissionsDesc() {
+        return requiresPermissionsDesc;
+    }
+
+    public void setRequiresPermissions(RequiresPermissions requiresPermissions) {
+        this.requiresPermissions = requiresPermissions;
+    }
+
+    public void setRequiresPermissionsDesc(RequiresPermissionsDesc requiresPermissionsDesc) {
+        this.requiresPermissionsDesc = requiresPermissionsDesc;
+    }
+
+    public String getApi() {
+        return api;
+    }
+
+    public void setApi(String api) {
+        this.api = api;
+    }
+}

+ 152 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/util/PermissionUtil.java

@@ -0,0 +1,152 @@
+package org.linlinjava.litemall.admin.util;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.reflect.MethodUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.admin.vo.PermVo;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.stereotype.Controller;
+import org.springframework.util.ClassUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+public class PermissionUtil {
+
+    public static List<PermVo> listPermVo(List<Permission> permissions) {
+        List<PermVo> root = new ArrayList<>();
+        for (Permission permission : permissions) {
+            RequiresPermissions requiresPermissions = permission.getRequiresPermissions();
+            RequiresPermissionsDesc requiresPermissionsDesc = permission.getRequiresPermissionsDesc();
+            String api = permission.getApi();
+
+            String[] menus = requiresPermissionsDesc.menu();
+            if (menus.length != 2) {
+                throw new RuntimeException("目前只支持两级菜单");
+            }
+            String menu1 = menus[0];
+            PermVo perm1 = null;
+            for (PermVo permVo : root) {
+                if (permVo.getLabel().equals(menu1)) {
+                    perm1 = permVo;
+                    break;
+                }
+            }
+            if (perm1 == null) {
+                perm1 = new PermVo();
+                perm1.setId(menu1);
+                perm1.setLabel(menu1);
+                perm1.setChildren(new ArrayList<>());
+                root.add(perm1);
+            }
+            String menu2 = menus[1];
+            PermVo perm2 = null;
+            for (PermVo permVo : perm1.getChildren()) {
+                if (permVo.getLabel().equals(menu2)) {
+                    perm2 = permVo;
+                    break;
+                }
+            }
+            if (perm2 == null) {
+                perm2 = new PermVo();
+                perm2.setId(menu2);
+                perm2.setLabel(menu2);
+                perm2.setChildren(new ArrayList<>());
+                perm1.getChildren().add(perm2);
+            }
+
+            String button = requiresPermissionsDesc.button();
+            PermVo leftPerm = null;
+            for (PermVo permVo : perm2.getChildren()) {
+                if (permVo.getLabel().equals(button)) {
+                    leftPerm = permVo;
+                    break;
+                }
+            }
+            if (leftPerm == null) {
+                leftPerm = new PermVo();
+                leftPerm.setId(requiresPermissions.value()[0]);
+                leftPerm.setLabel(requiresPermissionsDesc.button());
+                leftPerm.setApi(api);
+                perm2.getChildren().add(leftPerm);
+            } else {
+                // TODO
+                // 目前限制Controller里面每个方法的RequiresPermissionsDesc注解是唯一的
+                // 如果允许相同,可能会造成内部权限不一致。
+                throw new RuntimeException("权限已经存在,不能添加新权限");
+            }
+
+        }
+        return root;
+    }
+
+    public static List<Permission> listPermission(ApplicationContext context, String basicPackage) {
+        Map<String, Object> map = context.getBeansWithAnnotation(Controller.class);
+        List<Permission> permissions = new ArrayList<>();
+        for (Map.Entry<String, Object> entry : map.entrySet()) {
+            Object bean = entry.getValue();
+            if (!StringUtils.contains(ClassUtils.getPackageName(bean.getClass()), basicPackage)) {
+                continue;
+            }
+
+            Class<?> clz = bean.getClass();
+            Class controllerClz = clz.getSuperclass();
+            RequestMapping clazzRequestMapping = AnnotationUtils.findAnnotation(controllerClz, RequestMapping.class);
+            List<Method> methods = MethodUtils.getMethodsListWithAnnotation(controllerClz, RequiresPermissions.class);
+            for (Method method : methods) {
+                RequiresPermissions requiresPermissions = AnnotationUtils.getAnnotation(method,
+                        RequiresPermissions.class);
+                RequiresPermissionsDesc requiresPermissionsDesc = AnnotationUtils.getAnnotation(method,
+                        RequiresPermissionsDesc.class);
+
+                if (requiresPermissions == null || requiresPermissionsDesc == null) {
+                    continue;
+                }
+
+                String api = "";
+                if (clazzRequestMapping != null) {
+                    api = clazzRequestMapping.value()[0];
+                }
+
+                PostMapping postMapping = AnnotationUtils.getAnnotation(method, PostMapping.class);
+                if (postMapping != null) {
+                    api = "POST " + api + postMapping.value()[0];
+
+                    Permission permission = new Permission();
+                    permission.setRequiresPermissions(requiresPermissions);
+                    permission.setRequiresPermissionsDesc(requiresPermissionsDesc);
+                    permission.setApi(api);
+                    permissions.add(permission);
+                    continue;
+                }
+                GetMapping getMapping = AnnotationUtils.getAnnotation(method, GetMapping.class);
+                if (getMapping != null) {
+                    api = "GET " + api + getMapping.value()[0];
+                    Permission permission = new Permission();
+                    permission.setRequiresPermissions(requiresPermissions);
+                    permission.setRequiresPermissionsDesc(requiresPermissionsDesc);
+                    permission.setApi(api);
+                    permissions.add(permission);
+                    continue;
+                }
+                // TODO
+                // 这里只支持GetMapping注解或者PostMapping注解,应该进一步提供灵活性
+                throw new RuntimeException("目前权限管理应该在method的前面使用GetMapping注解或者PostMapping注解");
+            }
+        }
+        return permissions;
+    }
+
+    public static Set<String> listPermissionString(List<Permission> permissions) {
+        Set<String> permissionsString = new HashSet<>();
+        for (Permission permission : permissions) {
+            permissionsString.add(permission.getRequiresPermissions().value()[0]);
+        }
+        return permissionsString;
+    }
+}

+ 33 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/vo/CatVo.java

@@ -0,0 +1,33 @@
+package org.linlinjava.litemall.admin.vo;
+
+import java.util.List;
+
+public class CatVo {
+    private Integer value = null;
+    private String label = null;
+    private List children = null;
+
+    public Integer getValue() {
+        return value;
+    }
+
+    public void setValue(Integer value) {
+        this.value = value;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public List getChildren() {
+        return children;
+    }
+
+    public void setChildren(List children) {
+        this.children = children;
+    }
+}

+ 78 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/vo/CategoryVo.java

@@ -0,0 +1,78 @@
+package org.linlinjava.litemall.admin.vo;
+
+import java.util.List;
+
+public class CategoryVo {
+    private Integer id;
+    private String name;
+    private String keywords;
+    private String desc;
+    private String iconUrl;
+    private String picUrl;
+    private String level;
+    private List<CategoryVo> children;
+
+    public List<CategoryVo> getChildren() {
+        return children;
+    }
+
+    public void setChildren(List<CategoryVo> children) {
+        this.children = children;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getKeywords() {
+        return keywords;
+    }
+
+    public void setKeywords(String keywords) {
+        this.keywords = keywords;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
+    public String getIconUrl() {
+        return iconUrl;
+    }
+
+    public void setIconUrl(String iconUrl) {
+        this.iconUrl = iconUrl;
+    }
+
+    public String getPicUrl() {
+        return picUrl;
+    }
+
+    public void setPicUrl(String picUrl) {
+        this.picUrl = picUrl;
+    }
+
+    public String getLevel() {
+        return level;
+    }
+
+    public void setLevel(String level) {
+        this.level = level;
+    }
+}

+ 43 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/vo/PermVo.java

@@ -0,0 +1,43 @@
+package org.linlinjava.litemall.admin.vo;
+
+import java.util.List;
+
+public class PermVo {
+    private String id;
+    private String label;
+    private String api;
+    private List<PermVo> children;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public void setApi(String api) {
+        this.api = api;
+    }
+
+    public String getApi() {
+        return api;
+    }
+
+    public List<PermVo> getChildren() {
+        return children;
+    }
+
+    public void setChildren(List<PermVo> children) {
+        this.children = children;
+    }
+
+}

+ 52 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/vo/RegionVo.java

@@ -0,0 +1,52 @@
+package org.linlinjava.litemall.admin.vo;
+
+import java.util.List;
+
+public class RegionVo {
+    private Integer id;
+    private String name;
+    private Byte type;
+    private Integer code;
+
+    private List<RegionVo> children;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public List<RegionVo> getChildren() {
+        return children;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Byte getType() {
+        return type;
+    }
+
+    public void setType(Byte type) {
+        this.type = type;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public void setCode(Integer code) {
+        this.code = code;
+    }
+
+    public void setChildren(List<RegionVo> children) {
+        this.children = children;
+    }
+}

+ 31 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/vo/StatVo.java

@@ -0,0 +1,31 @@
+package org.linlinjava.litemall.admin.vo;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class StatVo {
+    private String[] columns = new String[0];
+    private List<Map> rows = new ArrayList<>();
+
+    public String[] getColumns() {
+        return columns;
+    }
+
+    public void setColumns(String[] columns) {
+        this.columns = columns;
+    }
+
+    public List<Map> getRows() {
+        return rows;
+    }
+
+    public void setRows(List<Map> rows) {
+        this.rows = rows;
+    }
+
+    public void add(Map... r) {
+        rows.addAll(Arrays.asList(r));
+    }
+}

+ 100 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAdController.java

@@ -0,0 +1,100 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallAd;
+import org.linlinjava.litemall.db.service.LitemallAdService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/ad")
+@Validated
+public class AdminAdController {
+    private final Log logger = LogFactory.getLog(AdminAdController.class);
+
+    @Autowired
+    private LitemallAdService adService;
+
+    @RequiresPermissions("admin:ad:list")
+    @RequiresPermissionsDesc(menu = {"推广管理", "广告管理"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(String name, String content,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallAd> adList = adService.querySelective(name, content, page, limit, sort, order);
+        return ResponseUtil.okList(adList);
+    }
+
+    private Object validate(LitemallAd ad) {
+        String name = ad.getName();
+        if (StringUtils.isEmpty(name)) {
+            return ResponseUtil.badArgument();
+        }
+        String content = ad.getContent();
+        if (StringUtils.isEmpty(content)) {
+            return ResponseUtil.badArgument();
+        }
+        return null;
+    }
+
+    @RequiresPermissions("admin:ad:create")
+    @RequiresPermissionsDesc(menu = {"推广管理", "广告管理"}, button = "添加")
+    @PostMapping("/create")
+    public Object create(@RequestBody LitemallAd ad) {
+        Object error = validate(ad);
+        if (error != null) {
+            return error;
+        }
+        adService.add(ad);
+        return ResponseUtil.ok(ad);
+    }
+
+    @RequiresPermissions("admin:ad:read")
+    @RequiresPermissionsDesc(menu = {"推广管理", "广告管理"}, button = "详情")
+    @GetMapping("/read")
+    public Object read(@NotNull Integer id) {
+        LitemallAd ad = adService.findById(id);
+        return ResponseUtil.ok(ad);
+    }
+
+    @RequiresPermissions("admin:ad:update")
+    @RequiresPermissionsDesc(menu = {"推广管理", "广告管理"}, button = "编辑")
+    @PostMapping("/update")
+    public Object update(@RequestBody LitemallAd ad) {
+        Object error = validate(ad);
+        if (error != null) {
+            return error;
+        }
+        if (adService.updateById(ad) == 0) {
+            return ResponseUtil.updatedDataFailed();
+        }
+
+        return ResponseUtil.ok(ad);
+    }
+
+    @RequiresPermissions("admin:ad:delete")
+    @RequiresPermissionsDesc(menu = {"推广管理", "广告管理"}, button = "删除")
+    @PostMapping("/delete")
+    public Object delete(@RequestBody LitemallAd ad) {
+        Integer id = ad.getId();
+        if (id == null) {
+            return ResponseUtil.badArgument();
+        }
+        adService.deleteById(id);
+        return ResponseUtil.ok();
+    }
+
+}

+ 45 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAddressController.java

@@ -0,0 +1,45 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallAddress;
+import org.linlinjava.litemall.db.service.LitemallAddressService;
+import org.linlinjava.litemall.db.service.LitemallRegionService;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/address")
+@Validated
+public class AdminAddressController {
+    private final Log logger = LogFactory.getLog(AdminAddressController.class);
+
+    @Autowired
+    private LitemallAddressService addressService;
+    @Autowired
+    private LitemallRegionService regionService;
+
+    @RequiresPermissions("admin:address:list")
+    @RequiresPermissionsDesc(menu = {"用户管理", "收货地址"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(Integer userId, String name,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+
+        List<LitemallAddress> addressList = addressService.querySelective(userId, name, page, limit, sort, order);
+        return ResponseUtil.okList(addressList);
+    }
+}

+ 142 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAdminController.java

@@ -0,0 +1,142 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.apache.shiro.subject.Subject;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.admin.service.LogHelper;
+import org.linlinjava.litemall.core.util.RegexUtil;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.util.bcrypt.BCryptPasswordEncoder;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallAdmin;
+import org.linlinjava.litemall.db.service.LitemallAdminService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+import static org.linlinjava.litemall.admin.util.AdminResponseCode.*;
+
+@RestController
+@RequestMapping("/admin/admin")
+@Validated
+public class AdminAdminController {
+    private final Log logger = LogFactory.getLog(AdminAdminController.class);
+
+    @Autowired
+    private LitemallAdminService adminService;
+    @Autowired
+    private LogHelper logHelper;
+
+    @RequiresPermissions("admin:admin:list")
+    @RequiresPermissionsDesc(menu = {"系统管理", "管理员管理"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(String username,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallAdmin> adminList = adminService.querySelective(username, page, limit, sort, order);
+        return ResponseUtil.okList(adminList);
+    }
+
+    private Object validate(LitemallAdmin admin) {
+        String name = admin.getUsername();
+        if (StringUtils.isEmpty(name)) {
+            return ResponseUtil.badArgument();
+        }
+        if (!RegexUtil.isUsername(name)) {
+            return ResponseUtil.fail(ADMIN_INVALID_NAME, "管理员名称不符合规定");
+        }
+        String password = admin.getPassword();
+        if (StringUtils.isEmpty(password) || password.length() < 6) {
+            return ResponseUtil.fail(ADMIN_INVALID_PASSWORD, "管理员密码长度不能小于6");
+        }
+        return null;
+    }
+
+    @RequiresPermissions("admin:admin:create")
+    @RequiresPermissionsDesc(menu = {"系统管理", "管理员管理"}, button = "添加")
+    @PostMapping("/create")
+    public Object create(@RequestBody LitemallAdmin admin) {
+        Object error = validate(admin);
+        if (error != null) {
+            return error;
+        }
+
+        String username = admin.getUsername();
+        List<LitemallAdmin> adminList = adminService.findAdmin(username);
+        if (adminList.size() > 0) {
+            return ResponseUtil.fail(ADMIN_NAME_EXIST, "管理员已经存在");
+        }
+
+        String rawPassword = admin.getPassword();
+        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+        String encodedPassword = encoder.encode(rawPassword);
+        admin.setPassword(encodedPassword);
+        adminService.add(admin);
+        logHelper.logAuthSucceed("添加管理员", username);
+        return ResponseUtil.ok(admin);
+    }
+
+    @RequiresPermissions("admin:admin:read")
+    @RequiresPermissionsDesc(menu = {"系统管理", "管理员管理"}, button = "详情")
+    @GetMapping("/read")
+    public Object read(@NotNull Integer id) {
+        LitemallAdmin admin = adminService.findById(id);
+        return ResponseUtil.ok(admin);
+    }
+
+    @RequiresPermissions("admin:admin:update")
+    @RequiresPermissionsDesc(menu = {"系统管理", "管理员管理"}, button = "编辑")
+    @PostMapping("/update")
+    public Object update(@RequestBody LitemallAdmin admin) {
+        Object error = validate(admin);
+        if (error != null) {
+            return error;
+        }
+
+        Integer anotherAdminId = admin.getId();
+        if (anotherAdminId == null) {
+            return ResponseUtil.badArgument();
+        }
+
+        // 不允许管理员通过编辑接口修改密码
+        admin.setPassword(null);
+
+        if (adminService.updateById(admin) == 0) {
+            return ResponseUtil.updatedDataFailed();
+        }
+
+        logHelper.logAuthSucceed("编辑管理员", admin.getUsername());
+        return ResponseUtil.ok(admin);
+    }
+
+    @RequiresPermissions("admin:admin:delete")
+    @RequiresPermissionsDesc(menu = {"系统管理", "管理员管理"}, button = "删除")
+    @PostMapping("/delete")
+    public Object delete(@RequestBody LitemallAdmin admin) {
+        Integer anotherAdminId = admin.getId();
+        if (anotherAdminId == null) {
+            return ResponseUtil.badArgument();
+        }
+
+        // 管理员不能删除自身账号
+        Subject currentUser = SecurityUtils.getSubject();
+        LitemallAdmin currentAdmin = (LitemallAdmin) currentUser.getPrincipal();
+        if (currentAdmin.getId().equals(anotherAdminId)) {
+            return ResponseUtil.fail(ADMIN_DELETE_NOT_ALLOWED, "管理员不能删除自己账号");
+        }
+
+        adminService.deleteById(anotherAdminId);
+        logHelper.logAuthSucceed("删除管理员", admin.getUsername());
+        return ResponseUtil.ok();
+    }
+}

+ 232 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAftersaleController.java

@@ -0,0 +1,232 @@
+package org.linlinjava.litemall.admin.web;
+
+import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
+import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.WxPayService;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.admin.service.LogHelper;
+import org.linlinjava.litemall.admin.util.AdminResponseCode;
+import org.linlinjava.litemall.core.notify.NotifyService;
+import org.linlinjava.litemall.core.notify.NotifyType;
+import org.linlinjava.litemall.core.util.JacksonUtil;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallAftersale;
+import org.linlinjava.litemall.db.domain.LitemallGoodsProduct;
+import org.linlinjava.litemall.db.domain.LitemallOrder;
+import org.linlinjava.litemall.db.domain.LitemallOrderGoods;
+import org.linlinjava.litemall.db.service.*;
+import org.linlinjava.litemall.db.util.AftersaleConstant;
+import org.linlinjava.litemall.db.util.OrderUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+import static org.linlinjava.litemall.admin.util.AdminResponseCode.ORDER_REFUND_FAILED;
+
+@RestController
+@RequestMapping("/admin/aftersale")
+@Validated
+public class AdminAftersaleController {
+    private final Log logger = LogFactory.getLog(AdminAftersaleController.class);
+
+    @Autowired
+    private LitemallAftersaleService aftersaleService;
+    @Autowired
+    private LitemallOrderService orderService;
+    @Autowired
+    private LitemallOrderGoodsService orderGoodsService;
+    @Autowired
+    private LitemallGoodsProductService goodsProductService;
+    @Autowired
+    private LogHelper logHelper;
+    @Autowired
+    private WxPayService wxPayService;
+    @Autowired
+    private NotifyService notifyService;
+
+    @RequiresPermissions("admin:aftersale:list")
+    @RequiresPermissionsDesc(menu = {"商城管理", "售后管理"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(Integer orderId, String aftersaleSn, Short status,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallAftersale> aftersaleList = aftersaleService.querySelective(orderId, aftersaleSn, status, page, limit, sort, order);
+        return ResponseUtil.okList(aftersaleList);
+    }
+
+    @RequiresPermissions("admin:aftersale:recept")
+    @RequiresPermissionsDesc(menu = {"商城管理", "售后管理"}, button = "审核通过")
+    @PostMapping("/recept")
+    public Object recept(@RequestBody LitemallAftersale aftersale) {
+        Integer id = aftersale.getId();
+        LitemallAftersale aftersaleOne = aftersaleService.findById(id);
+        if(aftersaleOne == null){
+            return ResponseUtil.fail(AdminResponseCode.AFTERSALE_NOT_ALLOWED, "售后不存在");
+        }
+        Short status = aftersaleOne.getStatus();
+        if(!status.equals(AftersaleConstant.STATUS_REQUEST)){
+            return ResponseUtil.fail(AdminResponseCode.AFTERSALE_NOT_ALLOWED, "售后不能进行审核通过操作");
+        }
+        aftersaleOne.setStatus(AftersaleConstant.STATUS_RECEPT);
+        aftersaleOne.setHandleTime(LocalDateTime.now());
+        aftersaleService.updateById(aftersaleOne);
+
+        // 订单也要更新售后状态
+        orderService.updateAftersaleStatus(aftersaleOne.getOrderId(), AftersaleConstant.STATUS_RECEPT);
+        return ResponseUtil.ok();
+    }
+
+    @RequiresPermissions("admin:aftersale:batch-recept")
+    @RequiresPermissionsDesc(menu = {"商城管理", "售后管理"}, button = "批量通过")
+    @PostMapping("/batch-recept")
+    public Object batchRecept(@RequestBody String body) {
+        List<Integer> ids = JacksonUtil.parseIntegerList(body, "ids");
+        // NOTE
+        // 批量操作中,如果一部分数据项失败,应该如何处理
+        // 这里采用忽略失败,继续处理其他项。
+        // 当然开发者可以采取其他处理方式,具体情况具体分析,例如利用事务回滚所有操作然后返回用户失败信息
+        for(Integer id : ids) {
+            LitemallAftersale aftersale = aftersaleService.findById(id);
+            if(aftersale == null){
+                continue;
+            }
+            Short status = aftersale.getStatus();
+            if(!status.equals(AftersaleConstant.STATUS_REQUEST)){
+                continue;
+            }
+            aftersale.setStatus(AftersaleConstant.STATUS_RECEPT);
+            aftersale.setHandleTime(LocalDateTime.now());
+            aftersaleService.updateById(aftersale);
+
+            // 订单也要更新售后状态
+            orderService.updateAftersaleStatus(aftersale.getOrderId(), AftersaleConstant.STATUS_RECEPT);
+        }
+        return ResponseUtil.ok();
+    }
+
+    @RequiresPermissions("admin:aftersale:reject")
+    @RequiresPermissionsDesc(menu = {"商城管理", "售后管理"}, button = "审核拒绝")
+    @PostMapping("/reject")
+    public Object reject(@RequestBody LitemallAftersale aftersale) {
+        Integer id = aftersale.getId();
+        LitemallAftersale aftersaleOne = aftersaleService.findById(id);
+        if(aftersaleOne == null){
+            return ResponseUtil.badArgumentValue();
+        }
+        Short status = aftersaleOne.getStatus();
+        if(!status.equals(AftersaleConstant.STATUS_REQUEST)){
+            return ResponseUtil.fail(AdminResponseCode.AFTERSALE_NOT_ALLOWED, "售后不能进行审核拒绝操作");
+        }
+        aftersaleOne.setStatus(AftersaleConstant.STATUS_REJECT);
+        aftersaleOne.setHandleTime(LocalDateTime.now());
+        aftersaleService.updateById(aftersaleOne);
+
+        // 订单也要更新售后状态
+        orderService.updateAftersaleStatus(aftersaleOne.getOrderId(), AftersaleConstant.STATUS_REJECT);
+        return ResponseUtil.ok();
+    }
+
+    @RequiresPermissions("admin:aftersale:batch-reject")
+    @RequiresPermissionsDesc(menu = {"商城管理", "售后管理"}, button = "批量拒绝")
+    @PostMapping("/batch-reject")
+    public Object batchReject(@RequestBody String body) {
+        List<Integer> ids = JacksonUtil.parseIntegerList(body, "ids");
+        for(Integer id : ids) {
+            LitemallAftersale aftersale = aftersaleService.findById(id);
+            if(aftersale == null){
+                continue;
+            }
+            Short status = aftersale.getStatus();
+            if(!status.equals(AftersaleConstant.STATUS_REQUEST)){
+                continue;
+            }
+            aftersale.setStatus(AftersaleConstant.STATUS_REJECT);
+            aftersale.setHandleTime(LocalDateTime.now());
+            aftersaleService.updateById(aftersale);
+
+            // 订单也要更新售后状态
+            orderService.updateAftersaleStatus(aftersale.getOrderId(), AftersaleConstant.STATUS_REJECT);
+        }
+        return ResponseUtil.ok();
+    }
+
+    @RequiresPermissions("admin:aftersale:refund")
+    @RequiresPermissionsDesc(menu = {"商城管理", "售后管理"}, button = "退款")
+    @PostMapping("/refund")
+    public Object refund(@RequestBody LitemallAftersale aftersale) {
+        Integer id = aftersale.getId();
+        LitemallAftersale aftersaleOne = aftersaleService.findById(id);
+        if(aftersaleOne == null){
+            return ResponseUtil.badArgumentValue();
+        }
+        if(!aftersaleOne.getStatus().equals(AftersaleConstant.STATUS_RECEPT)){
+            return ResponseUtil.fail(AdminResponseCode.AFTERSALE_NOT_ALLOWED, "售后不能进行退款操作");
+        }
+        Integer orderId = aftersaleOne.getOrderId();
+        LitemallOrder order = orderService.findById(orderId);
+
+        // 微信退款
+        WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest();
+        wxPayRefundRequest.setOutTradeNo(order.getOrderSn());
+        wxPayRefundRequest.setOutRefundNo("refund_" + order.getOrderSn());
+        // 元转成分
+        Integer totalFee = aftersaleOne.getAmount().multiply(new BigDecimal(100)).intValue();
+        wxPayRefundRequest.setTotalFee(totalFee);
+        wxPayRefundRequest.setRefundFee(totalFee);
+
+        WxPayRefundResult wxPayRefundResult;
+        try {
+            wxPayRefundResult = wxPayService.refund(wxPayRefundRequest);
+        } catch (WxPayException e) {
+            logger.error(e.getMessage(), e);
+            return ResponseUtil.fail(ORDER_REFUND_FAILED, "订单退款失败");
+        }
+        if (!wxPayRefundResult.getReturnCode().equals("SUCCESS")) {
+            logger.warn("refund fail: " + wxPayRefundResult.getReturnMsg());
+            return ResponseUtil.fail(ORDER_REFUND_FAILED, "订单退款失败");
+        }
+        if (!wxPayRefundResult.getResultCode().equals("SUCCESS")) {
+            logger.warn("refund fail: " + wxPayRefundResult.getReturnMsg());
+            return ResponseUtil.fail(ORDER_REFUND_FAILED, "订单退款失败");
+        }
+
+        aftersaleOne.setStatus(AftersaleConstant.STATUS_REFUND);
+        aftersaleOne.setHandleTime(LocalDateTime.now());
+        aftersaleService.updateById(aftersaleOne);
+
+        orderService.updateAftersaleStatus(orderId, AftersaleConstant.STATUS_REFUND);
+
+        // NOTE
+        // 如果是“退货退款”类型的售后,这里退款说明用户的货已经退回,则需要商品货品数量增加
+        // 开发者也可以删除一下代码,在其他地方增加商品货品入库操作
+        if(aftersale.getType().equals(AftersaleConstant.TYPE_GOODS_REQUIRED)) {
+            List<LitemallOrderGoods> orderGoodsList = orderGoodsService.queryByOid(orderId);
+            for (LitemallOrderGoods orderGoods : orderGoodsList) {
+                Integer productId = orderGoods.getProductId();
+                Short number = orderGoods.getNumber();
+                goodsProductService.addStock(productId, number);
+            }
+        }
+
+        // 发送短信通知,这里采用异步发送
+        // 退款成功通知用户, 例如“您申请的订单退款 [ 单号:{1} ] 已成功,请耐心等待到账。”
+        // TODO 注意订单号只发后6位
+        notifyService.notifySmsTemplate(order.getMobile(), NotifyType.REFUND,
+                new String[]{order.getOrderSn().substring(8, 14)});
+
+        logHelper.logOrderSucceed("退款", "订单编号 " + order.getOrderSn() + " 售后编号 " + aftersale.getAftersaleSn());
+        return ResponseUtil.ok();
+    }
+}

+ 175 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAuthController.java

@@ -0,0 +1,175 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.LockedAccountException;
+import org.apache.shiro.authc.UnknownAccountException;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.authz.annotation.RequiresAuthentication;
+import org.apache.shiro.subject.Subject;
+import org.linlinjava.litemall.admin.service.LogHelper;
+import org.linlinjava.litemall.admin.util.Permission;
+import org.linlinjava.litemall.admin.util.PermissionUtil;
+import org.linlinjava.litemall.core.util.IpUtil;
+import org.linlinjava.litemall.core.util.JacksonUtil;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.db.domain.LitemallAdmin;
+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.ApplicationContext;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.time.LocalDateTime;
+import java.util.*;
+
+import static org.linlinjava.litemall.admin.util.AdminResponseCode.ADMIN_INVALID_ACCOUNT;
+
+@RestController
+@RequestMapping("/admin/auth")
+@Validated
+public class AdminAuthController {
+    private final Log logger = LogFactory.getLog(AdminAuthController.class);
+
+    @Autowired
+    private LitemallAdminService adminService;
+    @Autowired
+    private LitemallRoleService roleService;
+    @Autowired
+    private LitemallPermissionService permissionService;
+    @Autowired
+    private LogHelper logHelper;
+
+    /*
+     *  { username : value, password : value }
+     */
+    @PostMapping("/login")
+    public Object login(@RequestBody String body, HttpServletRequest request) {
+        String username = JacksonUtil.parseString(body, "username");
+        String password = JacksonUtil.parseString(body, "password");
+
+        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
+            return ResponseUtil.badArgument();
+        }
+
+        Subject currentUser = SecurityUtils.getSubject();
+        try {
+            currentUser.login(new UsernamePasswordToken(username, password));
+        } catch (UnknownAccountException uae) {
+            logHelper.logAuthFail("登录", "用户帐号或密码不正确");
+            return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "用户帐号或密码不正确");
+        } catch (LockedAccountException lae) {
+            logHelper.logAuthFail("登录", "用户帐号已锁定不可用");
+            return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "用户帐号已锁定不可用");
+
+        } catch (AuthenticationException ae) {
+            logHelper.logAuthFail("登录", "认证失败");
+            return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "认证失败");
+        }
+
+        currentUser = SecurityUtils.getSubject();
+        LitemallAdmin admin = (LitemallAdmin) currentUser.getPrincipal();
+        admin.setLastLoginIp(IpUtil.getIpAddr(request));
+        admin.setLastLoginTime(LocalDateTime.now());
+        adminService.updateById(admin);
+
+        logHelper.logAuthSucceed("登录");
+
+        // userInfo
+        Map<String, Object> adminInfo = new HashMap<String, Object>();
+        adminInfo.put("nickName", admin.getUsername());
+        adminInfo.put("avatar", admin.getAvatar());
+
+        Map<Object, Object> result = new HashMap<Object, Object>();
+        result.put("token", currentUser.getSession().getId());
+        result.put("adminInfo", adminInfo);
+        return ResponseUtil.ok(result);
+    }
+
+    /*
+     *
+     */
+    @RequiresAuthentication
+    @PostMapping("/logout")
+    public Object logout() {
+        Subject currentUser = SecurityUtils.getSubject();
+
+        logHelper.logAuthSucceed("退出");
+        currentUser.logout();
+        return ResponseUtil.ok();
+    }
+
+
+    @RequiresAuthentication
+    @GetMapping("/info")
+    public Object info() {
+        Subject currentUser = SecurityUtils.getSubject();
+        LitemallAdmin admin = (LitemallAdmin) currentUser.getPrincipal();
+
+        Map<String, Object> data = new HashMap<>();
+        data.put("name", admin.getUsername());
+        data.put("avatar", admin.getAvatar());
+
+        Integer[] roleIds = admin.getRoleIds();
+        Set<String> roles = roleService.queryByIds(roleIds);
+        Set<String> permissions = permissionService.queryByRoleIds(roleIds);
+        data.put("roles", roles);
+        // NOTE
+        // 这里需要转换perms结构,因为对于前端而已API形式的权限更容易理解
+        data.put("perms", toApi(permissions));
+        return ResponseUtil.ok(data);
+    }
+
+    @Autowired
+    private ApplicationContext context;
+    private HashMap<String, String> systemPermissionsMap = null;
+
+    private Collection<String> toApi(Set<String> permissions) {
+        if (systemPermissionsMap == null) {
+            systemPermissionsMap = new HashMap<>();
+            final String basicPackage = "org.linlinjava.litemall.admin";
+            List<Permission> systemPermissions = PermissionUtil.listPermission(context, basicPackage);
+            for (Permission permission : systemPermissions) {
+                String perm = permission.getRequiresPermissions().value()[0];
+                String api = permission.getApi();
+                systemPermissionsMap.put(perm, api);
+            }
+        }
+
+        Collection<String> apis = new HashSet<>();
+        for (String perm : permissions) {
+            String api = systemPermissionsMap.get(perm);
+            apis.add(api);
+
+            if (perm.equals("*")) {
+                apis.clear();
+                apis.add("*");
+                return apis;
+                //                return systemPermissionsMap.values();
+
+            }
+        }
+        return apis;
+    }
+
+    @GetMapping("/401")
+    public Object page401() {
+        return ResponseUtil.unlogin();
+    }
+
+    @GetMapping("/index")
+    public Object pageIndex() {
+        return ResponseUtil.ok();
+    }
+
+    @GetMapping("/403")
+    public Object page403() {
+        return ResponseUtil.unauthz();
+    }
+}

+ 106 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminBrandController.java

@@ -0,0 +1,106 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallBrand;
+import org.linlinjava.litemall.db.service.LitemallBrandService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/brand")
+@Validated
+public class AdminBrandController {
+    private final Log logger = LogFactory.getLog(AdminBrandController.class);
+
+    @Autowired
+    private LitemallBrandService brandService;
+
+    @RequiresPermissions("admin:brand:list")
+    @RequiresPermissionsDesc(menu = {"商场管理", "品牌管理"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(String id, String name,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallBrand> brandList = brandService.querySelective(id, name, page, limit, sort, order);
+        return ResponseUtil.okList(brandList);
+    }
+
+    private Object validate(LitemallBrand brand) {
+        String name = brand.getName();
+        if (StringUtils.isEmpty(name)) {
+            return ResponseUtil.badArgument();
+        }
+
+        String desc = brand.getDesc();
+        if (StringUtils.isEmpty(desc)) {
+            return ResponseUtil.badArgument();
+        }
+
+        BigDecimal price = brand.getFloorPrice();
+        if (price == null) {
+            return ResponseUtil.badArgument();
+        }
+        return null;
+    }
+
+    @RequiresPermissions("admin:brand:create")
+    @RequiresPermissionsDesc(menu = {"商场管理", "品牌管理"}, button = "添加")
+    @PostMapping("/create")
+    public Object create(@RequestBody LitemallBrand brand) {
+        Object error = validate(brand);
+        if (error != null) {
+            return error;
+        }
+        brandService.add(brand);
+        return ResponseUtil.ok(brand);
+    }
+
+    @RequiresPermissions("admin:brand:read")
+    @RequiresPermissionsDesc(menu = {"商场管理", "品牌管理"}, button = "详情")
+    @GetMapping("/read")
+    public Object read(@NotNull Integer id) {
+        LitemallBrand brand = brandService.findById(id);
+        return ResponseUtil.ok(brand);
+    }
+
+    @RequiresPermissions("admin:brand:update")
+    @RequiresPermissionsDesc(menu = {"商场管理", "品牌管理"}, button = "编辑")
+    @PostMapping("/update")
+    public Object update(@RequestBody LitemallBrand brand) {
+        Object error = validate(brand);
+        if (error != null) {
+            return error;
+        }
+        if (brandService.updateById(brand) == 0) {
+            return ResponseUtil.updatedDataFailed();
+        }
+        return ResponseUtil.ok(brand);
+    }
+
+    @RequiresPermissions("admin:brand:delete")
+    @RequiresPermissionsDesc(menu = {"商场管理", "品牌管理"}, button = "删除")
+    @PostMapping("/delete")
+    public Object delete(@RequestBody LitemallBrand brand) {
+        Integer id = brand.getId();
+        if (id == null) {
+            return ResponseUtil.badArgument();
+        }
+        brandService.deleteById(id);
+        return ResponseUtil.ok();
+    }
+
+}

+ 153 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCategoryController.java

@@ -0,0 +1,153 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.admin.vo.CategoryVo;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.db.domain.LitemallCategory;
+import org.linlinjava.litemall.db.service.LitemallCategoryService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotNull;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/admin/category")
+@Validated
+public class AdminCategoryController {
+    private final Log logger = LogFactory.getLog(AdminCategoryController.class);
+
+    @Autowired
+    private LitemallCategoryService categoryService;
+
+    @RequiresPermissions("admin:category:list")
+    @RequiresPermissionsDesc(menu = {"商场管理", "类目管理"}, button = "查询")
+    @GetMapping("/list")
+    public Object list() {
+        List<CategoryVo> categoryVoList = new ArrayList<>();
+
+        List<LitemallCategory> categoryList = categoryService.queryByPid(0);
+        for (LitemallCategory category : categoryList) {
+            CategoryVo categoryVO = new CategoryVo();
+            categoryVO.setId(category.getId());
+            categoryVO.setDesc(category.getDesc());
+            categoryVO.setIconUrl(category.getIconUrl());
+            categoryVO.setPicUrl(category.getPicUrl());
+            categoryVO.setKeywords(category.getKeywords());
+            categoryVO.setName(category.getName());
+            categoryVO.setLevel(category.getLevel());
+
+            List<CategoryVo> children = new ArrayList<>();
+            List<LitemallCategory> subCategoryList = categoryService.queryByPid(category.getId());
+            for (LitemallCategory subCategory : subCategoryList) {
+                CategoryVo subCategoryVo = new CategoryVo();
+                subCategoryVo.setId(subCategory.getId());
+                subCategoryVo.setDesc(subCategory.getDesc());
+                subCategoryVo.setIconUrl(subCategory.getIconUrl());
+                subCategoryVo.setPicUrl(subCategory.getPicUrl());
+                subCategoryVo.setKeywords(subCategory.getKeywords());
+                subCategoryVo.setName(subCategory.getName());
+                subCategoryVo.setLevel(subCategory.getLevel());
+
+                children.add(subCategoryVo);
+            }
+
+            categoryVO.setChildren(children);
+            categoryVoList.add(categoryVO);
+        }
+
+        return ResponseUtil.okList(categoryVoList);
+    }
+
+    private Object validate(LitemallCategory category) {
+        String name = category.getName();
+        if (StringUtils.isEmpty(name)) {
+            return ResponseUtil.badArgument();
+        }
+
+        String level = category.getLevel();
+        if (StringUtils.isEmpty(level)) {
+            return ResponseUtil.badArgument();
+        }
+        if (!level.equals("L1") && !level.equals("L2")) {
+            return ResponseUtil.badArgumentValue();
+        }
+
+        Integer pid = category.getPid();
+        if (level.equals("L2") && (pid == null)) {
+            return ResponseUtil.badArgument();
+        }
+
+        return null;
+    }
+
+    @RequiresPermissions("admin:category:create")
+    @RequiresPermissionsDesc(menu = {"商场管理", "类目管理"}, button = "添加")
+    @PostMapping("/create")
+    public Object create(@RequestBody LitemallCategory category) {
+        Object error = validate(category);
+        if (error != null) {
+            return error;
+        }
+        categoryService.add(category);
+        return ResponseUtil.ok(category);
+    }
+
+    @RequiresPermissions("admin:category:read")
+    @RequiresPermissionsDesc(menu = {"商场管理", "类目管理"}, button = "详情")
+    @GetMapping("/read")
+    public Object read(@NotNull Integer id) {
+        LitemallCategory category = categoryService.findById(id);
+        return ResponseUtil.ok(category);
+    }
+
+    @RequiresPermissions("admin:category:update")
+    @RequiresPermissionsDesc(menu = {"商场管理", "类目管理"}, button = "编辑")
+    @PostMapping("/update")
+    public Object update(@RequestBody LitemallCategory category) {
+        Object error = validate(category);
+        if (error != null) {
+            return error;
+        }
+
+        if (categoryService.updateById(category) == 0) {
+            return ResponseUtil.updatedDataFailed();
+        }
+        return ResponseUtil.ok();
+    }
+
+    @RequiresPermissions("admin:category:delete")
+    @RequiresPermissionsDesc(menu = {"商场管理", "类目管理"}, button = "删除")
+    @PostMapping("/delete")
+    public Object delete(@RequestBody LitemallCategory category) {
+        Integer id = category.getId();
+        if (id == null) {
+            return ResponseUtil.badArgument();
+        }
+        categoryService.deleteById(id);
+        return ResponseUtil.ok();
+    }
+
+    @RequiresPermissions("admin:category:list")
+    @GetMapping("/l1")
+    public Object catL1() {
+        // 所有一级分类目录
+        List<LitemallCategory> l1CatList = categoryService.queryL1();
+        List<Map<String, Object>> data = new ArrayList<>(l1CatList.size());
+        for (LitemallCategory category : l1CatList) {
+            Map<String, Object> d = new HashMap<>(2);
+            d.put("value", category.getId());
+            d.put("label", category.getName());
+            data.add(d);
+        }
+        return ResponseUtil.okList(data);
+    }
+}

+ 42 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCollectController.java

@@ -0,0 +1,42 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallCollect;
+import org.linlinjava.litemall.db.service.LitemallCollectService;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/collect")
+@Validated
+public class AdminCollectController {
+    private final Log logger = LogFactory.getLog(AdminCollectController.class);
+
+    @Autowired
+    private LitemallCollectService collectService;
+
+
+    @RequiresPermissions("admin:collect:list")
+    @RequiresPermissionsDesc(menu = {"用户管理", "用户收藏"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(String userId, String valueId,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallCollect> collectList = collectService.querySelective(userId, valueId, page, limit, sort, order);
+        return ResponseUtil.okList(collectList);
+    }
+}

+ 51 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCommentController.java

@@ -0,0 +1,51 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallComment;
+import org.linlinjava.litemall.db.service.LitemallCommentService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/comment")
+@Validated
+public class AdminCommentController {
+    private final Log logger = LogFactory.getLog(AdminCommentController.class);
+
+    @Autowired
+    private LitemallCommentService commentService;
+
+    @RequiresPermissions("admin:comment:list")
+    @RequiresPermissionsDesc(menu = {"商品管理", "评论管理"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(String userId, String valueId,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallComment> commentList = commentService.querySelective(userId, valueId, page, limit, sort, order);
+        return ResponseUtil.okList(commentList);
+    }
+
+    @RequiresPermissions("admin:comment:delete")
+    @RequiresPermissionsDesc(menu = {"商品管理", "评论管理"}, button = "删除")
+    @PostMapping("/delete")
+    public Object delete(@RequestBody LitemallComment comment) {
+        Integer id = comment.getId();
+        if (id == null) {
+            return ResponseUtil.badArgument();
+        }
+        commentService.deleteById(id);
+        return ResponseUtil.ok();
+    }
+
+}

+ 96 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminConfigController.java

@@ -0,0 +1,96 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.system.SystemConfig;
+import org.linlinjava.litemall.core.util.JacksonUtil;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.db.service.LitemallSystemConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+@RestController
+@RequestMapping("/admin/config")
+@Validated
+public class AdminConfigController {
+    private final Log logger = LogFactory.getLog(AdminConfigController.class);
+
+    @Autowired
+    private LitemallSystemConfigService systemConfigService;
+
+    @RequiresPermissions("admin:config:mall:list")
+    @RequiresPermissionsDesc(menu = {"配置管理", "商场配置"}, button = "详情")
+    @GetMapping("/mall")
+    public Object listMall() {
+        Map<String, String> data = systemConfigService.listMail();
+        return ResponseUtil.ok(data);
+    }
+
+    @RequiresPermissions("admin:config:mall:updateConfigs")
+    @RequiresPermissionsDesc(menu = {"配置管理", "商场配置"}, button = "编辑")
+    @PostMapping("/mall")
+    public Object updateMall(@RequestBody String body) {
+        Map<String, String> data = JacksonUtil.toMap(body);
+        systemConfigService.updateConfig(data);
+        SystemConfig.updateConfigs(data);
+        return ResponseUtil.ok();
+    }
+
+    @RequiresPermissions("admin:config:express:list")
+    @RequiresPermissionsDesc(menu = {"配置管理", "运费配置"}, button = "详情")
+    @GetMapping("/express")
+    public Object listExpress() {
+        Map<String, String> data = systemConfigService.listExpress();
+        return ResponseUtil.ok(data);
+    }
+
+    @RequiresPermissions("admin:config:express:updateConfigs")
+    @RequiresPermissionsDesc(menu = {"配置管理", "运费配置"}, button = "编辑")
+    @PostMapping("/express")
+    public Object updateExpress(@RequestBody String body) {
+        Map<String, String> data = JacksonUtil.toMap(body);
+        systemConfigService.updateConfig(data);
+        SystemConfig.updateConfigs(data);
+        return ResponseUtil.ok();
+    }
+
+    @RequiresPermissions("admin:config:order:list")
+    @RequiresPermissionsDesc(menu = {"配置管理", "订单配置"}, button = "详情")
+    @GetMapping("/order")
+    public Object lisOrder() {
+        Map<String, String> data = systemConfigService.listOrder();
+        return ResponseUtil.ok(data);
+    }
+
+    @RequiresPermissions("admin:config:order:updateConfigs")
+    @RequiresPermissionsDesc(menu = {"配置管理", "订单配置"}, button = "编辑")
+    @PostMapping("/order")
+    public Object updateOrder(@RequestBody String body) {
+        Map<String, String> data = JacksonUtil.toMap(body);
+        systemConfigService.updateConfig(data);
+        return ResponseUtil.ok();
+    }
+
+    @RequiresPermissions("admin:config:wx:list")
+    @RequiresPermissionsDesc(menu = {"配置管理", "小程序配置"}, button = "详情")
+    @GetMapping("/wx")
+    public Object listWx() {
+        Map<String, String> data = systemConfigService.listWx();
+        return ResponseUtil.ok(data);
+    }
+
+    @RequiresPermissions("admin:config:wx:updateConfigs")
+    @RequiresPermissionsDesc(menu = {"配置管理", "小程序配置"}, button = "编辑")
+    @PostMapping("/wx")
+    public Object updateWx(@RequestBody String body) {
+        Map<String, String> data = JacksonUtil.toMap(body);
+        systemConfigService.updateConfig(data);
+        SystemConfig.updateConfigs(data);
+        return ResponseUtil.ok();
+    }
+}

+ 116 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCouponController.java

@@ -0,0 +1,116 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallCoupon;
+import org.linlinjava.litemall.db.domain.LitemallCouponUser;
+import org.linlinjava.litemall.db.service.LitemallCouponService;
+import org.linlinjava.litemall.db.service.LitemallCouponUserService;
+import org.linlinjava.litemall.db.util.CouponConstant;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/coupon")
+@Validated
+public class AdminCouponController {
+    private final Log logger = LogFactory.getLog(AdminCouponController.class);
+
+    @Autowired
+    private LitemallCouponService couponService;
+    @Autowired
+    private LitemallCouponUserService couponUserService;
+
+    @RequiresPermissions("admin:coupon:list")
+    @RequiresPermissionsDesc(menu = {"推广管理", "优惠券管理"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(String name, Short type, Short status,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallCoupon> couponList = couponService.querySelective(name, type, status, page, limit, sort, order);
+        return ResponseUtil.okList(couponList);
+    }
+
+    @RequiresPermissions("admin:coupon:listuser")
+    @RequiresPermissionsDesc(menu = {"推广管理", "优惠券管理"}, button = "查询用户")
+    @GetMapping("/listuser")
+    public Object listuser(Integer userId, Integer couponId, Short status,
+                           @RequestParam(defaultValue = "1") Integer page,
+                           @RequestParam(defaultValue = "10") Integer limit,
+                           @Sort @RequestParam(defaultValue = "add_time") String sort,
+                           @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallCouponUser> couponList = couponUserService.queryList(userId, couponId, status, page,
+                limit, sort, order);
+        return ResponseUtil.okList(couponList);
+    }
+
+    private Object validate(LitemallCoupon coupon) {
+        String name = coupon.getName();
+        if (StringUtils.isEmpty(name)) {
+            return ResponseUtil.badArgument();
+        }
+        return null;
+    }
+
+    @RequiresPermissions("admin:coupon:create")
+    @RequiresPermissionsDesc(menu = {"推广管理", "优惠券管理"}, button = "添加")
+    @PostMapping("/create")
+    public Object create(@RequestBody LitemallCoupon coupon) {
+        Object error = validate(coupon);
+        if (error != null) {
+            return error;
+        }
+
+        // 如果是兑换码类型,则这里需要生存一个兑换码
+        if (coupon.getType().equals(CouponConstant.TYPE_CODE)) {
+            String code = couponService.generateCode();
+            coupon.setCode(code);
+        }
+
+        couponService.add(coupon);
+        return ResponseUtil.ok(coupon);
+    }
+
+    @RequiresPermissions("admin:coupon:read")
+    @RequiresPermissionsDesc(menu = {"推广管理", "优惠券管理"}, button = "详情")
+    @GetMapping("/read")
+    public Object read(@NotNull Integer id) {
+        LitemallCoupon coupon = couponService.findById(id);
+        return ResponseUtil.ok(coupon);
+    }
+
+    @RequiresPermissions("admin:coupon:update")
+    @RequiresPermissionsDesc(menu = {"推广管理", "优惠券管理"}, button = "编辑")
+    @PostMapping("/update")
+    public Object update(@RequestBody LitemallCoupon coupon) {
+        Object error = validate(coupon);
+        if (error != null) {
+            return error;
+        }
+        if (couponService.updateById(coupon) == 0) {
+            return ResponseUtil.updatedDataFailed();
+        }
+        return ResponseUtil.ok(coupon);
+    }
+
+    @RequiresPermissions("admin:coupon:delete")
+    @RequiresPermissionsDesc(menu = {"推广管理", "优惠券管理"}, button = "删除")
+    @PostMapping("/delete")
+    public Object delete(@RequestBody LitemallCoupon coupon) {
+        couponService.deleteById(coupon.getId());
+        return ResponseUtil.ok();
+    }
+
+}

+ 49 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminDashbordController.java

@@ -0,0 +1,49 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.db.service.LitemallGoodsProductService;
+import org.linlinjava.litemall.db.service.LitemallGoodsService;
+import org.linlinjava.litemall.db.service.LitemallOrderService;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/admin/dashboard")
+@Validated
+public class AdminDashbordController {
+    private final Log logger = LogFactory.getLog(AdminDashbordController.class);
+
+    @Autowired
+    private LitemallUserService userService;
+    @Autowired
+    private LitemallGoodsService goodsService;
+    @Autowired
+    private LitemallGoodsProductService productService;
+    @Autowired
+    private LitemallOrderService orderService;
+
+    @GetMapping("")
+    public Object info() {
+        int userTotal = userService.count();
+        int goodsTotal = goodsService.count();
+        int productTotal = productService.count();
+        int orderTotal = orderService.count();
+        Map<String, Integer> data = new HashMap<>();
+        data.put("userTotal", userTotal);
+        data.put("goodsTotal", goodsTotal);
+        data.put("productTotal", productTotal);
+        data.put("orderTotal", orderTotal);
+
+        return ResponseUtil.ok(data);
+    }
+
+}

+ 46 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminFeedbackController.java

@@ -0,0 +1,46 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallFeedback;
+import org.linlinjava.litemall.db.service.LitemallFeedbackService;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * @author Yogeek
+ * @date 2018/8/26 1:11
+ */
+@RestController
+@RequestMapping("/admin/feedback")
+@Validated
+public class AdminFeedbackController {
+    private final Log logger = LogFactory.getLog(AdminFeedbackController.class);
+
+    @Autowired
+    private LitemallFeedbackService feedbackService;
+
+    @RequiresPermissions("admin:feedback:list")
+    @RequiresPermissionsDesc(menu = {"用户管理", "意见反馈"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(Integer userId, String username,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallFeedback> feedbackList = feedbackService.querySelective(userId, username, page, limit, sort,
+                order);
+        return ResponseUtil.okList(feedbackList);
+    }
+}

+ 42 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminFootprintController.java

@@ -0,0 +1,42 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallFootprint;
+import org.linlinjava.litemall.db.service.LitemallFootprintService;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/footprint")
+@Validated
+public class AdminFootprintController {
+    private final Log logger = LogFactory.getLog(AdminFootprintController.class);
+
+    @Autowired
+    private LitemallFootprintService footprintService;
+
+    @RequiresPermissions("admin:footprint:list")
+    @RequiresPermissionsDesc(menu = {"用户管理", "用户足迹"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(String userId, String goodsId,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallFootprint> footprintList = footprintService.querySelective(userId, goodsId, page, limit, sort,
+                order);
+        return ResponseUtil.okList(footprintList);
+    }
+}

+ 108 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGoodsController.java

@@ -0,0 +1,108 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.admin.dto.GoodsAllinone;
+import org.linlinjava.litemall.admin.service.AdminGoodsService;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallGoods;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotNull;
+
+@RestController
+@RequestMapping("/admin/goods")
+@Validated
+public class AdminGoodsController {
+    private final Log logger = LogFactory.getLog(AdminGoodsController.class);
+
+    @Autowired
+    private AdminGoodsService adminGoodsService;
+
+    /**
+     * 查询商品
+     *
+     * @param goodsId
+     * @param goodsSn
+     * @param name
+     * @param page
+     * @param limit
+     * @param sort
+     * @param order
+     * @return
+     */
+    @RequiresPermissions("admin:goods:list")
+    @RequiresPermissionsDesc(menu = {"商品管理", "商品管理"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(Integer goodsId, String goodsSn, String name,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        return adminGoodsService.list(goodsId, goodsSn, name, page, limit, sort, order);
+    }
+
+    @GetMapping("/catAndBrand")
+    public Object list2() {
+        return adminGoodsService.list2();
+    }
+
+    /**
+     * 编辑商品
+     *
+     * @param goodsAllinone
+     * @return
+     */
+    @RequiresPermissions("admin:goods:update")
+    @RequiresPermissionsDesc(menu = {"商品管理", "商品管理"}, button = "编辑")
+    @PostMapping("/update")
+    public Object update(@RequestBody GoodsAllinone goodsAllinone) {
+        return adminGoodsService.update(goodsAllinone);
+    }
+
+    /**
+     * 删除商品
+     *
+     * @param goods
+     * @return
+     */
+    @RequiresPermissions("admin:goods:delete")
+    @RequiresPermissionsDesc(menu = {"商品管理", "商品管理"}, button = "删除")
+    @PostMapping("/delete")
+    public Object delete(@RequestBody LitemallGoods goods) {
+        return adminGoodsService.delete(goods);
+    }
+
+    /**
+     * 添加商品
+     *
+     * @param goodsAllinone
+     * @return
+     */
+    @RequiresPermissions("admin:goods:create")
+    @RequiresPermissionsDesc(menu = {"商品管理", "商品管理"}, button = "上架")
+    @PostMapping("/create")
+    public Object create(@RequestBody GoodsAllinone goodsAllinone) {
+        return adminGoodsService.create(goodsAllinone);
+    }
+
+    /**
+     * 商品详情
+     *
+     * @param id
+     * @return
+     */
+    @RequiresPermissions("admin:goods:read")
+    @RequiresPermissionsDesc(menu = {"商品管理", "商品管理"}, button = "详情")
+    @GetMapping("/detail")
+    public Object detail(@NotNull Integer id) {
+        return adminGoodsService.detail(id);
+
+    }
+
+}

+ 190 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGrouponController.java

@@ -0,0 +1,190 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.admin.task.GrouponRuleExpiredTask;
+import org.linlinjava.litemall.admin.util.AdminResponseCode;
+import org.linlinjava.litemall.core.task.TaskService;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallGoods;
+import org.linlinjava.litemall.db.domain.LitemallGroupon;
+import org.linlinjava.litemall.db.domain.LitemallGrouponRules;
+import org.linlinjava.litemall.db.service.LitemallGoodsService;
+import org.linlinjava.litemall.db.service.LitemallGrouponRulesService;
+import org.linlinjava.litemall.db.service.LitemallGrouponService;
+import org.linlinjava.litemall.db.util.GrouponConstant;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.math.BigDecimal;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/admin/groupon")
+@Validated
+public class AdminGrouponController {
+    private final Log logger = LogFactory.getLog(AdminGrouponController.class);
+
+    @Autowired
+    private LitemallGrouponRulesService rulesService;
+    @Autowired
+    private LitemallGoodsService goodsService;
+    @Autowired
+    private LitemallGrouponService grouponService;
+    @Autowired
+    private TaskService taskService;
+
+    @RequiresPermissions("admin:groupon:read")
+    @RequiresPermissionsDesc(menu = {"推广管理", "团购管理"}, button = "详情")
+    @GetMapping("/listRecord")
+    public Object listRecord(String grouponRuleId,
+                             @RequestParam(defaultValue = "1") Integer page,
+                             @RequestParam(defaultValue = "10") Integer limit,
+                             @Sort @RequestParam(defaultValue = "add_time") String sort,
+                             @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallGroupon> grouponList = grouponService.querySelective(grouponRuleId, page, limit, sort, order);
+
+        List<Map<String, Object>> groupons = new ArrayList<>();
+        for (LitemallGroupon groupon : grouponList) {
+            try {
+                Map<String, Object> recordData = new HashMap<>();
+                List<LitemallGroupon> subGrouponList = grouponService.queryJoinRecord(groupon.getId());
+                LitemallGrouponRules rules = rulesService.findById(groupon.getRulesId());
+                LitemallGoods goods = goodsService.findById(rules.getGoodsId());
+
+                recordData.put("groupon", groupon);
+                recordData.put("subGroupons", subGrouponList);
+                recordData.put("rules", rules);
+                recordData.put("goods", goods);
+
+                groupons.add(recordData);
+            } catch (Exception e) {
+                logger.error(e.getMessage(), e);
+            }
+        }
+
+        return ResponseUtil.okList(groupons, grouponList);
+    }
+
+    @RequiresPermissions("admin:groupon:list")
+    @RequiresPermissionsDesc(menu = {"推广管理", "团购管理"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(String goodsId,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallGrouponRules> rulesList = rulesService.querySelective(goodsId, page, limit, sort, order);
+        return ResponseUtil.okList(rulesList);
+    }
+
+    private Object validate(LitemallGrouponRules grouponRules) {
+        Integer goodsId = grouponRules.getGoodsId();
+        if (goodsId == null) {
+            return ResponseUtil.badArgument();
+        }
+        BigDecimal discount = grouponRules.getDiscount();
+        if (discount == null) {
+            return ResponseUtil.badArgument();
+        }
+        Integer discountMember = grouponRules.getDiscountMember();
+        if (discountMember == null) {
+            return ResponseUtil.badArgument();
+        }
+        LocalDateTime expireTime = grouponRules.getExpireTime();
+        if (expireTime == null) {
+            return ResponseUtil.badArgument();
+        }
+
+        return null;
+    }
+
+    @RequiresPermissions("admin:groupon:update")
+    @RequiresPermissionsDesc(menu = {"推广管理", "团购管理"}, button = "编辑")
+    @PostMapping("/update")
+    public Object update(@RequestBody LitemallGrouponRules grouponRules) {
+        Object error = validate(grouponRules);
+        if (error != null) {
+            return error;
+        }
+
+        LitemallGrouponRules rules = rulesService.findById(grouponRules.getId());
+        if(rules == null){
+            return ResponseUtil.badArgumentValue();
+        }
+        if(!rules.getStatus().equals(GrouponConstant.RULE_STATUS_ON)){
+            return ResponseUtil.fail(AdminResponseCode.GROUPON_GOODS_OFFLINE, "团购已经下线");
+        }
+
+        Integer goodsId = grouponRules.getGoodsId();
+        LitemallGoods goods = goodsService.findById(goodsId);
+        if (goods == null) {
+            return ResponseUtil.badArgumentValue();
+        }
+
+        grouponRules.setGoodsName(goods.getName());
+        grouponRules.setPicUrl(goods.getPicUrl());
+
+        if (rulesService.updateById(grouponRules) == 0) {
+            return ResponseUtil.updatedDataFailed();
+        }
+
+        return ResponseUtil.ok();
+    }
+
+    @RequiresPermissions("admin:groupon:create")
+    @RequiresPermissionsDesc(menu = {"推广管理", "团购管理"}, button = "添加")
+    @PostMapping("/create")
+    public Object create(@RequestBody LitemallGrouponRules grouponRules) {
+        Object error = validate(grouponRules);
+        if (error != null) {
+            return error;
+        }
+
+        Integer goodsId = grouponRules.getGoodsId();
+        LitemallGoods goods = goodsService.findById(goodsId);
+        if (goods == null) {
+            return ResponseUtil.fail(AdminResponseCode.GROUPON_GOODS_UNKNOWN, "团购商品不存在");
+        }
+        if(rulesService.countByGoodsId(goodsId) > 0){
+            return ResponseUtil.fail(AdminResponseCode.GROUPON_GOODS_EXISTED, "团购商品已经存在");
+        }
+
+        grouponRules.setGoodsName(goods.getName());
+        grouponRules.setPicUrl(goods.getPicUrl());
+        grouponRules.setStatus(GrouponConstant.RULE_STATUS_ON);
+        rulesService.createRules(grouponRules);
+
+        LocalDateTime now = LocalDateTime.now();
+        LocalDateTime expire = grouponRules.getExpireTime();
+        long delay = ChronoUnit.MILLIS.between(now, expire);
+        // 团购过期任务
+        taskService.addTask(new GrouponRuleExpiredTask(grouponRules.getId(), delay));
+
+        return ResponseUtil.ok(grouponRules);
+    }
+
+    @RequiresPermissions("admin:groupon:delete")
+    @RequiresPermissionsDesc(menu = {"推广管理", "团购管理"}, button = "删除")
+    @PostMapping("/delete")
+    public Object delete(@RequestBody LitemallGrouponRules grouponRules) {
+        Integer id = grouponRules.getId();
+        if (id == null) {
+            return ResponseUtil.badArgument();
+        }
+
+        rulesService.delete(id);
+        return ResponseUtil.ok();
+    }
+}

+ 40 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminHistoryController.java

@@ -0,0 +1,40 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallSearchHistory;
+import org.linlinjava.litemall.db.service.LitemallSearchHistoryService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/history")
+public class AdminHistoryController {
+    private final Log logger = LogFactory.getLog(AdminHistoryController.class);
+
+    @Autowired
+    private LitemallSearchHistoryService searchHistoryService;
+
+    @RequiresPermissions("admin:history:list")
+    @RequiresPermissionsDesc(menu = {"用户管理", "搜索历史"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(String userId, String keyword,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallSearchHistory> historyList = searchHistoryService.querySelective(userId, keyword, page, limit,
+                sort, order);
+        return ResponseUtil.okList(historyList);
+    }
+}

+ 67 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminIndexController.java

@@ -0,0 +1,67 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.*;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/admin/index")
+public class AdminIndexController {
+    private final Log logger = LogFactory.getLog(AdminIndexController.class);
+
+    @RequestMapping("/index")
+    public Object index() {
+        return ResponseUtil.ok("hello world, this is admin service");
+    }
+
+    @RequiresGuest
+    @RequestMapping("/guest")
+    public Object guest() {
+        return ResponseUtil.ok("hello world, this is admin service");
+    }
+
+    @RequiresAuthentication
+    @RequestMapping("/authn")
+    public Object authn() {
+        return ResponseUtil.ok("hello world, this is admin service");
+    }
+
+    @RequiresUser
+    @RequestMapping("/user")
+    public Object user() {
+        return ResponseUtil.ok("hello world, this is admin service");
+    }
+
+    @RequiresRoles("admin")
+    @RequestMapping("/admin")
+    public Object admin() {
+        return ResponseUtil.ok("hello world, this is admin service");
+    }
+
+    @RequiresRoles("admin2")
+    @RequestMapping("/admin2")
+    public Object admin2() {
+        return ResponseUtil.ok("hello world, this is admin service");
+    }
+
+    @RequiresPermissions("index:permission:read")
+    @RequiresPermissionsDesc(menu = {"其他", "权限测试"}, button = "权限读")
+    @GetMapping("/read")
+    public Object read() {
+        return ResponseUtil.ok("hello world, this is admin service");
+    }
+
+    @RequiresPermissions("index:permission:write")
+    @RequiresPermissionsDesc(menu = {"其他", "权限测试"}, button = "权限写")
+    @PostMapping("/write")
+    public Object write() {
+        return ResponseUtil.ok("hello world, this is admin service");
+    }
+
+}

+ 99 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminIssueController.java

@@ -0,0 +1,99 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallIssue;
+import org.linlinjava.litemall.db.service.LitemallIssueService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/issue")
+@Validated
+public class AdminIssueController {
+    private final Log logger = LogFactory.getLog(AdminIssueController.class);
+
+    @Autowired
+    private LitemallIssueService issueService;
+
+    @RequiresPermissions("admin:issue:list")
+    @RequiresPermissionsDesc(menu = {"商场管理", "通用问题"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(String question,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallIssue> issueList = issueService.querySelective(question, page, limit, sort, order);
+        return ResponseUtil.okList(issueList);
+    }
+
+    private Object validate(LitemallIssue issue) {
+        String question = issue.getQuestion();
+        if (StringUtils.isEmpty(question)) {
+            return ResponseUtil.badArgument();
+        }
+        String answer = issue.getAnswer();
+        if (StringUtils.isEmpty(answer)) {
+            return ResponseUtil.badArgument();
+        }
+        return null;
+    }
+
+    @RequiresPermissions("admin:issue:create")
+    @RequiresPermissionsDesc(menu = {"商场管理", "通用问题"}, button = "添加")
+    @PostMapping("/create")
+    public Object create(@RequestBody LitemallIssue issue) {
+        Object error = validate(issue);
+        if (error != null) {
+            return error;
+        }
+        issueService.add(issue);
+        return ResponseUtil.ok(issue);
+    }
+
+    @RequiresPermissions("admin:issue:read")
+    @GetMapping("/read")
+    public Object read(@NotNull Integer id) {
+        LitemallIssue issue = issueService.findById(id);
+        return ResponseUtil.ok(issue);
+    }
+
+    @RequiresPermissions("admin:issue:update")
+    @RequiresPermissionsDesc(menu = {"商场管理", "通用问题"}, button = "编辑")
+    @PostMapping("/update")
+    public Object update(@RequestBody LitemallIssue issue) {
+        Object error = validate(issue);
+        if (error != null) {
+            return error;
+        }
+        if (issueService.updateById(issue) == 0) {
+            return ResponseUtil.updatedDataFailed();
+        }
+
+        return ResponseUtil.ok(issue);
+    }
+
+    @RequiresPermissions("admin:issue:delete")
+    @RequiresPermissionsDesc(menu = {"商场管理", "通用问题"}, button = "删除")
+    @PostMapping("/delete")
+    public Object delete(@RequestBody LitemallIssue issue) {
+        Integer id = issue.getId();
+        if (id == null) {
+            return ResponseUtil.badArgument();
+        }
+        issueService.deleteById(id);
+        return ResponseUtil.ok();
+    }
+
+}

+ 95 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminKeywordController.java

@@ -0,0 +1,95 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallKeyword;
+import org.linlinjava.litemall.db.service.LitemallKeywordService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/keyword")
+@Validated
+public class AdminKeywordController {
+    private final Log logger = LogFactory.getLog(AdminKeywordController.class);
+
+    @Autowired
+    private LitemallKeywordService keywordService;
+
+    @RequiresPermissions("admin:keyword:list")
+    @RequiresPermissionsDesc(menu = {"商场管理", "关键词"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(String keyword, String url,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallKeyword> keywordList = keywordService.querySelective(keyword, url, page, limit, sort, order);
+        return ResponseUtil.okList(keywordList);
+    }
+
+    private Object validate(LitemallKeyword keywords) {
+        String keyword = keywords.getKeyword();
+        if (StringUtils.isEmpty(keyword)) {
+            return ResponseUtil.badArgument();
+        }
+        return null;
+    }
+
+    @RequiresPermissions("admin:keyword:create")
+    @RequiresPermissionsDesc(menu = {"商场管理", "关键词"}, button = "添加")
+    @PostMapping("/create")
+    public Object create(@RequestBody LitemallKeyword keyword) {
+        Object error = validate(keyword);
+        if (error != null) {
+            return error;
+        }
+        keywordService.add(keyword);
+        return ResponseUtil.ok(keyword);
+    }
+
+    @RequiresPermissions("admin:keyword:read")
+    @RequiresPermissionsDesc(menu = {"商场管理", "关键词"}, button = "详情")
+    @GetMapping("/read")
+    public Object read(@NotNull Integer id) {
+        LitemallKeyword keyword = keywordService.findById(id);
+        return ResponseUtil.ok(keyword);
+    }
+
+    @RequiresPermissions("admin:keyword:update")
+    @RequiresPermissionsDesc(menu = {"商场管理", "关键词"}, button = "编辑")
+    @PostMapping("/update")
+    public Object update(@RequestBody LitemallKeyword keyword) {
+        Object error = validate(keyword);
+        if (error != null) {
+            return error;
+        }
+        if (keywordService.updateById(keyword) == 0) {
+            return ResponseUtil.updatedDataFailed();
+        }
+        return ResponseUtil.ok(keyword);
+    }
+
+    @RequiresPermissions("admin:keyword:delete")
+    @RequiresPermissionsDesc(menu = {"商场管理", "关键词"}, button = "删除")
+    @PostMapping("/delete")
+    public Object delete(@RequestBody LitemallKeyword keyword) {
+        Integer id = keyword.getId();
+        if (id == null) {
+            return ResponseUtil.badArgument();
+        }
+        keywordService.deleteById(id);
+        return ResponseUtil.ok();
+    }
+
+}

+ 41 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminLogController.java

@@ -0,0 +1,41 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallLog;
+import org.linlinjava.litemall.db.service.LitemallLogService;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/log")
+@Validated
+public class AdminLogController {
+    private final Log logger = LogFactory.getLog(AdminLogController.class);
+
+    @Autowired
+    private LitemallLogService logService;
+
+    @RequiresPermissions("admin:log:list")
+    @RequiresPermissionsDesc(menu = {"系统管理", "操作日志"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(String name,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallLog> logList = logService.querySelective(name, page, limit, sort, order);
+        return ResponseUtil.okList(logList);
+    }
+}

+ 153 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminNoticeController.java

@@ -0,0 +1,153 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.apache.shiro.subject.Subject;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.util.JacksonUtil;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.*;
+import org.linlinjava.litemall.db.service.LitemallAdminService;
+import org.linlinjava.litemall.db.service.LitemallNoticeAdminService;
+import org.linlinjava.litemall.db.service.LitemallNoticeService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotNull;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.linlinjava.litemall.admin.util.AdminResponseCode.NOTICE_UPDATE_NOT_ALLOWED;
+
+@RestController
+@RequestMapping("/admin/notice")
+@Validated
+public class AdminNoticeController {
+    private final Log logger = LogFactory.getLog(AdminNoticeController.class);
+
+    @Autowired
+    private LitemallNoticeService noticeService;
+    @Autowired
+    private LitemallAdminService adminService;
+    @Autowired
+    private LitemallNoticeAdminService noticeAdminService;
+
+    @RequiresPermissions("admin:notice:list")
+    @RequiresPermissionsDesc(menu = {"系统管理", "通知管理"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(String title, String content,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallNotice> noticeList = noticeService.querySelective(title, content, page, limit, sort, order);
+        return ResponseUtil.okList(noticeList);
+    }
+
+    private Object validate(LitemallNotice notice) {
+        String title = notice.getTitle();
+        if (StringUtils.isEmpty(title)) {
+            return ResponseUtil.badArgument();
+        }
+        return null;
+    }
+
+    private Integer getAdminId(){
+        Subject currentUser = SecurityUtils.getSubject();
+        LitemallAdmin admin = (LitemallAdmin) currentUser.getPrincipal();
+        return admin.getId();
+    }
+
+    @RequiresPermissions("admin:notice:create")
+    @RequiresPermissionsDesc(menu = {"推广管理", "通知管理"}, button = "添加")
+    @PostMapping("/create")
+    public Object create(@RequestBody LitemallNotice notice) {
+        Object error = validate(notice);
+        if (error != null) {
+            return error;
+        }
+        // 1. 添加通知记录
+        notice.setAdminId(getAdminId());
+        noticeService.add(notice);
+        // 2. 添加管理员通知记录
+        List<LitemallAdmin> adminList = adminService.all();
+        LitemallNoticeAdmin noticeAdmin = new LitemallNoticeAdmin();
+        noticeAdmin.setNoticeId(notice.getId());
+        noticeAdmin.setNoticeTitle(notice.getTitle());
+        for(LitemallAdmin admin : adminList){
+            noticeAdmin.setAdminId(admin.getId());
+            noticeAdminService.add(noticeAdmin);
+        }
+        return ResponseUtil.ok(notice);
+    }
+
+    @RequiresPermissions("admin:notice:read")
+    @RequiresPermissionsDesc(menu = {"推广管理", "通知管理"}, button = "详情")
+    @GetMapping("/read")
+    public Object read(@NotNull Integer id) {
+        LitemallNotice notice = noticeService.findById(id);
+        List<LitemallNoticeAdmin> noticeAdminList = noticeAdminService.queryByNoticeId(id);
+        Map<String, Object> data = new HashMap<>(2);
+        data.put("notice", notice);
+        data.put("noticeAdminList", noticeAdminList);
+        return ResponseUtil.ok(data);
+    }
+
+    @RequiresPermissions("admin:notice:update")
+    @RequiresPermissionsDesc(menu = {"推广管理", "通知管理"}, button = "编辑")
+    @PostMapping("/update")
+    public Object update(@RequestBody LitemallNotice notice) {
+        Object error = validate(notice);
+        if (error != null) {
+            return error;
+        }
+        LitemallNotice originalNotice = noticeService.findById(notice.getId());
+        if (originalNotice == null) {
+            return ResponseUtil.badArgument();
+        }
+        // 如果通知已经有人阅读过,则不支持编辑
+        if(noticeAdminService.countReadByNoticeId(notice.getId()) > 0){
+            return ResponseUtil.fail(NOTICE_UPDATE_NOT_ALLOWED, "通知已被阅读,不能重新编辑");
+        }
+        // 1. 更新通知记录
+        notice.setAdminId(getAdminId());
+        noticeService.updateById(notice);
+        // 2. 更新管理员通知记录
+        if(!originalNotice.getTitle().equals(notice.getTitle())){
+            LitemallNoticeAdmin noticeAdmin = new LitemallNoticeAdmin();
+            noticeAdmin.setNoticeTitle(notice.getTitle());
+            noticeAdminService.updateByNoticeId(noticeAdmin, notice.getId());
+        }
+        return ResponseUtil.ok(notice);
+    }
+
+    @RequiresPermissions("admin:notice:delete")
+    @RequiresPermissionsDesc(menu = {"推广管理", "通知管理"}, button = "删除")
+    @PostMapping("/delete")
+    public Object delete(@RequestBody LitemallNotice notice) {
+        // 1. 删除通知管理员记录
+        noticeAdminService.deleteByNoticeId(notice.getId());
+        // 2. 删除通知记录
+        noticeService.deleteById(notice.getId());
+        return ResponseUtil.ok();
+    }
+
+    @RequiresPermissions("admin:notice:batch-delete")
+    @RequiresPermissionsDesc(menu = {"推广管理", "通知管理"}, button = "批量删除")
+    @PostMapping("/batch-delete")
+    public Object batchDelete(@RequestBody String body) {
+        List<Integer> ids = JacksonUtil.parseIntegerList(body, "ids");
+        // 1. 删除通知管理员记录
+        noticeAdminService.deleteByNoticeIds(ids);
+        // 2. 删除通知记录
+        noticeService.deleteByIds(ids);
+        return ResponseUtil.ok();
+    }
+}

+ 134 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminOrderController.java

@@ -0,0 +1,134 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.admin.service.AdminOrderService;
+import org.linlinjava.litemall.core.express.ExpressService;
+import org.linlinjava.litemall.core.notify.NotifyService;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/order")
+@Validated
+public class AdminOrderController {
+    private final Log logger = LogFactory.getLog(AdminOrderController.class);
+
+    @Autowired
+    private AdminOrderService adminOrderService;
+    @Autowired
+    private ExpressService expressService;
+
+    /**
+     * 查询订单
+     *
+     * @param userId
+     * @param orderSn
+     * @param orderStatusArray
+     * @param page
+     * @param limit
+     * @param sort
+     * @param order
+     * @return
+     */
+    @RequiresPermissions("admin:order:list")
+    @RequiresPermissionsDesc(menu = {"商场管理", "订单管理"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(Integer userId, 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<Short> orderStatusArray,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @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
+     */
+    @GetMapping("/channel")
+    public Object channel() {
+        return ResponseUtil.ok(expressService.getVendors());
+    }
+
+    /**
+     * 订单详情
+     *
+     * @param id
+     * @return
+     */
+    @RequiresPermissions("admin:order:read")
+    @RequiresPermissionsDesc(menu = {"商场管理", "订单管理"}, button = "详情")
+    @GetMapping("/detail")
+    public Object detail(@NotNull Integer id) {
+        return adminOrderService.detail(id);
+    }
+
+    /**
+     * 订单退款
+     *
+     * @param body 订单信息,{ orderId:xxx }
+     * @return 订单退款操作结果
+     */
+    @RequiresPermissions("admin:order:refund")
+    @RequiresPermissionsDesc(menu = {"商场管理", "订单管理"}, button = "订单退款")
+    @PostMapping("/refund")
+    public Object refund(@RequestBody String body) {
+        return adminOrderService.refund(body);
+    }
+
+    /**
+     * 发货
+     *
+     * @param body 订单信息,{ orderId:xxx, shipSn: xxx, shipChannel: xxx }
+     * @return 订单操作结果
+     */
+    @RequiresPermissions("admin:order:ship")
+    @RequiresPermissionsDesc(menu = {"商场管理", "订单管理"}, button = "订单发货")
+    @PostMapping("/ship")
+    public Object ship(@RequestBody String body) {
+        return adminOrderService.ship(body);
+    }
+
+
+    /**
+     * 删除订单
+     *
+     * @param body 订单信息,{ orderId:xxx }
+     * @return 订单操作结果
+     */
+    @RequiresPermissions("admin:order:delete")
+    @RequiresPermissionsDesc(menu = {"商场管理", "订单管理"}, button = "订单删除")
+    @PostMapping("/delete")
+    public Object delete(@RequestBody String body) {
+        return adminOrderService.delete(body);
+    }
+
+    /**
+     * 回复订单商品
+     *
+     * @param body 订单信息,{ orderId:xxx }
+     * @return 订单操作结果
+     */
+    @RequiresPermissions("admin:order:reply")
+    @RequiresPermissionsDesc(menu = {"商场管理", "订单管理"}, button = "订单商品回复")
+    @PostMapping("/reply")
+    public Object reply(@RequestBody String body) {
+        return adminOrderService.reply(body);
+    }
+}

+ 159 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminProfileController.java

@@ -0,0 +1,159 @@
+package org.linlinjava.litemall.admin.web;
+
+import io.swagger.models.auth.In;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authz.annotation.RequiresAuthentication;
+import org.apache.shiro.subject.Subject;
+import org.linlinjava.litemall.core.util.JacksonUtil;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.util.bcrypt.BCryptPasswordEncoder;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallAdmin;
+import org.linlinjava.litemall.db.domain.LitemallIssue;
+import org.linlinjava.litemall.db.domain.LitemallNotice;
+import org.linlinjava.litemall.db.domain.LitemallNoticeAdmin;
+import org.linlinjava.litemall.db.service.LitemallAdminService;
+import org.linlinjava.litemall.db.service.LitemallNoticeAdminService;
+import org.linlinjava.litemall.db.service.LitemallNoticeService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.linlinjava.litemall.admin.util.AdminResponseCode.ADMIN_INVALID_ACCOUNT;
+
+@RestController
+@RequestMapping("/admin/profile")
+@Validated
+public class AdminProfileController {
+    private final Log logger = LogFactory.getLog(AdminProfileController.class);
+
+    @Autowired
+    private LitemallAdminService adminService;
+    @Autowired
+    private LitemallNoticeService noticeService;
+    @Autowired
+    private LitemallNoticeAdminService noticeAdminService;
+
+    @RequiresAuthentication
+    @PostMapping("/password")
+    public Object create(@RequestBody String body) {
+        String oldPassword = JacksonUtil.parseString(body, "oldPassword");
+        String newPassword = JacksonUtil.parseString(body, "newPassword");
+        if (StringUtils.isEmpty(oldPassword)) {
+            return ResponseUtil.badArgument();
+        }
+        if (StringUtils.isEmpty(newPassword)) {
+            return ResponseUtil.badArgument();
+        }
+
+        Subject currentUser = SecurityUtils.getSubject();
+        LitemallAdmin admin = (LitemallAdmin) currentUser.getPrincipal();
+
+        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+        if (!encoder.matches(oldPassword, admin.getPassword())) {
+            return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "账号密码不对");
+        }
+
+        String encodedNewPassword = encoder.encode(newPassword);
+        admin.setPassword(encodedNewPassword);
+
+        adminService.updateById(admin);
+        return ResponseUtil.ok();
+    }
+
+    private Integer getAdminId(){
+        Subject currentUser = SecurityUtils.getSubject();
+        LitemallAdmin admin = (LitemallAdmin) currentUser.getPrincipal();
+        return admin.getId();
+    }
+
+    @RequiresAuthentication
+    @GetMapping("/nnotice")
+    public Object nNotice() {
+        int count = noticeAdminService.countUnread(getAdminId());
+        return ResponseUtil.ok(count);
+    }
+
+    @RequiresAuthentication
+    @GetMapping("/lsnotice")
+    public Object lsNotice(String title, String type,
+                            @RequestParam(defaultValue = "1") Integer page,
+                            @RequestParam(defaultValue = "10") Integer limit,
+                            @Sort @RequestParam(defaultValue = "add_time") String sort,
+                            @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallNoticeAdmin> noticeList = noticeAdminService.querySelective(title, type, getAdminId(), page, limit, sort, order);
+        return ResponseUtil.okList(noticeList);
+    }
+
+    @RequiresAuthentication
+    @PostMapping("/catnotice")
+    public Object catNotice(@RequestBody String body) {
+        Integer noticeId = JacksonUtil.parseInteger(body, "noticeId");
+        if(noticeId == null){
+            return ResponseUtil.badArgument();
+        }
+
+        LitemallNoticeAdmin noticeAdmin = noticeAdminService.find(noticeId, getAdminId());
+        if(noticeAdmin == null){
+           return ResponseUtil.badArgumentValue();
+        }
+        // 更新通知记录中的时间
+        noticeAdmin.setReadTime(LocalDateTime.now());
+        noticeAdminService.update(noticeAdmin);
+
+        // 返回通知的相关信息
+        Map<String, Object> data = new HashMap<>();
+        LitemallNotice notice = noticeService.findById(noticeId);
+        data.put("title", notice.getTitle());
+        data.put("content", notice.getContent());
+        data.put("time", notice.getUpdateTime());
+        Integer adminId = notice.getAdminId();
+        if(adminId.equals(0)){
+            data.put("admin", "系统");
+        }
+        else{
+            LitemallAdmin admin = adminService.findById(notice.getAdminId());
+            data.put("admin", admin.getUsername());
+            data.put("avatar", admin.getAvatar());
+        }
+        return ResponseUtil.ok(data);
+    }
+
+    @RequiresAuthentication
+    @PostMapping("/bcatnotice")
+    public Object bcatNotice(@RequestBody String body) {
+        List<Integer> ids = JacksonUtil.parseIntegerList(body, "ids");
+        noticeAdminService.markReadByIds(ids, getAdminId());
+        return ResponseUtil.ok();
+    }
+
+    @RequiresAuthentication
+    @PostMapping("/rmnotice")
+    public Object rmNotice(@RequestBody String body) {
+        Integer id = JacksonUtil.parseInteger(body, "id");
+        if(id == null){
+            return ResponseUtil.badArgument();
+        }
+        noticeAdminService.deleteById(id, getAdminId());
+        return ResponseUtil.ok();
+    }
+
+    @RequiresAuthentication
+    @PostMapping("/brmnotice")
+    public Object brmNotice(@RequestBody String body) {
+        List<Integer> ids = JacksonUtil.parseIntegerList(body, "ids");
+        noticeAdminService.deleteByIds(ids, getAdminId());
+        return ResponseUtil.ok();
+    }
+
+}

+ 87 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminRegionController.java

@@ -0,0 +1,87 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.linlinjava.litemall.admin.vo.RegionVo;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.db.domain.LitemallRegion;
+import org.linlinjava.litemall.db.service.LitemallRegionService;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.constraints.NotNull;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@RestController
+@RequestMapping("/admin/region")
+@Validated
+public class AdminRegionController {
+    private final Log logger = LogFactory.getLog(AdminRegionController.class);
+
+    @Autowired
+    private LitemallRegionService regionService;
+
+    @GetMapping("/clist")
+    public Object clist(@NotNull Integer id) {
+        List<LitemallRegion> regionList = regionService.queryByPid(id);
+        return ResponseUtil.okList(regionList);
+    }
+
+    @GetMapping("/list")
+    public Object list() {
+        List<RegionVo> regionVoList = new ArrayList<>();
+
+        List<LitemallRegion> litemallRegions = regionService.getAll();
+        Map<Byte, List<LitemallRegion>> collect = litemallRegions.stream().collect(Collectors.groupingBy(LitemallRegion::getType));
+        byte provinceType = 1;
+        List<LitemallRegion> provinceList = collect.get(provinceType);
+        byte cityType = 2;
+        List<LitemallRegion> city = collect.get(cityType);
+        Map<Integer, List<LitemallRegion>> cityListMap = city.stream().collect(Collectors.groupingBy(LitemallRegion::getPid));
+        byte areaType = 3;
+        List<LitemallRegion> areas = collect.get(areaType);
+        Map<Integer, List<LitemallRegion>> areaListMap = areas.stream().collect(Collectors.groupingBy(LitemallRegion::getPid));
+
+        for (LitemallRegion province : provinceList) {
+            RegionVo provinceVO = new RegionVo();
+            provinceVO.setId(province.getId());
+            provinceVO.setName(province.getName());
+            provinceVO.setCode(province.getCode());
+            provinceVO.setType(province.getType());
+
+            List<LitemallRegion> cityList = cityListMap.get(province.getId());
+            List<RegionVo> cityVOList = new ArrayList<>();
+            for (LitemallRegion cityVo : cityList) {
+                RegionVo cityVO = new RegionVo();
+                cityVO.setId(cityVo.getId());
+                cityVO.setName(cityVo.getName());
+                cityVO.setCode(cityVo.getCode());
+                cityVO.setType(cityVo.getType());
+
+                List<LitemallRegion> areaList = areaListMap.get(cityVo.getId());
+                List<RegionVo> areaVOList = new ArrayList<>();
+                for (LitemallRegion area : areaList) {
+                    RegionVo areaVO = new RegionVo();
+                    areaVO.setId(area.getId());
+                    areaVO.setName(area.getName());
+                    areaVO.setCode(area.getCode());
+                    areaVO.setType(area.getType());
+                    areaVOList.add(areaVO);
+                }
+
+                cityVO.setChildren(areaVOList);
+                cityVOList.add(cityVO);
+            }
+            provinceVO.setChildren(cityVOList);
+            regionVoList.add(provinceVO);
+        }
+
+        return ResponseUtil.okList(regionVoList);
+    }
+}

+ 227 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminRoleController.java

@@ -0,0 +1,227 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.admin.util.AdminResponseCode;
+import org.linlinjava.litemall.admin.util.Permission;
+import org.linlinjava.litemall.admin.util.PermissionUtil;
+import org.linlinjava.litemall.admin.vo.PermVo;
+import org.linlinjava.litemall.core.util.JacksonUtil;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallAdmin;
+import org.linlinjava.litemall.db.domain.LitemallPermission;
+import org.linlinjava.litemall.db.domain.LitemallRole;
+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.ApplicationContext;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotNull;
+import java.util.*;
+
+import static org.linlinjava.litemall.admin.util.AdminResponseCode.ROLE_NAME_EXIST;
+import static org.linlinjava.litemall.admin.util.AdminResponseCode.ROLE_USER_EXIST;
+
+@RestController
+@RequestMapping("/admin/role")
+@Validated
+public class AdminRoleController {
+    private final Log logger = LogFactory.getLog(AdminRoleController.class);
+
+    @Autowired
+    private LitemallRoleService roleService;
+    @Autowired
+    private LitemallPermissionService permissionService;
+    @Autowired
+    private LitemallAdminService adminService;
+
+    @RequiresPermissions("admin:role:list")
+    @RequiresPermissionsDesc(menu = {"系统管理", "角色管理"}, button = "角色查询")
+    @GetMapping("/list")
+    public Object list(String name,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallRole> roleList = roleService.querySelective(name, page, limit, sort, order);
+        return ResponseUtil.okList(roleList);
+    }
+
+    @GetMapping("/options")
+    public Object options() {
+        List<LitemallRole> roleList = roleService.queryAll();
+
+        List<Map<String, Object>> options = new ArrayList<>(roleList.size());
+        for (LitemallRole role : roleList) {
+            Map<String, Object> option = new HashMap<>(2);
+            option.put("value", role.getId());
+            option.put("label", role.getName());
+            options.add(option);
+        }
+
+        return ResponseUtil.okList(options);
+    }
+
+    @RequiresPermissions("admin:role:read")
+    @RequiresPermissionsDesc(menu = {"系统管理", "角色管理"}, button = "角色详情")
+    @GetMapping("/read")
+    public Object read(@NotNull Integer id) {
+        LitemallRole role = roleService.findById(id);
+        return ResponseUtil.ok(role);
+    }
+
+
+    private Object validate(LitemallRole role) {
+        String name = role.getName();
+        if (StringUtils.isEmpty(name)) {
+            return ResponseUtil.badArgument();
+        }
+
+        return null;
+    }
+
+    @RequiresPermissions("admin:role:create")
+    @RequiresPermissionsDesc(menu = {"系统管理", "角色管理"}, button = "角色添加")
+    @PostMapping("/create")
+    public Object create(@RequestBody LitemallRole role) {
+        Object error = validate(role);
+        if (error != null) {
+            return error;
+        }
+
+        if (roleService.checkExist(role.getName())) {
+            return ResponseUtil.fail(ROLE_NAME_EXIST, "角色已经存在");
+        }
+
+        roleService.add(role);
+
+        return ResponseUtil.ok(role);
+    }
+
+    @RequiresPermissions("admin:role:update")
+    @RequiresPermissionsDesc(menu = {"系统管理", "角色管理"}, button = "角色编辑")
+    @PostMapping("/update")
+    public Object update(@RequestBody LitemallRole role) {
+        Object error = validate(role);
+        if (error != null) {
+            return error;
+        }
+
+        roleService.updateById(role);
+        return ResponseUtil.ok();
+    }
+
+    @RequiresPermissions("admin:role:delete")
+    @RequiresPermissionsDesc(menu = {"系统管理", "角色管理"}, button = "角色删除")
+    @PostMapping("/delete")
+    public Object delete(@RequestBody LitemallRole role) {
+        Integer id = role.getId();
+        if (id == null) {
+            return ResponseUtil.badArgument();
+        }
+
+        // 如果当前角色所对应管理员仍存在,则拒绝删除角色。
+        List<LitemallAdmin> adminList = adminService.all();
+        for (LitemallAdmin admin : adminList) {
+            Integer[] roleIds = admin.getRoleIds();
+            for (Integer roleId : roleIds) {
+                if (id.equals(roleId)) {
+                    return ResponseUtil.fail(ROLE_USER_EXIST, "当前角色存在管理员,不能删除");
+                }
+            }
+        }
+
+        roleService.deleteById(id);
+        return ResponseUtil.ok();
+    }
+
+
+    @Autowired
+    private ApplicationContext context;
+    private List<PermVo> systemPermissions = null;
+    private Set<String> systemPermissionsString = null;
+
+    private List<PermVo> getSystemPermissions() {
+        final String basicPackage = "org.linlinjava.litemall.admin";
+        if (systemPermissions == null) {
+            List<Permission> permissions = PermissionUtil.listPermission(context, basicPackage);
+            systemPermissions = PermissionUtil.listPermVo(permissions);
+            systemPermissionsString = PermissionUtil.listPermissionString(permissions);
+        }
+        return systemPermissions;
+    }
+
+    private Set<String> getAssignedPermissions(Integer roleId) {
+        // 这里需要注意的是,如果存在超级权限*,那么这里需要转化成当前所有系统权限。
+        // 之所以这么做,是因为前端不能识别超级权限,所以这里需要转换一下。
+        Set<String> assignedPermissions = null;
+        if (permissionService.checkSuperPermission(roleId)) {
+            getSystemPermissions();
+            assignedPermissions = systemPermissionsString;
+        } else {
+            assignedPermissions = permissionService.queryByRoleId(roleId);
+        }
+
+        return assignedPermissions;
+    }
+
+    /**
+     * 管理员的权限情况
+     *
+     * @return 系统所有权限列表和管理员已分配权限
+     */
+    @RequiresPermissions("admin:role:permission:get")
+    @RequiresPermissionsDesc(menu = {"系统管理", "角色管理"}, button = "权限详情")
+    @GetMapping("/permissions")
+    public Object getPermissions(Integer roleId) {
+        List<PermVo> systemPermissions = getSystemPermissions();
+        Set<String> assignedPermissions = getAssignedPermissions(roleId);
+
+        Map<String, Object> data = new HashMap<>();
+        data.put("systemPermissions", systemPermissions);
+        data.put("assignedPermissions", assignedPermissions);
+        return ResponseUtil.ok(data);
+    }
+
+
+    /**
+     * 更新管理员的权限
+     *
+     * @param body
+     * @return
+     */
+    @RequiresPermissions("admin:role:permission:update")
+    @RequiresPermissionsDesc(menu = {"系统管理", "角色管理"}, button = "权限变更")
+    @PostMapping("/permissions")
+    public Object updatePermissions(@RequestBody String body) {
+        Integer roleId = JacksonUtil.parseInteger(body, "roleId");
+        List<String> permissions = JacksonUtil.parseStringList(body, "permissions");
+        if (roleId == null || permissions == null) {
+            return ResponseUtil.badArgument();
+        }
+
+        // 如果修改的角色是超级权限,则拒绝修改。
+        if (permissionService.checkSuperPermission(roleId)) {
+            return ResponseUtil.fail(AdminResponseCode.ROLE_SUPER_SUPERMISSION, "当前角色的超级权限不能变更");
+        }
+
+        // 先删除旧的权限,再更新新的权限
+        permissionService.deleteByRoleId(roleId);
+        for (String permission : permissions) {
+            LitemallPermission litemallPermission = new LitemallPermission();
+            litemallPermission.setRoleId(roleId);
+            litemallPermission.setPermission(permission);
+            permissionService.add(litemallPermission);
+        }
+        return ResponseUtil.ok();
+    }
+
+}

+ 65 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminStatController.java

@@ -0,0 +1,65 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.admin.vo.StatVo;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.db.service.StatService;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/admin/stat")
+@Validated
+public class AdminStatController {
+    private final Log logger = LogFactory.getLog(AdminStatController.class);
+
+    @Autowired
+    private StatService statService;
+
+    @RequiresPermissions("admin:stat:user")
+    @RequiresPermissionsDesc(menu = {"统计管理", "用户统计"}, button = "查询")
+    @GetMapping("/user")
+    public Object statUser() {
+        List<Map> rows = statService.statUser();
+        String[] columns = new String[]{"day", "users"};
+        StatVo statVo = new StatVo();
+        statVo.setColumns(columns);
+        statVo.setRows(rows);
+        return ResponseUtil.ok(statVo);
+    }
+
+    @RequiresPermissions("admin:stat:order")
+    @RequiresPermissionsDesc(menu = {"统计管理", "订单统计"}, button = "查询")
+    @GetMapping("/order")
+    public Object statOrder() {
+        List<Map> rows = statService.statOrder();
+        String[] columns = new String[]{"day", "orders", "customers", "amount", "pcr"};
+        StatVo statVo = new StatVo();
+        statVo.setColumns(columns);
+        statVo.setRows(rows);
+
+        return ResponseUtil.ok(statVo);
+    }
+
+    @RequiresPermissions("admin:stat:goods")
+    @RequiresPermissionsDesc(menu = {"统计管理", "商品统计"}, button = "查询")
+    @GetMapping("/goods")
+    public Object statGoods() {
+        List<Map> rows = statService.statGoods();
+        String[] columns = new String[]{"day", "orders", "products", "amount"};
+        StatVo statVo = new StatVo();
+        statVo.setColumns(columns);
+        statVo.setRows(rows);
+        return ResponseUtil.ok(statVo);
+    }
+
+}

+ 89 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminStorageController.java

@@ -0,0 +1,89 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.storage.StorageService;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallStorage;
+import org.linlinjava.litemall.db.service.LitemallStorageService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.validation.constraints.NotNull;
+import java.io.IOException;
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/storage")
+@Validated
+public class AdminStorageController {
+    private final Log logger = LogFactory.getLog(AdminStorageController.class);
+
+    @Autowired
+    private StorageService storageService;
+    @Autowired
+    private LitemallStorageService litemallStorageService;
+
+    @RequiresPermissions("admin:storage:list")
+    @RequiresPermissionsDesc(menu = {"系统管理", "对象存储"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(String key, String name,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallStorage> storageList = litemallStorageService.querySelective(key, name, page, limit, sort, order);
+        return ResponseUtil.okList(storageList);
+    }
+
+    @RequiresPermissions("admin:storage:create")
+    @RequiresPermissionsDesc(menu = {"系统管理", "对象存储"}, button = "上传")
+    @PostMapping("/create")
+    public Object create(@RequestParam("file") MultipartFile file) throws IOException {
+        String originalFilename = file.getOriginalFilename();
+        LitemallStorage litemallStorage = storageService.store(file.getInputStream(), file.getSize(),
+                file.getContentType(), originalFilename);
+        return ResponseUtil.ok(litemallStorage);
+    }
+
+    @RequiresPermissions("admin:storage:read")
+    @RequiresPermissionsDesc(menu = {"系统管理", "对象存储"}, button = "详情")
+    @PostMapping("/read")
+    public Object read(@NotNull Integer id) {
+        LitemallStorage storageInfo = litemallStorageService.findById(id);
+        if (storageInfo == null) {
+            return ResponseUtil.badArgumentValue();
+        }
+        return ResponseUtil.ok(storageInfo);
+    }
+
+    @RequiresPermissions("admin:storage:update")
+    @RequiresPermissionsDesc(menu = {"系统管理", "对象存储"}, button = "编辑")
+    @PostMapping("/update")
+    public Object update(@RequestBody LitemallStorage litemallStorage) {
+        if (litemallStorageService.update(litemallStorage) == 0) {
+            return ResponseUtil.updatedDataFailed();
+        }
+        return ResponseUtil.ok(litemallStorage);
+    }
+
+    @RequiresPermissions("admin:storage:delete")
+    @RequiresPermissionsDesc(menu = {"系统管理", "对象存储"}, button = "删除")
+    @PostMapping("/delete")
+    public Object delete(@RequestBody LitemallStorage litemallStorage) {
+        String key = litemallStorage.getKey();
+        if (StringUtils.isEmpty(key)) {
+            return ResponseUtil.badArgument();
+        }
+        litemallStorageService.deleteByKey(key);
+        storageService.delete(key);
+        return ResponseUtil.ok();
+    }
+}

+ 127 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminTopicController.java

@@ -0,0 +1,127 @@
+package org.linlinjava.litemall.admin.web;
+
+import io.swagger.models.auth.In;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.util.JacksonUtil;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallGoods;
+import org.linlinjava.litemall.db.domain.LitemallTopic;
+import org.linlinjava.litemall.db.service.LitemallGoodsService;
+import org.linlinjava.litemall.db.service.LitemallTopicService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/admin/topic")
+@Validated
+public class AdminTopicController {
+    private final Log logger = LogFactory.getLog(AdminTopicController.class);
+
+    @Autowired
+    private LitemallTopicService topicService;
+    @Autowired
+    private LitemallGoodsService goodsService;
+
+    @RequiresPermissions("admin:topic:list")
+    @RequiresPermissionsDesc(menu = {"推广管理", "专题管理"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(String title, String subtitle,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort(accepts = {"id", "add_time", "price"}) @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallTopic> topicList = topicService.querySelective(title, subtitle, page, limit, sort, order);
+        return ResponseUtil.okList(topicList);
+    }
+
+    private Object validate(LitemallTopic topic) {
+        String title = topic.getTitle();
+        if (StringUtils.isEmpty(title)) {
+            return ResponseUtil.badArgument();
+        }
+        String content = topic.getContent();
+        if (StringUtils.isEmpty(content)) {
+            return ResponseUtil.badArgument();
+        }
+        BigDecimal price = topic.getPrice();
+        if (price == null) {
+            return ResponseUtil.badArgument();
+        }
+        return null;
+    }
+
+    @RequiresPermissions("admin:topic:create")
+    @RequiresPermissionsDesc(menu = {"推广管理", "专题管理"}, button = "添加")
+    @PostMapping("/create")
+    public Object create(@RequestBody LitemallTopic topic) {
+        Object error = validate(topic);
+        if (error != null) {
+            return error;
+        }
+        topicService.add(topic);
+        return ResponseUtil.ok(topic);
+    }
+
+    @RequiresPermissions("admin:topic:read")
+    @RequiresPermissionsDesc(menu = {"推广管理", "专题管理"}, button = "详情")
+    @GetMapping("/read")
+    public Object read(@NotNull Integer id) {
+        LitemallTopic topic = topicService.findById(id);
+        Integer[] goodsIds = topic.getGoods();
+        List<LitemallGoods> goodsList = null;
+        if (goodsIds == null || goodsIds.length == 0) {
+            goodsList = new ArrayList<>();
+        } else {
+            goodsList = goodsService.queryByIds(goodsIds);
+        }
+        Map<String, Object> data = new HashMap<>(2);
+        data.put("topic", topic);
+        data.put("goodsList", goodsList);
+        return ResponseUtil.ok(data);
+    }
+
+    @RequiresPermissions("admin:topic:update")
+    @RequiresPermissionsDesc(menu = {"推广管理", "专题管理"}, button = "编辑")
+    @PostMapping("/update")
+    public Object update(@RequestBody LitemallTopic topic) {
+        Object error = validate(topic);
+        if (error != null) {
+            return error;
+        }
+        if (topicService.updateById(topic) == 0) {
+            return ResponseUtil.updatedDataFailed();
+        }
+        return ResponseUtil.ok(topic);
+    }
+
+    @RequiresPermissions("admin:topic:delete")
+    @RequiresPermissionsDesc(menu = {"推广管理", "专题管理"}, button = "删除")
+    @PostMapping("/delete")
+    public Object delete(@RequestBody LitemallTopic topic) {
+        topicService.deleteById(topic.getId());
+        return ResponseUtil.ok();
+    }
+
+    @RequiresPermissions("admin:topic:batch-delete")
+    @RequiresPermissionsDesc(menu = {"推广管理", "专题管理"}, button = "批量删除")
+    @PostMapping("/batch-delete")
+    public Object batchDelete(@RequestBody String body) {
+        List<Integer> ids = JacksonUtil.parseIntegerList(body, "ids");
+        topicService.deleteByIds(ids);
+        return ResponseUtil.ok();
+    }
+}

+ 58 - 0
litemall-master/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminUserController.java

@@ -0,0 +1,58 @@
+package org.linlinjava.litemall.admin.web;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
+import org.linlinjava.litemall.core.util.ResponseUtil;
+import org.linlinjava.litemall.core.validator.Order;
+import org.linlinjava.litemall.core.validator.Sort;
+import org.linlinjava.litemall.db.domain.LitemallUser;
+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
+public class AdminUserController {
+    private final Log logger = LogFactory.getLog(AdminUserController.class);
+
+    @Autowired
+    private LitemallUserService userService;
+
+    @RequiresPermissions("admin:user:list")
+    @RequiresPermissionsDesc(menu = {"用户管理", "会员管理"}, button = "查询")
+    @GetMapping("/list")
+    public Object list(String username, String mobile,
+                       @RequestParam(defaultValue = "1") Integer page,
+                       @RequestParam(defaultValue = "10") Integer limit,
+                       @Sort @RequestParam(defaultValue = "add_time") String sort,
+                       @Order @RequestParam(defaultValue = "desc") String order) {
+        List<LitemallUser> 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));
+    }
+}

+ 0 - 0
litemall-master/litemall-admin-api/src/main/resources/application-admin.yml


+ 21 - 0
litemall-master/litemall-admin-api/src/main/resources/application.yml

@@ -0,0 +1,21 @@
+spring:
+  profiles:
+    active: db, core, admin
+  messages:
+    encoding: UTF-8
+  mvc:
+    static-path-pattern: /**
+
+server:
+  port: 8083
+
+logging:
+  level:
+    root:  ERROR
+    org.springframework:  ERROR
+    org.mybatis:  ERROR
+    org.linlinjava.litemall.admin:  DEBUG
+    org.linlinjava.litemall:  ERROR
+
+swagger:
+  production: false

BIN
litemall-master/litemall-admin-api/src/main/resources/static/litemall.png


+ 30 - 0
litemall-master/litemall-admin-api/src/test/java/org/linlinjava/litemall/admin/AdminConfigTest.java

@@ -0,0 +1,30 @@
+package org.linlinjava.litemall.admin;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.core.env.Environment;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+@WebAppConfiguration
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest
+public class AdminConfigTest {
+    @Autowired
+    private Environment environment;
+
+    @Test
+    public void test() {
+        // 测试获取application-core.yml配置信息
+        System.out.println(environment.getProperty("litemall.express.appId"));
+        // 测试获取application-db.yml配置信息
+        System.out.println(environment.getProperty("spring.datasource.druid.url"));
+        // 测试获取application-admin.yml配置信息
+        // System.out.println(environment.getProperty(""));
+        // 测试获取application.yml配置信息
+        System.out.println(environment.getProperty("logging.level.org.linlinjava.litemall.admin"));
+    }
+
+}

+ 25 - 0
litemall-master/litemall-admin-api/src/test/java/org/linlinjava/litemall/admin/BcryptTest.java

@@ -0,0 +1,25 @@
+package org.linlinjava.litemall.admin;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.linlinjava.litemall.core.util.bcrypt.BCryptPasswordEncoder;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest
+public class BcryptTest {
+
+    @Test
+    public void test() {
+        String rawPassword = "aaaaaa";
+        String encodedPassword = "";
+        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
+        encodedPassword = bCryptPasswordEncoder.encode(rawPassword);
+
+        System.out.println("rawPassword=" + rawPassword + " encodedPassword=" + encodedPassword);
+
+        Assert.assertTrue(bCryptPasswordEncoder.matches(rawPassword, encodedPassword));
+    }
+}

+ 27 - 0
litemall-master/litemall-admin-api/src/test/java/org/linlinjava/litemall/admin/CreateShareImageTest.java

@@ -0,0 +1,27 @@
+package org.linlinjava.litemall.admin;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.linlinjava.litemall.core.qcode.QCodeService;
+import org.linlinjava.litemall.db.domain.LitemallGoods;
+import org.linlinjava.litemall.db.service.LitemallGoodsService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+@WebAppConfiguration
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest
+public class CreateShareImageTest {
+    @Autowired
+    QCodeService qCodeService;
+    @Autowired
+    LitemallGoodsService litemallGoodsService;
+
+    @Test
+    public void test() {
+        LitemallGoods good = litemallGoodsService.findById(1181010);
+        qCodeService.createGoodShareImage(good.getId().toString(), good.getPicUrl(), good.getName());
+    }
+}

+ 55 - 0
litemall-master/litemall-admin-api/src/test/java/org/linlinjava/litemall/admin/DbTest.java

@@ -0,0 +1,55 @@
+package org.linlinjava.litemall.admin;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.core.env.Environment;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.LocalDate;
+
+@WebAppConfiguration
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class DbTest {
+    @Autowired
+    private Environment environment;
+
+    @Test
+    public void test() {
+        String user = environment.getProperty("spring.datasource.druid.username");
+        String password = environment.getProperty("spring.datasource.druid.password");
+        String url = environment.getProperty("spring.datasource.druid.url");
+        int index1 = url.indexOf("3306/");
+        int index2 = url.indexOf("?");
+        String db = url.substring(index1+5, index2);
+        System.out.println(user);
+        System.out.println(password);
+        System.out.println(db);
+    }
+
+    @Test
+    public void testFileCreate() throws IOException {
+        LocalDate localDate = LocalDate.now();
+        String fileName = localDate.toString() + ".sql";
+        System.out.println(fileName);
+
+        File file = new File("backup", fileName);
+        file.getParentFile().mkdirs();
+        file.createNewFile();
+    }
+
+    @Test
+    public void testFileDelete() throws IOException {
+        LocalDate localDate = LocalDate.now();
+        String fileName = localDate.toString() + ".sql";
+        System.out.println(fileName);
+
+        File file = new File("backup", fileName);
+        file.deleteOnExit();
+    }
+}

+ 31 - 0
litemall-master/litemall-admin-api/src/test/java/org/linlinjava/litemall/admin/PermissionTest.java

@@ -0,0 +1,31 @@
+package org.linlinjava.litemall.admin;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.linlinjava.litemall.admin.util.Permission;
+import org.linlinjava.litemall.admin.util.PermissionUtil;
+import org.linlinjava.litemall.admin.vo.PermVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+import java.util.List;
+
+@WebAppConfiguration
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest
+public class PermissionTest {
+
+    @Autowired
+    private ApplicationContext context;
+
+    @Test
+    public void test() {
+        final String basicPackage = "org.linlinjava.litemall.admin";
+        List<Permission> permissionList = PermissionUtil.listPermission(context, basicPackage);
+        List<PermVo> permVoList = PermissionUtil.listPermVo(permissionList);
+        permVoList.stream().forEach(System.out::println);
+    }
+}

+ 14 - 0
litemall-master/litemall-admin/.editorconfig

@@ -0,0 +1,14 @@
+# https://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+insert_final_newline = false
+trim_trailing_whitespace = false

+ 8 - 0
litemall-master/litemall-admin/.env.deployment

@@ -0,0 +1,8 @@
+NODE_ENV = production
+
+# just a flag
+ENV = 'deploymenet'
+
+# base api
+VUE_APP_BASE_API = 'http://122.51.199.160:8080/admin'
+

+ 14 - 0
litemall-master/litemall-admin/.env.development

@@ -0,0 +1,14 @@
+# just a flag
+ENV = 'development'
+
+# base api
+VUE_APP_BASE_API = 'http://localhost:8080/admin'
+
+# vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable,
+# to control whether the babel-plugin-dynamic-import-node plugin is enabled.
+# It only does one thing by converting all import() to require().
+# This configuration can significantly increase the speed of hot updates,
+# when you have a large number of pages.
+# Detail:  https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js
+
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 8 - 0
litemall-master/litemall-admin/.env.production

@@ -0,0 +1,8 @@
+NODE_ENV = production
+
+# just a flag
+ENV = 'production'
+
+# base api
+VUE_APP_BASE_API = 'https://www.example.com/admin'
+

+ 4 - 0
litemall-master/litemall-admin/.eslintignore

@@ -0,0 +1,4 @@
+build/*.js
+src/assets
+public
+dist

+ 198 - 0
litemall-master/litemall-admin/.eslintrc.js

@@ -0,0 +1,198 @@
+module.exports = {
+  root: true,
+  parserOptions: {
+    parser: 'babel-eslint',
+    sourceType: 'module'
+  },
+  env: {
+    browser: true,
+    node: true,
+    es6: true,
+  },
+  extends: ['plugin:vue/recommended', 'eslint:recommended'],
+
+  // add your custom rules here
+  //it is base on https://github.com/vuejs/eslint-config-vue
+  rules: {
+    "vue/max-attributes-per-line": [2, {
+      "singleline": 10,
+      "multiline": {
+        "max": 1,
+        "allowFirstLine": false
+      }
+    }],
+    "vue/singleline-html-element-content-newline": "off",
+    "vue/multiline-html-element-content-newline":"off",
+    "vue/name-property-casing": ["error", "PascalCase"],
+    "vue/no-v-html": "off",
+    'accessor-pairs': 2,
+    'arrow-spacing': [2, {
+      'before': true,
+      'after': true
+    }],
+    'block-spacing': [2, 'always'],
+    'brace-style': [2, '1tbs', {
+      'allowSingleLine': true
+    }],
+    'camelcase': [0, {
+      'properties': 'always'
+    }],
+    'comma-dangle': [2, 'never'],
+    'comma-spacing': [2, {
+      'before': false,
+      'after': true
+    }],
+    'comma-style': [2, 'last'],
+    'constructor-super': 2,
+    'curly': [2, 'multi-line'],
+    'dot-location': [2, 'property'],
+    'eol-last': 2,
+    'eqeqeq': ["error", "always", {"null": "ignore"}],
+    'generator-star-spacing': [2, {
+      'before': true,
+      'after': true
+    }],
+    'handle-callback-err': [2, '^(err|error)$'],
+    'indent': [2, 2, {
+      'SwitchCase': 1
+    }],
+    'jsx-quotes': [2, 'prefer-single'],
+    'key-spacing': [2, {
+      'beforeColon': false,
+      'afterColon': true
+    }],
+    'keyword-spacing': [2, {
+      'before': true,
+      'after': true
+    }],
+    'new-cap': [2, {
+      'newIsCap': true,
+      'capIsNew': false
+    }],
+    'new-parens': 2,
+    'no-array-constructor': 2,
+    'no-caller': 2,
+    'no-console': 'off',
+    'no-class-assign': 2,
+    'no-cond-assign': 2,
+    'no-const-assign': 2,
+    'no-control-regex': 0,
+    'no-delete-var': 2,
+    'no-dupe-args': 2,
+    'no-dupe-class-members': 2,
+    'no-dupe-keys': 2,
+    'no-duplicate-case': 2,
+    'no-empty-character-class': 2,
+    'no-empty-pattern': 2,
+    'no-eval': 2,
+    'no-ex-assign': 2,
+    'no-extend-native': 2,
+    'no-extra-bind': 2,
+    'no-extra-boolean-cast': 2,
+    'no-extra-parens': [2, 'functions'],
+    'no-fallthrough': 2,
+    'no-floating-decimal': 2,
+    'no-func-assign': 2,
+    'no-implied-eval': 2,
+    'no-inner-declarations': [2, 'functions'],
+    'no-invalid-regexp': 2,
+    'no-irregular-whitespace': 2,
+    'no-iterator': 2,
+    'no-label-var': 2,
+    'no-labels': [2, {
+      'allowLoop': false,
+      'allowSwitch': false
+    }],
+    'no-lone-blocks': 2,
+    'no-mixed-spaces-and-tabs': 2,
+    'no-multi-spaces': 2,
+    'no-multi-str': 2,
+    'no-multiple-empty-lines': [2, {
+      'max': 1
+    }],
+    'no-native-reassign': 2,
+    'no-negated-in-lhs': 2,
+    'no-new-object': 2,
+    'no-new-require': 2,
+    'no-new-symbol': 2,
+    'no-new-wrappers': 2,
+    'no-obj-calls': 2,
+    'no-octal': 2,
+    'no-octal-escape': 2,
+    'no-path-concat': 2,
+    'no-proto': 2,
+    'no-redeclare': 2,
+    'no-regex-spaces': 2,
+    'no-return-assign': [2, 'except-parens'],
+    'no-self-assign': 2,
+    'no-self-compare': 2,
+    'no-sequences': 2,
+    'no-shadow-restricted-names': 2,
+    'no-spaced-func': 2,
+    'no-sparse-arrays': 2,
+    'no-this-before-super': 2,
+    'no-throw-literal': 2,
+    'no-trailing-spaces': 2,
+    'no-undef': 2,
+    'no-undef-init': 2,
+    'no-unexpected-multiline': 2,
+    'no-unmodified-loop-condition': 2,
+    'no-unneeded-ternary': [2, {
+      'defaultAssignment': false
+    }],
+    'no-unreachable': 2,
+    'no-unsafe-finally': 2,
+    'no-unused-vars': [2, {
+      'vars': 'all',
+      'args': 'none'
+    }],
+    'no-useless-call': 2,
+    'no-useless-computed-key': 2,
+    'no-useless-constructor': 2,
+    'no-useless-escape': 0,
+    'no-whitespace-before-property': 2,
+    'no-with': 2,
+    'one-var': [2, {
+      'initialized': 'never'
+    }],
+    'operator-linebreak': [2, 'after', {
+      'overrides': {
+        '?': 'before',
+        ':': 'before'
+      }
+    }],
+    'padded-blocks': [2, 'never'],
+    'quotes': [2, 'single', {
+      'avoidEscape': true,
+      'allowTemplateLiterals': true
+    }],
+    'semi': [2, 'never'],
+    'semi-spacing': [2, {
+      'before': false,
+      'after': true
+    }],
+    'space-before-blocks': [2, 'always'],
+    'space-before-function-paren': [2, 'never'],
+    'space-in-parens': [2, 'never'],
+    'space-infix-ops': 2,
+    'space-unary-ops': [2, {
+      'words': true,
+      'nonwords': false
+    }],
+    'spaced-comment': [2, 'always', {
+      'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
+    }],
+    'template-curly-spacing': [2, 'never'],
+    'use-isnan': 2,
+    'valid-typeof': 2,
+    'wrap-iife': [2, 'any'],
+    'yield-star-spacing': [2, 'both'],
+    'yoda': [2, 'never'],
+    'prefer-const': 2,
+    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
+    'object-curly-spacing': [2, 'always', {
+      objectsInObjects: false
+    }],
+    'array-bracket-spacing': [2, 'never']
+  }
+}

+ 23 - 0
litemall-master/litemall-admin/.gitignore

@@ -0,0 +1,23 @@
+.DS_Store
+node_modules/
+dist/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+**/*.log
+
+tests/**/coverage/
+tests/e2e/reports
+selenium-debug.log
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.local
+
+package-lock.json
+yarn.lock

+ 5 - 0
litemall-master/litemall-admin/babel.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  presets: [
+    '@vue/app'
+  ]
+}

+ 34 - 0
litemall-master/litemall-admin/build/index.js

@@ -0,0 +1,34 @@
+const { run } = require('runjs')
+const chalk = require('chalk')
+const rawArgv = process.argv.slice(2)
+const args = rawArgv.join(' ')
+
+if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
+  const report = rawArgv.includes('--report')
+
+  run(`vue-cli-service build ${args}`)
+
+  const port = 9526
+  const publicPath = '/'
+
+  var connect = require('connect')
+  var serveStatic = require('serve-static')
+  const app = connect()
+
+  app.use(
+    publicPath,
+    serveStatic('./dist', {
+      index: ['index.html', '/']
+    })
+  )
+
+  app.listen(port, function () {
+    console.log(chalk.green(`> Preview at  http://localhost:${port}${publicPath}`))
+    if (report) {
+      console.log(chalk.green(`> Report at  http://localhost:${port}${publicPath}report.html`))
+    }
+
+  })
+} else {
+  run(`vue-cli-service build ${args}`)
+}

+ 24 - 0
litemall-master/litemall-admin/jest.config.js

@@ -0,0 +1,24 @@
+module.exports = {
+  moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
+  transform: {
+    '^.+\\.vue$': 'vue-jest',
+    '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
+      'jest-transform-stub',
+    '^.+\\.jsx?$': 'babel-jest'
+  },
+  moduleNameMapper: {
+    '^@/(.*)$': '<rootDir>/src/$1'
+  },
+  snapshotSerializers: ['jest-serializer-vue'],
+  testMatch: [
+    '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
+  ],
+  collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'],
+  coverageDirectory: '<rootDir>/tests/unit/coverage',
+  // 'collectCoverage': true,
+  'coverageReporters': [
+    'lcov',
+    'text-summary'
+  ],
+  testURL: 'http://localhost/'
+}

+ 9 - 0
litemall-master/litemall-admin/jsconfig.json

@@ -0,0 +1,9 @@
+{ 
+  "compilerOptions": {
+    "baseUrl": "./",
+    "paths": {
+        "@/*": ["src/*"]
+    }
+  },
+  "exclude": ["node_modules", "dist"]
+}

+ 104 - 0
litemall-master/litemall-admin/package.json

@@ -0,0 +1,104 @@
+{
+  "name": "litemall-admin",
+  "version": "1.0.0",
+  "description": "litemall-admin basing on vue-element-admin 4.3.0",
+  "author": "linlinjava <linlinjava@163.com>",
+  "license": "MIT",
+  "scripts": {
+    "dev": "vue-cli-service serve",
+    "build": "vue-cli-service build --mode production",
+    "build:prod": "vue-cli-service build --mode production",
+    "build:dep": "vue-cli-service build --mode deployment",
+    "preview": "node build/index.js --preview",
+    "lint": "eslint --ext .js,.vue src",
+    "test:unit": "jest --clearCache && vue-cli-service test:unit",
+    "test:ci": "npm run lint && npm run test:unit",
+    "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
+  },
+  "husky": {
+    "hooks": {
+      "pre-commit": "lint-staged"
+    }
+  },
+  "lint-staged": {
+    "src/**/*.{js,vue}": [
+      "eslint --fix",
+      "git add"
+    ]
+  },
+  "keywords": [
+    "vue",
+    "admin",
+    "dashboard",
+    "element-ui",
+    "boilerplate",
+    "admin-template",
+    "management-system"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/linlinjava/litemall.git"
+  },
+  "bugs": {
+    "url": "https://github.com/linlinjava/litemall/issues"
+  },
+  "dependencies": {
+    "@tinymce/tinymce-vue": "3.0.1",
+    "lodash": "^4.17.11",
+    "v-charts": "1.19.0",
+    "axios": "0.18.1",
+    "clipboard": "2.0.4",
+    "connect": "3.6.6",
+    "echarts": "4.2.1",
+    "element-ui": "2.13.2",
+    "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"
+  },
+  "devDependencies": {
+    "@babel/core": "7.0.0",
+    "@babel/register": "7.0.0",
+    "@vue/cli-plugin-babel": "3.5.3",
+    "@vue/cli-plugin-eslint": "^3.9.1",
+    "@vue/cli-plugin-unit-jest": "3.5.3",
+    "@vue/cli-service": "3.5.3",
+    "@vue/test-utils": "1.0.0-beta.29",
+    "autoprefixer": "^9.5.1",
+    "babel-core": "7.0.0-bridge.0",
+    "babel-eslint": "10.0.1",
+    "babel-jest": "23.6.0",
+    "chalk": "2.4.2",
+    "chokidar": "2.1.5",
+    "connect": "3.6.6",
+    "eslint": "5.15.3",
+    "eslint-plugin-vue": "5.2.2",
+    "html-webpack-plugin": "3.2.0",
+    "husky": "1.3.1",
+    "lint-staged": "8.1.5",
+    "sass": "^1.26.2",
+    "runjs": "^4.3.2",
+    "sass-loader": "^7.1.0",
+    "script-ext-html-webpack-plugin": "2.1.3",
+    "serve-static": "^1.13.2",
+    "svg-sprite-loader": "4.1.3",
+    "svgo": "1.2.0",
+    "vue-template-compiler": "2.6.10"
+  },
+  "engines": {
+    "node": ">=8.9",
+    "npm": ">= 3.0.0"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions"
+  ]
+}

+ 5 - 0
litemall-master/litemall-admin/postcss.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  plugins: {
+    autoprefixer: {}
+  }
+}

BIN
litemall-master/litemall-admin/public/favicon.ico


Some files were not shown because too many files changed in this diff