Merge branch 'master' into dev

This commit is contained in:
linlinjava
2019-12-29 18:26:50 +08:00
committed by GitHub
1041 changed files with 27733 additions and 13328 deletions

51
.gitignore vendored
View File

@@ -1,5 +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
.idea
.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

View File

@@ -1,5 +1,52 @@
# 更新日志
## V 1.5.0
*2019-11-15* 持续优化轻商城模块以及推荐项目Flutter_Mall
#### Bug 修复
* `小商城`优惠券绑定绑定优惠券ID(#157 by @beaver383)
* `小商城`评论列表不能正确显示
* `轻商城`修正取消订单接口 (#256 by @1037621594)
#### 优化
* `小商城`采用延迟队列实现支付超时取消订单功能(参考#275 by @alexzhu0592
* `小商城`分享按钮可选配置 (#239 by @galenzhao)
#### 新特性
* `基础系统`支持阿里云短信
* `轻商城`接入微信支付H5支付 (#291 by @beaver383)
* `小商城`团购拼团超期取消 (#284 by @beaver383)
* `管理后台`订单详情新增打印 (#274 by @fanchenggang )
* README文档推荐项目Flutter_Mall
## V 1.4.0
*2019-05-16*,支持移动端轻商城
#### Bug 修复
* `小商场`购物车和订单的商品数量必须正整数
* `小商城`微信支付回调校验失败通知信息
* `小商城`收货地址采用userId和id联合查询
* `管理后台`管理员不能删除自己账号
#### 优化
* `文档`支持API文档
* `基础系统`更新第三方插件mybatis-generator-plugin到1.3.2
* `管理后台`不允许管理员通过编辑接口修改管理员密码
#### 新特性
* `小商城`帮助中心页面
* `小商城`后端登录验证方式采用JWT (#167 by @Bigger-Ma)
* `轻商场`基本结构完成 (#157 by @pkwenda)
* `管理后台`支持操作日志管理
## V 1.3.0
*2019-03-11*,支持配置管理

View File

@@ -7,30 +7,41 @@ litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端
* [文档](https://linlinjava.gitbook.io/litemall)
* [贡献](https://linlinjava.gitbook.io/litemall/contribute)
* [FAQ](https://linlinjava.gitbook.io/litemall/faq)
* [API](https://linlinjava.gitbook.io/litemall/api)
## 项目实例
### 小商场实例
* renard-wx模块实例
![](./doc/pic/renard_wx_demo.png)
> 注意:此实例是真实小商场,开发者可以购买商品和付款,但请不要尝试退款操作。
* litemall-wx模块实例
![](./doc/pic/litemall_wx_demo.png)
> 注意:此实例是测试小商场,开发者请不要尝试购买商品、付款、退款操作。
### 轻商场实例
目前未部署
请手机扫描以下二维码访问:
![](./doc/pic/5.gif)
![](./doc/pic/mobmall.png)
或者浏览器采用手机模式访问以下网址: [http://122.51.199.160:8080/vue/index.html#/](http://122.51.199.160:8080/vue/index.html#/)
注意:
> 1. 由于第一次加载数据量较大建议wifi网络访问且耐心等待数秒。
> 2. 此实例是测试轻商场,不支持支付,而且处于开发中还不完善。
### 管理后台实例
![](./doc/pic/4.png)
1. 浏览器打开,输入以下网址: [http://118.24.0.153:8080/#/login](http://118.24.0.153:8080/#/login)
1. 浏览器打开,输入以下网址: [http://122.51.199.160:8080/#/login](http://122.51.199.160:8080/#/login)
2. 管理员用户名`admin123`,管理员密码`admin123`
> 注意:此实例只是测试管理后台,不是前两个小商城的管理后台。
@@ -99,9 +110,8 @@ litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端
```bash
cd litemall
mvn install
mvn package
cd ./litemall-all
mvn spring-boot:run
mvn clean package
java -Dfile.encoding=UTF-8 -jar litemall-all/target/litemall-all-0.1.0-exec.jar
```
4. 启动管理后台前端
@@ -144,7 +154,7 @@ litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端
## 开发计划
当前版本[v1.3.0](https://linlinjava.gitbook.io/litemall/changelog)
当前版本[v1.5.0](https://linlinjava.gitbook.io/litemall/changelog)
目前项目开发中,存在诸多不足,以下是目前规划的开发计划。
@@ -209,10 +219,16 @@ V 3.0.0 完成以下目标:
项目介绍:基于有赞 vant 组件库的移动商城。
项目参考litemall项目的litemall-vue模块基于vant--mobile-mall项目开发。
## 推荐
1. [Flutter_Mall](https://github.com/youxinLu/mall)
项目介绍Flutter_Mall是一款Flutter开源在线商城应用程序。
## 问题
![](doc/pic/qq.png)
![](doc/pic/qq2.png)
* 开发者有问题或者好的建议可以用Issues反馈交流请给出详细信息
* 在开发交流群中应讨论开发、业务和合作问题

View File

@@ -2,7 +2,7 @@
### 项目打包
1.主机或者开发机打包项目到deploy
1.服务器或者开发机打包项目到deploy
```
cd litemall
cat ./litemall-db/sql/litemall_schema.sql > ./deploy/db/litemall.sql
@@ -25,13 +25,13 @@
2. 修改litemall文件夹下面的*.yml外部配置文件当litemall-all模块启动时会
加载外部配置文件而覆盖默认jar包内部的配置文件。
例如,配置文件中一些地方需要设置成远程主机的IP地址
例如,配置文件中一些地方需要设置成远程服务器的IP地址
此时deploy部署包结构如下
* bin
存放远程主机运行的脚本包括deploy.sh脚本和reset.sh脚本
存放远程服务器运行的脚本包括deploy.sh脚本和reset.sh脚本
* db
@@ -39,15 +39,15 @@
* litemall
存放远程主机运行的代码包括litemall-all二进制可执行包和litemall外部配置文件
存放远程服务器运行的代码包括litemall-all二进制可执行包和litemall外部配置文件
* util
存放开发主机运行的脚本包括package.sh脚本和lazy.sh脚本。
由于是本地开发主机运行,因此开发者可以不用上传到远程主机
存放开发服务器运行的脚本包括package.sh脚本和lazy.sh脚本。
由于是本地开发服务器运行,因此开发者可以不用上传到远程服务器
### 项目部署
1. 远程主机环境MySQL和JDK1.8)已经安装好,请确保云主机的安全组已经允许相应的端口。
1. 远程服务器环境MySQL和JDK1.8)已经安装好,请确保云服务器的安全组已经允许相应的端口。
2. 导入db/litemall.sql
```bash
cd /home/ubuntu/deploy/db
@@ -59,7 +59,7 @@
sudo ln -f -s /home/ubuntu/deploy/litemall/litemall.jar /etc/init.d/litemall
sudo service litemall start
```
4. 测试是否部署成功(xxx.xxx.xxx.xxx是云主机IP
4. 测试是否部署成功(xxx.xxx.xxx.xxx是云服务器IP
```
http://xxx.xxx.xxx.xxx:8080/wx/index/index
http://xxx.xxx.xxx.xxx:8080/admin/index/index
@@ -73,26 +73,26 @@
* util/packet.sh
在开发主机运行可以自动项目打包
在开发服务器运行可以自动项目打包
* util/lazy.sh
在开发主机运行可以自动项目打包、项目上传远程主机、自动登录系统执行项目部署脚本。
在开发服务器运行可以自动项目打包、项目上传远程服务器、自动登录系统执行项目部署脚本。
注意:
> 1. 开发者需要在util/lazy.sh中设置相应的远程主机登录账号和密钥文件路径。
> 2. 开发者需要在bin/reset.sh设置远程主机的MySQL的root登录账户。
> 1. 开发者需要在util/lazy.sh中设置相应的远程服务器登录账号和密钥文件路径。
> 2. 开发者需要在bin/reset.sh设置远程服务器的MySQL的root登录账户。
* bin/deploy.sh
在远程主机运行可以自动部署服务
在远程服务器运行可以自动部署服务
* bin/reset.sh
在远程主机运行可以自动项目导入数据、删除本地上传图片、再执行bin/deploy.sh部署服务。
在远程服务器运行可以自动项目导入数据、删除本地上传图片、再执行bin/deploy.sh部署服务。
注意:
> 开发者需要在bin/reset.sh设置远程主机的MySQL的root登录账户。
> 开发者需要在bin/reset.sh设置远程服务器的MySQL的root登录账户。
总结当开发者设置好配置信息以后可以在本地运行lazy.sh脚本自动一键部署:
```bash

View File

@@ -14,18 +14,18 @@ PASSWORD=
if test -z "$PASSWORD"
then
echo "请设置云主机MySQL的root账号密码"
exit -1
echo "请设置云服务器MySQL的root账号密码"
exit 1
fi
# 导入数据
cd /home/ubuntu/deploy/db
cd /home/ubuntu/deploy/db || exit 2
mysql -h localhost -u $ROOT -p$PASSWORD < litemall.sql
# 删除storage文件夹内文件
cd /home/ubuntu/deploy/litemall/storage
cd /home/ubuntu/deploy/litemall/storage || exit 2
rm -f ./**
# 重新部署服务
cd /home/ubuntu/deploy/bin
cd /home/ubuntu/deploy/bin || exit 2
sudo ./deploy.sh

View File

@@ -1,107 +0,0 @@
litemall:
# 开发者应该设置成自己的wx相关信息
wx:
app-id: wxa5b486c6b918ecfb
app-secret: e04004829d4c383b4db7769d88dfbca1
mch-id: 111111
mch-key: xxxxxx
notify-url: http://118.24.0.153: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
# 短消息模版通知配置
# 短信息用于通知客户例如发货短信通知注意配置格式template-nametemplate-templateId 请参考 NotifyType 枚举值
sms:
enable: false
appid: 111111111
appkey: xxxxxxxxxxxxxx
template:
- name: paySucceed
templateId: 156349
- name: captcha
templateId: 156433
- name: ship
templateId: 158002
- name: refund
templateId: 159447
# 微信模版通知配置
# 微信模版用于通知客户或者运营者注意配置格式template-nametemplate-templateId 请参考 NotifyType 枚举值
wx:
enable: false
template:
- name: paySucceed
templateId: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- name: captcha
templateId: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- name: ship
templateId: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- name: refund
templateId: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 快鸟物流查询配置
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://118.24.0.153: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

View File

@@ -1,26 +0,0 @@
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
spring:
datasource:
druid:
url: jdbc:mysql://localhost:3306/litemall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true&verifyServerCertificate=false&useSSL=false
driver-class-name: com.mysql.jdbc.Driver
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

View File

@@ -1,19 +1,158 @@
spring:
profiles:
active: db, core, admin, wx
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: 8080
logging:
level:
root: ERROR
org.springframework: ERROR
org.mybatis: ERROR
org.linlinjava.litemall.core: ERROR
org.linlinjava.litemall.db: ERROR
org.linlinjava.litemall.admin: ERROR
org.linlinjava.litemall.wx: ERROR
org.linlinjava.litemall: ERROR
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
# 短消息模版通知配置
# 短信息用于通知客户例如发货短信通知注意配置格式template-nametemplate-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
# 微信模版通知配置
# 微信模版用于通知客户或者运营者注意配置格式template-nametemplate-templateId 请参考 NotifyType 枚举值
wx:
enable: false
template:
- name: paySucceed
templateId: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- name: captcha
templateId: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- name: ship
templateId: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- name: refund
templateId: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 快鸟物流查询配置
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

View File

@@ -2,11 +2,11 @@
# 本脚本的作用是
# 1. 项目打包
# 2. 上传云主机
# 3. 远程登录云主机并执行reset脚本
# 2. 上传云服务器
# 3. 远程登录云服务器并执行reset脚本
# 请设置云主机的IP地址和账户
# 例如 ubuntu@118.24.0.153
# 请设置云服务器的IP地址和账户
# 例如 ubuntu@122.51.199.160
REMOTE=
# 请设置本地SSH私钥文件id_rsa路径
# 例如 /home/litemall/id_rsa
@@ -14,30 +14,30 @@ ID_RSA=
if test -z "$REMOTE"
then
echo "请设置云主机登录IP地址和账户"
exit -1
echo "请设置云服务器登录IP地址和账户"
exit 1
fi
if test -z "$ID_RSA"
then
echo "请设置云主机登录IP地址和账户"
exit -1
echo "请设置云服务器登录IP地址和账户"
exit 1
fi
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
cd $DIR/../..
cd $DIR/../.. || exit 2
LITEMALL_HOME=$PWD
echo "LITEMALL_HOME $LITEMALL_HOME"
# 项目打包
cd $LITEMALL_HOME
cd $LITEMALL_HOME || exit 2
./deploy/util/package.sh
# 上传云主机
cd $LITEMALL_HOME
# 上传云服务器
cd $LITEMALL_HOME || exit 2
scp -i $ID_RSA -r ./deploy $REMOTE:/home/ubuntu/
# 远程登录云主机并执行reset脚本
# 远程登录云服务器并执行reset脚本
ssh $REMOTE -i $ID_RSA << eeooff
cd /home/ubuntu
sudo ./deploy/bin/reset.sh

View File

@@ -133,6 +133,44 @@ litemall.wx.notify-url=
3. 最后如果设置正确用chrome的开发者工具查看登录页面向后端请求返回数据信息
如果设置不正确,请启动相应的后端服务。
### 2.2 安装失败/启动不成功
现象:
执行`cnpm install`失败
原因:
可能下载依赖失败。
解决:
清空node_modules重新执行`cnpm install`命令或者自行百度、Google。
### 2.3 分页数据返回不正常
现象:
如果管理后台点击很大的分页页数(实际已超过当时数据最大页数),后端仍然能够返回数据。
原因:
这个不是BUG而是开发者对于查询页面超过实际页面后应该产生何种效果的不同理解。
* 返回最后一页数据可能是合理的;
* 返回空数据可能也是合理的。
解决:
litemall-db模块的application-db.yaml资源文件中reasonable是true
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
开发者可以尝试设置reasonable为false然后检查是否能够解决问题。
## 3. 基础系统
这里主要是指litemall-db、litemall-core和litemall-all模块三个模块的相关问题。
@@ -227,13 +265,15 @@ IDEA导入项目时非常耗时间或者卡断或者一直疯狂运行
原因:
应该是litemall-admin模块的node_modules文件夹导致的。
node_modules是litemall-admin所依赖的项目库可能有近200M的文件。
应该是litemall-admin模块和litemall-vue模块的node_modules文件夹导致的。
node_modules是litemall-admin和litemall-vue模块所依赖的项目库可能有近200M的文件。
而IDEA如果没有设置则可能尝试对该文件夹进行解析索引从而导致卡断。
解决方案:
先关闭IDEA然后删除node_modules文件夹然后重新打开IDEA设置node_modules文件夹Excluded状态。
1. 先关闭IDEA然后删除litemall-admin和litemall-vue模块内的node_modules文件夹
2. 然后分别创建空的node_modules文件夹
3. 重新打开IDEA分别设置litemall-admin模块和litemall-vue模块的node_modules文件夹Excluded状态。
![](./pic/excluded.png)

View File

@@ -4,6 +4,7 @@
* [更新日志](../CHANGELOG.md)
* [贡献指南](../CONTRIBUTE.md)
* [FAQ](./FAQ.md)
* [API](./api.md)
* [1. 系统架构](./project.md)
* [2. 基础系统](./platform.md)
* [3. 小商场](./wxmall.md)

View File

@@ -21,9 +21,7 @@
* `改善`管理员登录页面打开慢,优化速度
* `改善`地址优化,目前每一次点击都会请求后台,应该缓存已有的数据
* `改善`vue和vue-element-admin等及时更新
* `功能`系统日志功能
* `功能`系统数据字典功能
* `功能`系统栏目管理功能
* `功能`支持数据库备份
## 4.1 litemall-admin-api

3388
doc/api.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -5,15 +5,14 @@ litemall轻商城是商城移动版本。
技术:
* 轻商城前端即litemall-vue模块
* power by vue-cli3
* vue-cli3
* Vue + Vue-router + Vant + Sass
* axios
* vee-validate
* fastclick
* babel-polyfill
* @xkeshi/vue-countdown
* Vant
* 轻商城即litemall-wx-api模块也就是和小商城后端是一样的。
* 轻商城即litemall-wx-api模块也就是和小商城后端是一样的。
* Spring Boot 2.x
* Spring MVC
* [weixin-java-tools](https://gitee.com/binary/weixin-java-tools)
@@ -27,4 +26,4 @@ litemall轻商城是商城移动版本。
这里的代码基于[vant--mobile-mall](https://github.com/qianzhaoy/vant--mobile-mall)
文档未完成。
文档未完成。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 67 KiB

BIN
doc/pic/get.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

BIN
doc/pic/get_no_token.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
doc/pic/get_with_token.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
doc/pic/login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

BIN
doc/pic/mobmall.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

BIN
doc/pic/post_no_token.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

BIN
doc/pic/post_with_token.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
doc/pic/qq2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 106 KiB

View File

@@ -270,7 +270,7 @@ litemall_region表保存了行政区域信息包括省级、市级、县级
如果用户没有支付,也没有点击`取消订单`按钮,那么系统会定时查询数据库的订单信息。
如果发现存在订单未支付状态超时半小时,此时系统会自动取消订单,来释放商品资源。
对应的后台服务方法是litemall-admin-api模块的`AdminOrderController.checkOrderUnpaid`
对应的应该是litemall-admin-api模块的系统定时任务的`OrderJob.checkOrderUnpaid`
* 101 -> 201
@@ -314,7 +314,7 @@ litemall_region表保存了行政区域信息包括省级、市级、县级
当管理员发货以后,用户一直没有确认收货,系统定时检测订单状态,如果发现发货以后
七天用户都没有收货此时系统自动确认用户收货设置订单状态402。
所对应的后台服务方法是litemall-admin-api模块的`AdminOrderController.checkOrderUnpaid`
应该改为 litemall-admin-api模块的系统定时任务`OrderJob.checkOrderUnconfirm`
注意:
> 上述订单状态变化中具体的逻辑处理可以参考相应模块文档和模块代码。
@@ -1153,7 +1153,7 @@ public interface Storage {
## 2.4 litemall-all
在章节1.5中讨论的部署方案中设计了一种单主机单服务方案,
在章节1.5中讨论的部署方案中设计了一种单服务器单服务方案,
也就是说两个后台服务和静态文件都部署在一个Spring Boot可执行jar包中。
查看litemall-all模块代码仅仅只有一个Application类。

View File

@@ -21,10 +21,6 @@ litemall是一个简单的商场系统基于现有的开源项目重新实
由litemall-wx-api模块和litemall-vue模块组成。
注意,目前这里移动商城子系统的后端和小商场子系统是一样的。
* 简商城子系统(webmall)
这里仅列出,目前没有开发计划。
* 管理后台子系统(admin)
@@ -45,6 +41,27 @@ litemall是一个简单的商场系统基于现有的开源项目重新实
采用VSC开发工具开发litemall-admin模块和litemall-vue模块。
### 1.1.1 项目特点
项目存在以下特点:
* 数据库方面只是简单的表表和表之间的依赖关系没有采用外键设计而是依赖Java代码在service层面或者业务层面保证。这样做的好处是数据库频繁改动很方便不会因为外键而导致数据库难以修改;
* 涉及三种技术栈,但是每种技术栈仅涉及最基础的技术;
* 后端技术栈,仅涉及 SpringSpring Boot, Spring MVC和Mybatis技术其他后端技术暂时不采用;
* 小程序技术栈仅涉及miniprogram官方文档
* 前端技术栈仅涉及vue, vuex, vue-route和element技术
* 安全方面,仅采用最基本的代码,提供简单基本的安全服务;
* 性能方面没有涉及内存数据库缓存功能而是完全依赖MySQL;
* 对象存储服务方面,支持本地存储和第三方云存储方案。
* 消息通知方面,支持邮件通知、第三方云短信通知和微信模板通知;
* 部署方便,支持多服务部署和一键部署脚本;
* 文档全面,虽然还在开发中,但是规划中文档和代码注释一定会完成,帮助开发者理解项目。
总之,目前的系统只是为了学习技术和业务而开发的一个简单商场原型系统。虽然缺失很多企业级功能,但是是完整和合理的原型系统。
注意:
> 以上特点并不一定是优点。
## 1.2 系统功能
从业务功能上,目前由六个业务模块组成:
@@ -139,26 +156,79 @@ litemall是一个简单的商场系统基于现有的开源项目重新实
* 订单统计
* 商品统计
## 1.3 项目特点
## 1.3 项目技术
存在以下特点:
### 1.3.1 技术参考
* 数据库方面只是简单的表表和表之间的依赖关系没有采用外键设计而是依赖Java代码在service层面或者业务层面保证。这样做的好处是数据库频繁改动很方便不会因为外键而导致数据库难以修改;
* 涉及三种技术栈,但是每种技术栈仅涉及最基础的技术;
* 后端技术栈,仅涉及 SpringSpring Boot, Spring MVC和Mybatis技术其他后端技术暂时不采用;
* 小程序技术栈仅涉及miniprogram官方文档
* 前端技术栈仅涉及vue, vuex, vue-route和element技术
* 安全方面,仅采用最基本的代码,提供简单基本的安全服务;
* 性能方面没有涉及内存数据库缓存功能而是完全依赖MySQL;
* 对象存储服务方面,支持本地存储和第三方云存储方案。
* 消息通知方面,支持邮件通知、第三方云短信通知和微信模板通知;
* 部署方便,支持多服务部署和一键部署脚本;
* 文档全面,虽然还在开发中,但是规划中文档和代码注释一定会完成,帮助开发者理解项目。
#### 1.3.1.1 Spring Boot技术
总之,目前的系统只是为了学习技术和业务而开发的一个简单商场原型系统。虽然缺失很多企业级功能,但是是完整和合理的原型系统。
Spring Boot技术栈参考以下文档或者项目
注意:
> 以上特点并不一定是优点。
1. MySQL
了解创建数据库和表、添加、查询、更新和删除即可。
2. Spring Boot 2.x
* https://docs.spring.io/spring-boot/docs/2.1.5.RELEASE/reference/htmlsingle/#getting-started-introducing-spring-boot
* https://docs.spring.io/spring-boot/docs/2.1.5.RELEASE/reference/htmlsingle/#using-boot-maven
这里需要了解RestController, Service等注解以及如何使用自动化配置。
Spring Boot支持很多功能开发者使用时查阅。
3. Mybatis
* http://www.mybatis.org/mybatis-3/
* http://www.mybatis.org/mybatis-3/java-api.html
* http://www.mybatis.org/mybatis-3/sqlmap-xml.html
这里可以简单了解而使用Mybatis Generator来生成Java代码使用即可。
4. Mybatis Generator
* http://www.mybatis.org/generator/running/runningWithMaven.html
* http://www.mybatis.org/generator/generatedobjects/results.html
* http://www.mybatis.org/generator/generatedobjects/exampleClassUsage.html
5. Mybatis PageHelper
* https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/en/HowToUse.md
#### 1.3.1.2 小程序技术
1. 小程序
* https://developers.weixin.qq.com/miniprogram/dev/index.html
* https://developers.weixin.qq.com/miniprogram/dev/component/
* https://developers.weixin.qq.com/miniprogram/dev/api/
* https://developers.weixin.qq.com/community/develop
建议小程序方面遇到问题,可以到官方社区查找。
2. 微信支付
* https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3&index=1
#### 1.3.1.3 Vue技术
1. Vue
* https://cn.vuejs.org/index.html
2. Vant
* https://youzan.github.io/vant/#/zh-CN/intro
3. Element
* https://element.eleme.cn/#/zh-CN/component/installation
4. vue-element-admin
* https://github.com/PanJiaChen/vue-element-admin
* https://panjiachen.github.io/vue-element-admin-site/zh/
### 1.3.2 项目阶段
接下来从项目的开发、部署测试和上线三个阶段介绍litemall。
@@ -173,7 +243,7 @@ litemall是一个简单的商场系统基于现有的开源项目重新实
* dep
即deploy或者deployment这里指部署测试阶段通常代码已经编译打包运行在远程主机中,
即deploy或者deployment这里指部署测试阶段通常代码已经编译打包运行在远程服务器中,
可以对外服务。此外这里服务访问地址通常是IP地址。如果IP是公网IP那么
部署以后就可以对外服务;如果是内网地址,那么只能内网访问。这里的“用户”主要是
指开发者本身、测试者当然如果是局域网或者不介意IP访问的那么这里的“用户”
@@ -181,7 +251,7 @@ litemall是一个简单的商场系统基于现有的开源项目重新实
* prod
即product或者production这里指上线阶段通常也是代码编译打包运行在远处主机中可以对外服务。
即product或者production这里指上线阶段通常也是代码编译打包运行在远处服务器中可以对外服务。
此外这里服务访问地址通常是域名地址同时端口是80web端口。上线以后直接面向的是最终用户。
虽然服务的代码本身和dep是完全一样的但是考虑到场景的不同上线阶段可能在运行环境方面需要做
调整例如采用反向代理屏蔽内部实际项目结构。此外最大的不同应该是上线环境下要使用域名和80端口
@@ -303,8 +373,8 @@ flush privilege
> 但是建议开发者开发阶段采用IDEA。
> 2. 上述步骤只是一种实践方式,开发者可不拘泥于这些步骤,多实践。
> 当然,如果开发者不采用这里步骤而出现问题,请自行解决。
> 3. 开发者使用IDEA导入项目或者运行项目时可能会出现**软件卡顿**的现象这通常是litemall-admin的
> node_modules文件夹内自动下载了大量的litemall-admin的依赖库当IDEA尝试索引该文件夹内的大量文件时
> 3. 开发者使用IDEA导入项目或者运行项目时可能会出现**软件卡顿**的现象这通常是litemall-admin或者litemall-vue
> node_modules文件夹内自动下载了大量的依赖库当IDEA尝试索引该文件夹内的大量文件时
> 则出现IDEA卡顿的现象具体解决方式可以参见[FAQ](./FAQ.md)
### 1.4.3 微信小程序开发环境
@@ -348,12 +418,12 @@ flush privilege
**项目配置结构**
1. 管理后台前端即litemall-admin模块配置文件在litemall-admin/config中,存在三个配置文件`dev.env.js`,`dep.env.js`
和`dep.env.js`。这里面配置信息都是一样,最主要的配置是`BASE_API`,即管理后台的服务根地址。
1. 管理后台前端即litemall-admin模块配置文件在litemall-admin中存在三个配置文件`env.development`,`env.deployment`
和`.env.production`。这里面配置信息都是一样,最主要的配置是`VUE_APP_BASE_API`,即管理后台的服务根地址。
* 开发阶段,开发者运行命令`cnpm run dev`,这里就会采用`dev.env.js`配置文件;
* 部署阶段,当开发者运行命令`cnpm run build:dep`,这里就会采用`dep.env.js`配置文件;
* 上线阶段,当开发者运行命令`cnpm run build:prod`,这里就会采用`prod.env.js`配置文件。
* 开发阶段,开发者运行命令`cnpm run dev`,这里就会采用`env.development`配置文件;
* 部署阶段,当开发者运行命令`cnpm run build:dep`,这里就会采用`env.deployment`配置文件;
* 上线阶段,当开发者运行命令`cnpm run build:prod`,这里就会采用`.env.production`配置文件。
2. 小商场前端即litemall-wx模块配置文件是`litemall-wx/project.config.json`和`litemall-wx/api.js`。
这里面最主要的配置信息是`project.config.json`中的`appid`开发者需要设置自己申请的appid
@@ -365,7 +435,7 @@ flush privilege
// 局域网测试使用
// var WxApiRoot = 'http://192.168.0.101:8080/wx/';
// 云平台部署时使用
// var WxApiRoot = 'http://118.24.0.153:8080/wx/';
// var WxApiRoot = 'http://122.51.199.160:8080/wx/';
// 云平台上线时使用
// var WxApiRoot = 'https://www.menethil.com.cn/wx/';
@@ -390,24 +460,21 @@ flush privilege
#### 1.4.5.1 日志配置
如果开发者启动litemall-all模块则需要配置该模块的`application.yml`文件
如果开发者启动litemall-all模块则需要配置该模块的`logback-spring.xml`文件
```
logging:
level:
root: ERROR
org.springframework: ERROR
org.mybatis: ERROR
org.linlinjava.litemall.core: ERROR
org.linlinjava.litemall.db: ERROR
org.linlinjava.litemall.admin: ERROR
org.linlinjava.litemall.wx: ERROR
org.linlinjava.litemall: ERROR
<logger name="org.mybatis" level="ERROR" />
<logger name="org.springframework" level="ERROR" />
<logger name="org.linlinjava.litemall.core" level="DEBUG" />
  <logger name="org.linlinjava.litemall.db" level="DEBUG" />
<logger name="org.linlinjava.litemall.admin" level="DEBUG" />
<logger name="org.linlinjava.litemall.wx" level="DEBUG" />
<logger name="org.linlinjava.litemall" level="DEBUG" />
```
具体如何配置请自行学习Spring Boot的日志配置。
具体如何配置请自行学习Spring Boot的日志配置和logback日志配置
`org.linlinjava.litemall.core`定义litemall-core模块的日志级别
`org.linlinjava.litemall.db`定义litemall-core模块的日志级别
`org.linlinjava.litemall.db`定义litemall-db模块的日志级别
`org.linlinjava.litemall.wx`定义litemall-wx-api模块的日志级别
`org.linlinjava.litemall.admin`定义litemall-admin-api模块的日志级别
`org.linlinjava.litemall`而定义litemall所有后端模块的日志级别
@@ -417,7 +484,7 @@ logging:
注意:
> 如果开发者独立启动litemall-wx-api模块那么则需要配置litemall-wx-api模块的
> `application.yml`文件来设置日志
> 日志配置方式。
#### 1.4.5.2 数据库连接配置
@@ -522,24 +589,33 @@ litemall:
# 短信息用于通知客户例如发货短信通知注意配置格式template-nametemplate-templateId 请参考 NotifyType 枚举值
sms:
enable: false
appid: 111111111
appkey: xxxxxxxxxxxxxx
# 如果是腾讯云短信则设置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
- 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
```
配置方式:
1. 腾讯云短信平台申请,然后设置四个场景的短信模板;
2. 开发者在配置文件设置`enable`的值`true`然后其他信息设置
腾讯云短信平台申请的appid等值。
这里只测试过腾讯云短信平台,开发者需要自行测试其他短信云平台。
1. 腾讯云短信平台或者阿里云短信平台申请,然后设置四个场景的短信模板;
2. 开发者在配置文件设置`enable`的值`true`,设置`active`的值`tencent`或`aliyun`
3. 然后配置其他信息,例如腾讯云短信平台申请的appid等值。
这里只测试过腾讯云短信平台和阿里云短信平台,开发者需要自行测试其他短信云平台。
应用场景:
目前短信通知场景只支持支付成功、验证码、订单发送、退款成功四种情况。
@@ -549,6 +625,17 @@ litemall:
当配置好信息以后开发者可以litemall-core模块的`SmsTest`测试类中设置手机号和
模板所需要的参数值,独立启动`SmsTest`测试类发送短信,然后查看手机是否成功接收短信。
短信模板参数命名:
这里存在一个问题,即腾讯云短信的官方平台中申请短信模板格式的模板参数是数组,
例如“你好,验证码是{0},时间是{1}";
而阿里云短信的官方平台中申请短信模板的模板参数是JSON,
例如“你好,验证码是{param1},时间是{param2}"。
为了保持当前代码的通用性,本项目采用数组传递参数,而对阿里云申请模板的参数做了一定的假设:
1. 腾讯云模块参数,申请模板时按照官方设置即可,例如“你好,验证码是{0},时间是{1}";
2. 阿里云模板参数,本项目假定开发者在官方申请的参数格式应该采用"{ code: xxx, code1: xxx, code2: xxx }"
例如“你好,验证码是{code},时间是{code1}"。开发者可以查看`AliyunSmsSender`类的`sendWithTemplate`方法的
源代码即可理解。如果觉得不合理,可以自行调整相关代码。
#### 1.4.5.7 微信通知配置
微信通知是微信上收到的服务通知。
@@ -650,7 +737,7 @@ litemall:
在litemall-core模块的`application-core.yml`文件中配置对象存储服务:
* 本地对象存储配置
如果开发者采用当前主机保存上传的文件,则需要配置:
如果开发者采用当前服务器保存上传的文件,则需要配置:
```
litemall:
storage:
@@ -743,42 +830,43 @@ litemall:
* litemall-wx模块部署在微信开发者工具中此外数据API地址指向litemall-wx-api所在服务qi地址
* litemall-admin编译出的静态文件放在web服务器或者tomcat服务器此外服务器地址设置指向3中litemall-admin-api所在地址
最后,**如果项目部署云主机,则根据开发者的部署环境在以下文件中或代码中修改相应的配置。**
最后,**如果项目部署云服务器,则根据开发者的部署环境在以下文件中或代码中修改相应的配置。**
1. MySQL数据库设置合适的用户名和密码信息
2. 后端服务模块设置合适的配置信息;
3. 小商场前端litemall-wx模块`config/api.js`的`WxApiRoot`设置小商场后端服务的服务地址;
4. 管理后台前端litemall-admin模块`config/dep.env.js`中的`BASE_API`设置管理后台后端服务的服务地址。
4. 管理后台前端litemall-admin模块`.env.deployment`中的`VUE_APP_BASE_API`设置管理后台后端服务的服务地址。
实际上,最终的部署方案是灵活的:
* 可以是同一云主机中安装一个Spring Boot服务同时提供litemall-admin、litemall-admin-api和litemall-wx-api三种服务
* 可以单一云主机中仅安装一个tomcat/nginx服务器部署litemall-admin静态页面分发服务
* 可以是同一云服务器中安装一个Spring Boot服务同时提供litemall-admin、litemall-admin-api和litemall-wx-api三种服务
* 可以单一云服务器中仅安装一个tomcat/nginx服务器部署litemall-admin静态页面分发服务
然后部署两个Spring Boot的后端服务
* 也可以把litemall-admin静态页面托管第三方cdn然后开发者部署两个后端服务
* 当然,甚至多个服务器,采用集群式并发提供服务。
注意
> 1. `本机`指的是是当前的开发机
> 2. `主机`指的是开发者购买并部署的远程主机
> 2. `服务器`指的是开发者购买并部署的远程服务器
以下简单列举几种方案。
### 1.5.1 单机单服务部署方案
本节介绍基于腾讯云的单机单服务部署方案,面向的是服务器数据和应用部署在云主机单机中用于演示的场景。
本节介绍基于腾讯云的单机单服务部署方案,面向的是服务器数据和应用部署在云服务器单机中用于演示的场景。
其他云应该也是可行的。
主要流程是:创建云主机安装ubuntu操作系统按照JDK和MySQL应用运行环境部署单一Spring Boot服务。
主要流程是:创建云服务器安装ubuntu操作系统按照JDK和MySQL应用运行环境部署单一Spring Boot服务。
![](pic1/1-11.png)
#### 1.5.1.1 主机
#### 1.5.1.1 云服务器
请参考腾讯云官方文档进行相关操作。
1. 创建云主机虚拟机
1. 创建云服务器
请参考腾讯云、阿里云或者其他云平台的官方文档进行相关操作。
建议最低配置是**1核2G**。
2. 安装操作系统
本项目采用ubuntu 16.04.1,但是并不限制其他操作系统。
@@ -787,48 +875,55 @@ litemall:
![](pic1/1-4.png)
目前允许的端口:808180828083808080443223306
目前允许的端口808080443223306
注意:
这里其实只需要8080端口允许其他端口只是方开发阶段的测试和调试。
这里其实只需要8080端口允许其他端口只是方便开发阶段的测试和调试。
特别是3306端口作为MySQL的远程访问端口请在上线阶段关闭。
4. 设置SSH密钥可选
建议开发者设置SSH密钥可以免密码登录云主机,以及用于脚本自动上传应用。
建议开发者设置SSH密钥可以免密码登录云服务器,以及用于脚本自动上传应用。
5. 使用PuTTY远程登录云主机
5. 使用PuTTY远程登录云服务器
如果开发者设置SSH密钥可以采用免密码登录否则采用账号和密码登录。
#### 1.5.1.2 JDK8
#### 1.5.1.2 OpenJDK8
https://www.digitalocean.com/community/tutorials/how-to-install-java-with-apt-get-on-ubuntu-16-04
http://www.webupd8.org/2012/09/install-oracle-java-8-in-ubuntu-via-ppa.html
这里可以安装openjdk-8-jre
```bash
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java8-installer
sudo apt-get install oracle-java8-set-default
sudo apt-get install openjdk-8-jre
```
警告
> "ppa:webupd8team/java" 不是Oracle官方PPA可能存在安全隐患。
如果希望采用jdk而不是jre则可以运行
```bash
sudo apt-get update
sudo apt-get install openjdk-8-jdk
```
注意
> 如果用户想采用Oracle JDK8或者其他JDK环境请查阅相关资料安装。
#### 1.5.1.3 MySQL
https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-16-04
```
sudo apt-get update
sudo apt-get install mysql-server
sudo apt-get install mysql-client
```
如果配置MySQL可以运行命令
```
sudo mysql_secure_installation
```
#### 1.5.1.4 项目打包
1. 在主机或者开发机打包项目到deploy
1. 在服务器或者开发机打包项目到deploy
```
cd litemall
cat ./litemall-db/sql/litemall_schema.sql > ./deploy/db/litemall.sql
@@ -853,26 +948,26 @@ sudo apt-get install mysql-client
2. 修改litemall文件夹下面的*.yml外部配置文件当litemall-all模块启动时会
加载外部配置文件而覆盖默认jar包内部的配置文件。
例如,配置文件中一些地方需要设置成远程主机的IP地址
例如,配置文件中一些地方需要设置成远程服务器的IP地址
此时deploy部署包结构如下
* bin
存放远程主机运行的脚本包括deploy.sh脚本和reset.sh脚本
存放远程服务器运行的脚本包括deploy.sh脚本和reset.sh脚本
* db
存放litemall数据库文件
* litemall
存放远程主机运行的代码包括litemall-all二进制可执行包和litemall外部配置文件
存放远程服务器运行的代码包括litemall-all二进制可执行包和litemall外部配置文件
* util
存放开发主机运行的脚本包括package.sh脚本和lazy.sh脚本。
由于是本地开发主机运行,因此开发者可以不用上传到远程主机
存放开发服务器运行的脚本包括package.sh脚本和lazy.sh脚本。
由于是本地开发服务器运行,因此开发者可以不用上传到远程服务器
#### 1.5.1.5 项目部署
1. 远程主机环境MySQL和JDK1.8)已经安装好,请确保云主机的安全组已经允许相应的端口。
1. 远程服务器环境MySQL和JDK1.8)已经安装好,请确保云服务器的安全组已经允许相应的端口。
2. 导入db/litemall.sql
```bash
cd /home/ubuntu/deploy/db
@@ -884,7 +979,7 @@ sudo apt-get install mysql-client
sudo ln -f -s /home/ubuntu/deploy/litemall/litemall.jar /etc/init.d/litemall
sudo service litemall start
```
4. 测试是否部署成功(xxx.xxx.xxx.xxx是云主机IP
4. 测试是否部署成功(xxx.xxx.xxx.xxx是云服务器IP
```
http://xxx.xxx.xxx.xxx:8080/wx/index/index
http://xxx.xxx.xxx.xxx:8080/admin/index/index
@@ -903,26 +998,26 @@ sudo apt-get install mysql-client
* util/packet.sh
在开发主机运行可以自动项目打包
在开发服务器运行可以自动项目打包
* util/lazy.sh
在开发主机运行可以自动项目打包、项目上传远程主机、自动登录系统执行项目部署脚本。
在开发服务器运行可以自动项目打包、项目上传远程服务器、自动登录系统执行项目部署脚本。
注意:
> 1. 开发者需要在util/lazy.sh中设置相应的远程主机登录账号和密钥文件路径。
> 2. 开发者需要在bin/reset.sh设置远程主机的MySQL的root登录账户。
> 1. 开发者需要在util/lazy.sh中设置相应的远程服务器登录账号和密钥文件路径。
> 2. 开发者需要在bin/reset.sh设置远程服务器的MySQL的root登录账户。
* bin/deploy.sh
在远程主机运行可以自动部署服务
在远程服务器运行可以自动部署服务
* bin/reset.sh
在远程主机运行可以自动项目导入数据、删除本地上传图片、再执行bin/deploy.sh部署服务。
在远程服务器运行可以自动项目导入数据、删除本地上传图片、再执行bin/deploy.sh部署服务。
注意:
> 开发者需要在bin/reset.sh设置远程主机的MySQL的root登录账户。
> 开发者需要在bin/reset.sh设置远程服务器的MySQL的root登录账户。
总结当开发者设置好配置信息以后可以在本地运行lazy.sh脚本自动一键部署:
```bash
@@ -943,8 +1038,8 @@ cd litemall
1. 专门的云数据库部署数据
2. 专门的云存储方案
3. 专门的CDN分发管理后台的静态文件
4. 一台云主机部署管理后台的后端服务
5. 一台或多台云主机部署小商场的后端服务
4. 一台云服务器部署管理后台的后端服务
5. 一台或多台云服务器部署小商场的后端服务
虽然由于环境原因没有正式测试过,但是这种简单的集群式场景应该是可行的。
在1.5.2节中所演示的三个服务是独立的,因此延伸到这里分布式是非常容易的。
@@ -993,7 +1088,7 @@ sudo apt-get update
sudo apt-get install nginx
```
有的文档会指出需要防火墙设置,但是腾讯云主机防火墙默认没有开启。
有的文档会指出需要防火墙设置,但是腾讯云服务器防火墙默认没有开启。
开发者这里自己可以开启设置,或者直接不开启。
打开浏览器,输入以下地址:
@@ -1137,9 +1232,9 @@ http://www.example.com
1. MySQL数据库设置合适的用户名和密码信息
2. 管理后台后端服务模块设置合适的配置信息建议开发者参考deploy/litemall的外部配置文件
这样可以避免开发者对模块内部的开发配置文件造成修改;
3. 管理后台前端litemall-admin模块`config/prod.env.js`中的`BASE_API`设置管理后台后端服务的服务地址。
3. 管理后台前端litemall-admin模块`.env.production`中的`VUE_APP_BASE_API`设置管理后台后端服务的服务地址。
### 1.6.5 项目评估和调整
### 1.6.5 项目评估
本项目只是参考项目,项目代码质量和功能不可能符合开发者的最终需求,
因此开发者**请务必仔细评估项目代码**。
@@ -1178,6 +1273,69 @@ litemall-admin编译得到的前端文件在第一次加载时相当耗时
建议开发者根据自己业务或架构情况优化。
### 1.6.7 项目安全
项目一旦正式上线,即对外正式服务。但是服务同时,可能会存在安全隐患甚至黑客攻击。
本节仅列举一些注意事项,欢迎开发者补充和完善。
#### 1.6.7.1 账户安全
这里的账号安全,既包括商城端用户账户,也包括管理后台端管理员账户。
目前账号安全还缺乏一点的保护措施,例如
* 用户密码失败超过阈值,则显示验证码;
* 用户密码失败超过阈值,则取消登录;
* 用户密码失败超过阈值,则需要手机验证码;
#### 1.6.7.2 关键业务记录
有关订单或者金钱相关的操作,建议开发者尽可能记录在数据库中,以便以后回溯。
#### 1.6.7.3 API统一调整
本项目公布了参考API接口如果出现BUG可能会被黑客作为入口。
建议开发者上线之前可以统一调整接口,以减少安全隐患。
#### 1.6.7.4 对账
本项目管理后台没有对账功能,建议开发者可以开发对账比对商场的状态是否正常。
#### 1.6.7.5 取消或者限制退款
本项目不支持自动退款功能,而是在管理后台通过管理员点击退款按钮来人工退款。
但是仍然可能存在隐患,例如黑客通过漏洞进入管理后台从而进行不合理的退款操作。
因此建议开发者可以取消管理后台的退款按钮,而仅仅保持退款信息,管理员可以登录
微信官方支付平台进行退款操作。
或者建议开发者基于一定的业务逻辑或场景限制管理后台的退款功能。例如,设置当天
退款限额从而保证不会产生无限退款操作。
#### 1.6.7.6 资源限制访问
一些API操作涉及到后端服务器资源因此需要做一定的限制防止有限资源被恶意消耗。
有限资源可能包括:
* 验证码
* 图片上传
一些限制措施可能包括:
* 限制单个IP的访问频率
* 限制用户上传图片数量
#### 1.6.7.n 跟踪本项目进展
一旦有开发者反馈BUG本项目会优先解决并及时上传补丁。
因此建议开发者跟踪本项目进展留意每次BUG修复的commit。
同时也希望开发者发现任何BUG都及时反馈。
目前还不存在LTS版本未来业务稳定后可能会发布。
## 1.7 项目管理
这里简述一些当前项目开发的要点。
@@ -1194,16 +1352,18 @@ litemall-admin编译得到的前端文件在第一次加载时相当耗时
* litemall-all/.gitignore
* .gitignore
开发者可以采用单一.gitignore文件。
### 1.7.2 项目自动部署
#### 1.7.2.1 deploy部署
当前项目存在deploy部署文件夹这个是上述1.5.1节部署腾讯云主机所采取的一些脚本。
当前项目存在deploy部署文件夹这个是上述1.5.1节部署腾讯云服务器所采取的一些脚本。
流程如下:
1. util脚本是当前开发主机运行,用来打包项目和上传腾讯云主机
1. util脚本是当前开发服务器运行,用来打包项目和上传腾讯云服务器
2. 打包项目时会编译打包项目相关模块到litemall和db文件夹中
3. bin脚本是云主机运行,用来安装数据库、导入数据、启动项目服务。
3. bin脚本是云服务器运行,用来安装数据库、导入数据、启动项目服务。
这里deploy部署方式比较简单不灵活开发者可以参考开发自己的项目脚本。
@@ -1333,8 +1493,8 @@ application配置文件中但是问题就是数据库信息一旦改变则其
* 503业务不支持即后端虽然定义了接口但是还没有实现功能
* 504更新数据失效即后端采用了乐观锁更新而并发更新时存在数据更新失效
* 505更新数据失败即后端数据库更新失败正常情况应该更新成功
* 6xx小商城后端业务错误码具体见litemall-admin-api模块的`AdminResponseCode`类。
* 7xx管理后台后端业务错误码具体见litemall-wx-api模块的`WxResponseCode`类。
* 6xx管理后台后端业务错误码具体见litemall-admin-api模块的`AdminResponseCode`类。
* 7xx小商城后端业务错误码具体见litemall-wx-api模块的`WxResponseCode`类。
需要指出的是小商场后端可能返回4xx、5xx和6xx错误码管理后台后端则可能返回4xx、5xx和7xx错误码。
这样设计原因是方便小商场前端和管理后台前端区别对待。
@@ -1356,6 +1516,9 @@ application配置文件中但是问题就是数据库信息一旦改变则其
和小商场前端类似,管理后台前端处理后端响应错误码也存在三种类似的处理方式。
注意:
> 这里的4xx和5xx错误码和HTTP中的4xx和5xx状态码不是一个概念。
### 1.7.7 TODO
本项目存在一些TODO**强烈建议**开发者上线前仔细审阅是否存在问题和做相应调整。

View File

@@ -12,7 +12,6 @@
目前发现存在的一些问题:
* `缺失`优惠券功能
* `缺失`商品评价中管理员回复功能,进一步地用户之间相互评价回复
* `缺失`后台服务返回的token存在有效期小商场应该自动刷新
* `缺失`账号多次登录失败,应该小商城出现图片验证码限制,或者后台账号锁定
@@ -29,9 +28,6 @@
* `改善`个人页面中实现订单部件,显示用户订单数量,同时点击以后跳转到订单的相应页面。
* `改善`商品的订单中支持订单号搜索功能
* `改善`在一些内容比较多的页面中支持“顶部”功能
* `功能`目前已经有账号登录页面,可以再支持手机短信登录方式。
* `功能`个人页面支持帮助中心
* `功能`推荐功能,基于用户的一些信息,在合适的页面给出推荐商品
## 3.0 小商场环境
@@ -87,9 +83,9 @@
2. 启动后台服务
3. 部署后台服务到云主机
3. 部署后台服务到云服务器
4. litemall-wx的api.js设置云主机的域名。
4. litemall-wx的api.js设置云服务器的域名。
编译运行,尝试微信支付。
### 3.0.3 微信退款配置
@@ -240,14 +236,14 @@ var WxApiRoot = 'http://localhost:8082/wx/';
// 局域网测试使用
// var WxApiRoot = 'http://192.168.0.101:8082/wx/';
// 云平台部署时使用
// var WxApiRoot = 'http://118.24.0.153:8082/wx/';
// var WxApiRoot = 'http://122.51.199.160:8082/wx/';
```
也就是说这里存在三种类型的API服务地址这里是考虑到开发存在三种情况
1. 本机开发时localhost是当前开发机的地址
2. 手机预览时192.168.0.101是开发机的IP地址
3. 当后台部署在云主机中时118.24.0.153是云主机的IP地址
3. 当后台部署在云服务器中时122.51.199.160是云服务器的IP地址
4. 此外,更最重要的是,如果小程序正式部署时,这里的地址必须是域名,
而不能是IP地址。

View File

@@ -9,7 +9,10 @@
<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>
@@ -21,39 +24,22 @@
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -6,7 +6,8 @@ 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"})
@SpringBootApplication(scanBasePackages = {"org.linlinjava.litemall.db", "org.linlinjava.litemall.core", "org" +
".linlinjava.litemall.admin"})
@MapperScan("org.linlinjava.litemall.db.dao")
@EnableTransactionManagement
@EnableScheduling

View File

@@ -1,7 +1,5 @@
package org.linlinjava.litemall.admin.annotation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -11,5 +9,6 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermissionsDesc {
String[] menu();
String button();
}

View File

@@ -0,0 +1,45 @@
package org.linlinjava.litemall.admin.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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
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();
}
}

View File

@@ -1,10 +1,10 @@
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.mgt.SecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.linlinjava.litemall.admin.shiro.AdminAuthorizingRealm;
import org.linlinjava.litemall.admin.shiro.AdminWebSessionManager;
@@ -25,7 +25,7 @@ public class ShiroConfig {
}
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
@@ -45,12 +45,12 @@ public class ShiroConfig {
@Bean
public SessionManager sessionManager() {
AdminWebSessionManager mySessionManager = new AdminWebSessionManager();
return mySessionManager;
return new AdminWebSessionManager();
}
@Bean
public DefaultWebSecurityManager securityManager() {
public DefaultWebSecurityManager defaultWebSecurityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm());
securityManager.setSessionManager(sessionManager());
@@ -59,7 +59,8 @@ public class ShiroConfig {
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}

View File

@@ -1,5 +1,7 @@
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;
@@ -10,20 +12,22 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
@Order( value = Ordered.HIGHEST_PRECEDENCE )
@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) {
e.printStackTrace();
logger.warn(e.getMessage(), e);
return ResponseUtil.unlogin();
}
@ExceptionHandler(AuthorizationException.class)
@ResponseBody
public Object unauthorizedHandler(AuthorizationException e) {
e.printStackTrace();
logger.warn(e.getMessage(), e);
return ResponseUtil.unauthz();
}

View File

@@ -11,6 +11,7 @@ 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;
/**
@@ -35,13 +36,13 @@ public class CouponJob {
logger.info("系统开启任务检查优惠券是否已经过期");
List<LitemallCoupon> couponList = couponService.queryExpired();
for(LitemallCoupon coupon : couponList){
for (LitemallCoupon coupon : couponList) {
coupon.setStatus(CouponConstant.STATUS_EXPIRED);
couponService.updateById(coupon);
}
List<LitemallCouponUser> couponUserList = couponUserService.queryExpired();
for(LitemallCouponUser couponUser : couponUserList){
for (LitemallCouponUser couponUser : couponUserList) {
couponUser.setStatus(CouponUserConstant.STATUS_EXPIRED);
couponUserService.update(couponUser);
}

View File

@@ -3,12 +3,8 @@ 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.LitemallGoodsProduct;
import org.linlinjava.litemall.db.domain.LitemallOrder;
import org.linlinjava.litemall.db.domain.LitemallOrderGoods;
import org.linlinjava.litemall.db.service.LitemallGoodsProductService;
import org.linlinjava.litemall.db.service.LitemallOrderGoodsService;
import org.linlinjava.litemall.db.service.LitemallOrderService;
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;
@@ -31,43 +27,10 @@ public class OrderJob {
private LitemallOrderService orderService;
@Autowired
private LitemallGoodsProductService productService;
/**
* 自动取消订单
* <p>
* 定时检查订单未付款情况,如果超时 LITEMALL_ORDER_UNPAID 分钟则自动取消订单
* 定时时间是每次相隔半个小时。
* <p>
* TODO
* 注意,因为是相隔半小时检查,因此导致订单真正超时时间是 [LITEMALL_ORDER_UNPAID, 30 + LITEMALL_ORDER_UNPAID]
*/
@Scheduled(fixedDelay = 30 * 60 * 1000)
@Transactional
public void checkOrderUnpaid() {
logger.info("系统开启任务检查订单是否已经超期自动取消订单");
List<LitemallOrder> orderList = orderService.queryUnpaid(SystemConfig.getOrderUnpaid());
for (LitemallOrder order : orderList) {
// 设置订单已取消状态
order.setOrderStatus(OrderUtil.STATUS_AUTO_CANCEL);
order.setEndTime(LocalDateTime.now());
if (orderService.updateWithOptimisticLocker(order) == 0) {
throw new RuntimeException("更新数据已失效");
}
// 商品货品数量增加
Integer orderId = order.getId();
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("商品货品库存增加失败");
}
}
logger.info("订单 ID=" + order.getId() + " 已经超期自动取消订单");
}
}
@Autowired
private LitemallGrouponService grouponService;
@Autowired
private LitemallGrouponRulesService rulesService;
/**
* 自动确认订单
@@ -80,7 +43,7 @@ public class OrderJob {
*/
@Scheduled(cron = "0 0 3 * * ?")
public void checkOrderUnconfirm() {
logger.info("系统开启任务检查订单是否已经超期自动确认收货");
logger.info("系统开启定时任务检查订单是否已经超期自动确认收货");
List<LitemallOrder> orderList = orderService.queryUnconfirm(SystemConfig.getOrderUnconfirm());
for (LitemallOrder order : orderList) {
@@ -109,7 +72,6 @@ public class OrderJob {
public void checkOrderComment() {
logger.info("系统开启任务检查订单是否已经超期未评价");
LocalDateTime now = LocalDateTime.now();
List<LitemallOrder> orderList = orderService.queryComment(SystemConfig.getOrderComment());
for (LitemallOrder order : orderList) {
order.setComments((short) 0);

View File

@@ -1,6 +1,5 @@
package org.linlinjava.litemall.admin.service;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.linlinjava.litemall.admin.dto.GoodsAllinone;
@@ -40,21 +39,13 @@ public class AdminGoodsService {
private LitemallBrandService brandService;
@Autowired
private LitemallCartService cartService;
@Autowired
private LitemallOrderGoodsService orderGoodsService;
@Autowired
private QCodeService qCodeService;
public Object list(String goodsSn, String name,
public Object list(Integer goodsId, String goodsSn, String name,
Integer page, Integer limit, String sort, String order) {
List<LitemallGoods> goodsList = goodsService.querySelective(goodsSn, name, page, limit, sort, order);
long total = PageInfo.of(goodsList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", goodsList);
return ResponseUtil.ok(data);
List<LitemallGoods> goodsList = goodsService.querySelective(goodsId, goodsSn, name, page, limit, sort, order);
return ResponseUtil.okList(goodsList);
}
private Object validate(GoodsAllinone goodsAllinone) {
@@ -129,19 +120,25 @@ public class AdminGoodsService {
/**
* 编辑商品
* <p>
* TODO
* 目前商品修改的逻辑是
* 1. 更新litemall_goods表
* 2. 逻辑删除litemall_goods_specification、litemall_goods_attribute、litemall_goods_product
* 3. 添加litemall_goods_specification、litemall_goods_attribute、litemall_goods_product
* <p>
* 这里商品三个表的数据采用删除再添加的策略是因为
* 商品编辑页面,支持管理员添加删除商品规格、添加删除商品属性,因此这里仅仅更新是不可能的,
* 只能删除三个表旧的数据,然后添加新的数据。
* 但是这里又会引入新的问题就是存在订单商品货品ID指向了失效的商品货品表。
* 因此这里会拒绝管理员编辑商品,如果订单或购物车中存在商品。
* 所以这里可能需要重新设计
*
* NOTE
* 由于商品涉及到四个表特别是litemall_goods_product表依赖litemall_goods_specification表
* 这导致允许所有字段都是可编辑会带来一些问题,因此这里商品编辑功能是受限制:
* 1litemall_goods表可以编辑字段;
* 2litemall_goods_specification表只能编辑pic_url字段其他操作不支持
* 3litemall_goods_product表只能编辑price, number和url字段其他操作不支持
* 4litemall_goods_attribute表支持编辑、添加和删除操作。
*
* NOTE2:
* 前后端这里使用了一个小技巧:
* 如果前端传来的update_time字段是空则说明前端已经更新了某个记录则这个记录会更新
* 否则说明这个记录没有编辑过,无需更新该记录
*
* NOTE3:
* 1购物车缓存了一些商品信息因此需要及时更新。
* 目前这些字段是goods_sn, goods_name, price, pic_url。
* 2但是订单里面的商品信息则是不会更新。
* 如果订单是未支付订单,此时仍然以旧的价格支付。
*/
@Transactional
public Object update(GoodsAllinone goodsAllinone) {
@@ -155,38 +152,62 @@ public class AdminGoodsService {
LitemallGoodsSpecification[] specifications = goodsAllinone.getSpecifications();
LitemallGoodsProduct[] products = goodsAllinone.getProducts();
Integer id = goods.getId();
//将生成的分享图片地址写入数据库
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();
specificationService.deleteByGid(gid);
attributeService.deleteByGid(gid);
productService.deleteByGid(gid);
// 商品规格表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);
// 目前只支持更新规格表的图片字段
if(specification.getUpdateTime() == null){
specification.setSpecification(null);
specification.setValue(null);
specificationService.updateById(specification);
}
}
// 商品货品表litemall_product
for (LitemallGoodsProduct product : products) {
product.setGoodsId(goods.getId());
productService.add(product);
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();
@@ -224,6 +245,16 @@ public class AdminGoodsService {
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);

View File

@@ -4,7 +4,6 @@ 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 com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.linlinjava.litemall.core.notify.NotifyService;
@@ -54,14 +53,9 @@ public class AdminOrderService {
public Object list(Integer userId, String orderSn, List<Short> orderStatusArray,
Integer page, Integer limit, String sort, String order) {
List<LitemallOrder> orderList = orderService.querySelective(userId, orderSn, orderStatusArray, page, limit, sort, order);
long total = PageInfo.of(orderList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", orderList);
return ResponseUtil.ok(data);
List<LitemallOrder> orderList = orderService.querySelective(userId, orderSn, orderStatusArray, page, limit,
sort, order);
return ResponseUtil.okList(orderList);
}
public Object detail(Integer id) {
@@ -126,11 +120,11 @@ public class AdminOrderService {
wxPayRefundRequest.setTotalFee(totalFee);
wxPayRefundRequest.setRefundFee(totalFee);
WxPayRefundResult wxPayRefundResult = null;
WxPayRefundResult wxPayRefundResult;
try {
wxPayRefundResult = wxPayService.refund(wxPayRefundRequest);
} catch (WxPayException e) {
e.printStackTrace();
logger.error(e.getMessage(), e);
return ResponseUtil.fail(ORDER_REFUND_FAILED, "订单退款失败");
}
if (!wxPayRefundResult.getReturnCode().equals("SUCCESS")) {
@@ -142,8 +136,15 @@ public class AdminOrderService {
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("更新数据已失效");
}
@@ -161,7 +162,8 @@ public class AdminOrderService {
//TODO 发送邮件和短信通知,这里采用异步发送
// 退款成功通知用户, 例如“您申请的订单退款 [ 单号:{1} ] 已成功,请耐心等待到账。”
// 注意订单号只发后6位
notifyService.notifySmsTemplate(order.getMobile(), NotifyType.REFUND, new String[]{order.getOrderSn().substring(8, 14)});
notifyService.notifySmsTemplate(order.getMobile(), NotifyType.REFUND,
new String[]{order.getOrderSn().substring(8, 14)});
logHelper.logOrderSucceed("退款", "订单编号 " + orderId);
return ResponseUtil.ok();

View File

@@ -19,87 +19,86 @@ import javax.servlet.http.HttpServletRequest;
* 安全日志:用户安全相关的操作日志,例如登录、删除管理员
* 订单日志:用户交易相关的操作日志,例如订单发货、退款
* 其他日志:如果以上三种不合适,可以选择其他日志,建议是优先级最低的日志级别
*
* <p>
* 当然可能很多操作是不需要记录到数据库的,例如编辑商品、编辑广告品之类。
*/
@Component
public class LogHelper {
public final static Integer LOG_TYPE_GENERAL = 0;
public final static Integer LOG_TYPE_AUTH = 1;
public final static Integer LOG_TYPE_ORDER = 2;
public final static Integer LOG_TYPE_OTHER = 3;
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){
public void logGeneralSucceed(String action) {
logAdmin(LOG_TYPE_GENERAL, action, true, "", "");
}
public void logGeneralSucceed(String action, String result){
public void logGeneralSucceed(String action, String result) {
logAdmin(LOG_TYPE_GENERAL, action, true, result, "");
}
public void logGeneralFail(String action, String error){
public void logGeneralFail(String action, String error) {
logAdmin(LOG_TYPE_GENERAL, action, false, error, "");
}
public void logAuthSucceed(String action){
public void logAuthSucceed(String action) {
logAdmin(LOG_TYPE_AUTH, action, true, "", "");
}
public void logAuthSucceed(String action, String result){
public void logAuthSucceed(String action, String result) {
logAdmin(LOG_TYPE_AUTH, action, true, result, "");
}
public void logAuthFail(String action, String error){
public void logAuthFail(String action, String error) {
logAdmin(LOG_TYPE_AUTH, action, false, error, "");
}
public void logOrderSucceed(String action){
public void logOrderSucceed(String action) {
logAdmin(LOG_TYPE_ORDER, action, true, "", "");
}
public void logOrderSucceed(String action, String result){
public void logOrderSucceed(String action, String result) {
logAdmin(LOG_TYPE_ORDER, action, true, result, "");
}
public void logOrderFail(String action, String error){
public void logOrderFail(String action, String error) {
logAdmin(LOG_TYPE_ORDER, action, false, error, "");
}
public void logOtherSucceed(String action){
public void logOtherSucceed(String action) {
logAdmin(LOG_TYPE_OTHER, action, true, "", "");
}
public void logOtherSucceed(String action, String result){
public void logOtherSucceed(String action, String result) {
logAdmin(LOG_TYPE_OTHER, action, true, result, "");
}
public void logOtherFail(String action, String error){
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){
public void logAdmin(Integer type, String action, Boolean succeed, String result, String comment) {
LitemallLog log = new LitemallLog();
Subject currentUser = SecurityUtils.getSubject();
if(currentUser != null) {
if (currentUser != null) {
LitemallAdmin admin = (LitemallAdmin) currentUser.getPrincipal();
if(admin != null) {
if (admin != null) {
log.setAdmin(admin.getUsername());
}
else{
} else {
log.setAdmin("匿名用户");
}
}
else{
} else {
log.setAdmin("匿名用户");
}
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if(request != null) {
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if (request != null) {
log.setIp(IpUtil.getIpAddr(request));
}

View File

@@ -12,8 +12,6 @@ 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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -23,7 +21,6 @@ import java.util.Set;
public class AdminAuthorizingRealm extends AuthorizingRealm {
private static final Logger log = LoggerFactory.getLogger(AdminAuthorizingRealm.class);
@Autowired
private LitemallAdminService adminService;
@Autowired
@@ -52,7 +49,7 @@ public class AdminAuthorizingRealm extends AuthorizingRealm {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();
String password=new String(upToken.getPassword());
String password = new String(upToken.getPassword());
if (StringUtils.isEmpty(username)) {
throw new AccountException("用户名不能为空");
@@ -64,16 +61,16 @@ public class AdminAuthorizingRealm extends AuthorizingRealm {
List<LitemallAdmin> adminList = adminService.findAdmin(username);
Assert.state(adminList.size() < 2, "同一个用户名存在两个账户");
if (adminList.size() == 0) {
throw new UnknownAccountException("找不到用户("+username+")的帐号信息");
throw new UnknownAccountException("找不到用户(" + username + ")的帐号信息");
}
LitemallAdmin admin = adminList.get(0);
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
if (!encoder.matches(password, admin.getPassword())) {
throw new UnknownAccountException("找不到用户("+username+")的帐号信息");
throw new UnknownAccountException("找不到用户(" + username + ")的帐号信息");
}
return new SimpleAuthenticationInfo(admin,password,getName());
return new SimpleAuthenticationInfo(admin, password, getName());
}
}

View File

@@ -17,13 +17,13 @@ public class AdminWebSessionManager extends DefaultWebSessionManager {
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
String id = WebUtils.toHttp(request).getHeader(LOGIN_TOKEN_KEY);
if (!StringUtils.isEmpty(id)) {
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);
}
} else {
return super.getSessionId(request, response);
}
}
}

View File

@@ -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));
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -20,5 +20,8 @@ public class AdminResponseCode {
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;
}

View File

@@ -2,7 +2,6 @@ package org.linlinjava.litemall.admin.util;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.linlinjava.litemall.admin.annotation.RequiresPermissionsDesc;
import org.springframework.web.bind.annotation.RequestMapping;
public class Permission {
private RequiresPermissions requiresPermissions;

View File

@@ -74,8 +74,7 @@ public class PermissionUtil {
leftPerm.setLabel(requiresPermissionsDesc.button());
leftPerm.setApi(api);
perm2.getChildren().add(leftPerm);
}
else{
} else {
// TODO
// 目前限制Controller里面每个方法的RequiresPermissionsDesc注解是唯一的
// 如果允许相同,可能会造成内部权限不一致。
@@ -100,8 +99,10 @@ public class PermissionUtil {
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);
RequiresPermissions requiresPermissions = AnnotationUtils.getAnnotation(method,
RequiresPermissions.class);
RequiresPermissionsDesc requiresPermissionsDesc = AnnotationUtils.getAnnotation(method,
RequiresPermissionsDesc.class);
if (requiresPermissions == null || requiresPermissionsDesc == null) {
continue;
@@ -143,7 +144,7 @@ public class PermissionUtil {
public static Set<String> listPermissionString(List<Permission> permissions) {
Set<String> permissionsString = new HashSet<>();
for(Permission permission : permissions){
for (Permission permission : permissions) {
permissionsString.add(permission.getRequiresPermissions().value()[0]);
}
return permissionsString;

View File

@@ -1,6 +1,5 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authz.annotation.RequiresPermissions;
@@ -16,9 +15,7 @@ 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;
@RestController
@RequestMapping("/admin/ad")
@@ -30,7 +27,7 @@ public class AdminAdController {
private LitemallAdService adService;
@RequiresPermissions("admin:ad:list")
@RequiresPermissionsDesc(menu={"推广管理" , "广告管理"}, button="查询")
@RequiresPermissionsDesc(menu = {"推广管理", "广告管理"}, button = "查询")
@GetMapping("/list")
public Object list(String name, String content,
@RequestParam(defaultValue = "1") Integer page,
@@ -38,12 +35,7 @@ public class AdminAdController {
@Sort @RequestParam(defaultValue = "add_time") String sort,
@Order @RequestParam(defaultValue = "desc") String order) {
List<LitemallAd> adList = adService.querySelective(name, content, page, limit, sort, order);
long total = PageInfo.of(adList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", adList);
return ResponseUtil.ok(data);
return ResponseUtil.okList(adList);
}
private Object validate(LitemallAd ad) {
@@ -59,7 +51,7 @@ public class AdminAdController {
}
@RequiresPermissions("admin:ad:create")
@RequiresPermissionsDesc(menu={"推广管理" , "广告管理"}, button="添加")
@RequiresPermissionsDesc(menu = {"推广管理", "广告管理"}, button = "添加")
@PostMapping("/create")
public Object create(@RequestBody LitemallAd ad) {
Object error = validate(ad);
@@ -71,15 +63,15 @@ public class AdminAdController {
}
@RequiresPermissions("admin:ad:read")
@RequiresPermissionsDesc(menu={"推广管理" , "广告管理"}, button="详情")
@RequiresPermissionsDesc(menu = {"推广管理", "广告管理"}, button = "详情")
@GetMapping("/read")
public Object read(@NotNull Integer id) {
LitemallAd brand = adService.findById(id);
return ResponseUtil.ok(brand);
LitemallAd ad = adService.findById(id);
return ResponseUtil.ok(ad);
}
@RequiresPermissions("admin:ad:update")
@RequiresPermissionsDesc(menu={"推广管理" , "广告管理"}, button="编辑")
@RequiresPermissionsDesc(menu = {"推广管理", "广告管理"}, button = "编辑")
@PostMapping("/update")
public Object update(@RequestBody LitemallAd ad) {
Object error = validate(ad);
@@ -94,7 +86,7 @@ public class AdminAdController {
}
@RequiresPermissions("admin:ad:delete")
@RequiresPermissionsDesc(menu={"推广管理" , "广告管理"}, button="删除")
@RequiresPermissionsDesc(menu = {"推广管理", "广告管理"}, button = "删除")
@PostMapping("/delete")
public Object delete(@RequestBody LitemallAd ad) {
Integer id = ad.getId();

View File

@@ -1,6 +1,5 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authz.annotation.RequiresPermissions;
@@ -18,10 +17,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/admin/address")
@@ -35,7 +31,7 @@ public class AdminAddressController {
private LitemallRegionService regionService;
@RequiresPermissions("admin:address:list")
@RequiresPermissionsDesc(menu={"用户管理" , "收货地址"}, button="查询")
@RequiresPermissionsDesc(menu = {"用户管理", "收货地址"}, button = "查询")
@GetMapping("/list")
public Object list(Integer userId, String name,
@RequestParam(defaultValue = "1") Integer page,
@@ -44,13 +40,6 @@ public class AdminAddressController {
@Order @RequestParam(defaultValue = "desc") String order) {
List<LitemallAddress> addressList = addressService.querySelective(userId, name, page, limit, sort, order);
long total = PageInfo.of(addressList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", addressList);
return ResponseUtil.ok(data);
return ResponseUtil.okList(addressList);
}
}

View File

@@ -1,6 +1,5 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.SecurityUtils;
@@ -21,9 +20,7 @@ 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.*;
@@ -39,7 +36,7 @@ public class AdminAdminController {
private LogHelper logHelper;
@RequiresPermissions("admin:admin:list")
@RequiresPermissionsDesc(menu={"系统管理" , "管理员管理"}, button="查询")
@RequiresPermissionsDesc(menu = {"系统管理", "管理员管理"}, button = "查询")
@GetMapping("/list")
public Object list(String username,
@RequestParam(defaultValue = "1") Integer page,
@@ -47,12 +44,7 @@ public class AdminAdminController {
@Sort @RequestParam(defaultValue = "add_time") String sort,
@Order @RequestParam(defaultValue = "desc") String order) {
List<LitemallAdmin> adminList = adminService.querySelective(username, page, limit, sort, order);
long total = PageInfo.of(adminList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", adminList);
return ResponseUtil.ok(data);
return ResponseUtil.okList(adminList);
}
private Object validate(LitemallAdmin admin) {
@@ -71,7 +63,7 @@ public class AdminAdminController {
}
@RequiresPermissions("admin:admin:create")
@RequiresPermissionsDesc(menu={"系统管理" , "管理员管理"}, button="添加")
@RequiresPermissionsDesc(menu = {"系统管理", "管理员管理"}, button = "添加")
@PostMapping("/create")
public Object create(@RequestBody LitemallAdmin admin) {
Object error = validate(admin);
@@ -95,7 +87,7 @@ public class AdminAdminController {
}
@RequiresPermissions("admin:admin:read")
@RequiresPermissionsDesc(menu={"系统管理" , "管理员管理"}, button="详情")
@RequiresPermissionsDesc(menu = {"系统管理", "管理员管理"}, button = "详情")
@GetMapping("/read")
public Object read(@NotNull Integer id) {
LitemallAdmin admin = adminService.findById(id);
@@ -103,7 +95,7 @@ public class AdminAdminController {
}
@RequiresPermissions("admin:admin:update")
@RequiresPermissionsDesc(menu={"系统管理" , "管理员管理"}, button="编辑")
@RequiresPermissionsDesc(menu = {"系统管理", "管理员管理"}, button = "编辑")
@PostMapping("/update")
public Object update(@RequestBody LitemallAdmin admin) {
Object error = validate(admin);
@@ -128,7 +120,7 @@ public class AdminAdminController {
}
@RequiresPermissions("admin:admin:delete")
@RequiresPermissionsDesc(menu={"系统管理" , "管理员管理"}, button="删除")
@RequiresPermissionsDesc(menu = {"系统管理", "管理员管理"}, button = "删除")
@PostMapping("/delete")
public Object delete(@RequestBody LitemallAdmin admin) {
Integer anotherAdminId = admin.getId();

View File

@@ -16,9 +16,7 @@ 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.domain.LitemallLog;
import org.linlinjava.litemall.db.service.LitemallAdminService;
import org.linlinjava.litemall.db.service.LitemallLogService;
import org.linlinjava.litemall.db.service.LitemallPermissionService;
import org.linlinjava.litemall.db.service.LitemallRoleService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -82,7 +80,16 @@ public class AdminAuthController {
adminService.updateById(admin);
logHelper.logAuthSucceed("登录");
return ResponseUtil.ok(currentUser.getSession().getId());
// 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);
}
/*
@@ -115,7 +122,7 @@ public class AdminAuthController {
data.put("roles", roles);
// NOTE
// 这里需要转换perms结构因为对于前端而已API形式的权限更容易理解
data.put("perms", toAPI(permissions));
data.put("perms", toApi(permissions));
return ResponseUtil.ok(data);
}
@@ -123,7 +130,7 @@ public class AdminAuthController {
private ApplicationContext context;
private HashMap<String, String> systemPermissionsMap = null;
private Collection<String> toAPI(Set<String> permissions) {
private Collection<String> toApi(Set<String> permissions) {
if (systemPermissionsMap == null) {
systemPermissionsMap = new HashMap<>();
final String basicPackage = "org.linlinjava.litemall.admin";
@@ -144,7 +151,7 @@ public class AdminAuthController {
apis.clear();
apis.add("*");
return apis;
// return systemPermissionsMap.values();
// return systemPermissionsMap.values();
}
}

View File

@@ -1,6 +1,5 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authz.annotation.RequiresPermissions;
@@ -17,9 +16,7 @@ import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/admin/brand")
@@ -31,7 +28,7 @@ public class AdminBrandController {
private LitemallBrandService brandService;
@RequiresPermissions("admin:brand:list")
@RequiresPermissionsDesc(menu={"商场管理" , "品牌管理"}, button="查询")
@RequiresPermissionsDesc(menu = {"商场管理", "品牌管理"}, button = "查询")
@GetMapping("/list")
public Object list(String id, String name,
@RequestParam(defaultValue = "1") Integer page,
@@ -39,12 +36,7 @@ public class AdminBrandController {
@Sort @RequestParam(defaultValue = "add_time") String sort,
@Order @RequestParam(defaultValue = "desc") String order) {
List<LitemallBrand> brandList = brandService.querySelective(id, name, page, limit, sort, order);
long total = PageInfo.of(brandList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", brandList);
return ResponseUtil.ok(data);
return ResponseUtil.okList(brandList);
}
private Object validate(LitemallBrand brand) {
@@ -66,7 +58,7 @@ public class AdminBrandController {
}
@RequiresPermissions("admin:brand:create")
@RequiresPermissionsDesc(menu={"商场管理" , "品牌管理"}, button="添加")
@RequiresPermissionsDesc(menu = {"商场管理", "品牌管理"}, button = "添加")
@PostMapping("/create")
public Object create(@RequestBody LitemallBrand brand) {
Object error = validate(brand);
@@ -78,7 +70,7 @@ public class AdminBrandController {
}
@RequiresPermissions("admin:brand:read")
@RequiresPermissionsDesc(menu={"商场管理" , "品牌管理"}, button="详情")
@RequiresPermissionsDesc(menu = {"商场管理", "品牌管理"}, button = "详情")
@GetMapping("/read")
public Object read(@NotNull Integer id) {
LitemallBrand brand = brandService.findById(id);
@@ -86,7 +78,7 @@ public class AdminBrandController {
}
@RequiresPermissions("admin:brand:update")
@RequiresPermissionsDesc(menu={"商场管理" , "品牌管理"}, button="编辑")
@RequiresPermissionsDesc(menu = {"商场管理", "品牌管理"}, button = "编辑")
@PostMapping("/update")
public Object update(@RequestBody LitemallBrand brand) {
Object error = validate(brand);
@@ -100,7 +92,7 @@ public class AdminBrandController {
}
@RequiresPermissions("admin:brand:delete")
@RequiresPermissionsDesc(menu={"商场管理" , "品牌管理"}, button="删除")
@RequiresPermissionsDesc(menu = {"商场管理", "品牌管理"}, button = "删除")
@PostMapping("/delete")
public Object delete(@RequestBody LitemallBrand brand) {
Integer id = brand.getId();

View File

@@ -29,13 +29,13 @@ public class AdminCategoryController {
private LitemallCategoryService categoryService;
@RequiresPermissions("admin:category:list")
@RequiresPermissionsDesc(menu={"商场管理" , "类目管理"}, button="查询")
@RequiresPermissionsDesc(menu = {"商场管理", "类目管理"}, button = "查询")
@GetMapping("/list")
public Object list() {
List<CategoryVo> categoryVoList = new ArrayList<>();
List<LitemallCategory> categoryList = categoryService.queryByPid(0);
for(LitemallCategory category : categoryList){
for (LitemallCategory category : categoryList) {
CategoryVo categoryVO = new CategoryVo();
categoryVO.setId(category.getId());
categoryVO.setDesc(category.getDesc());
@@ -47,7 +47,7 @@ public class AdminCategoryController {
List<CategoryVo> children = new ArrayList<>();
List<LitemallCategory> subCategoryList = categoryService.queryByPid(category.getId());
for(LitemallCategory subCategory : subCategoryList){
for (LitemallCategory subCategory : subCategoryList) {
CategoryVo subCategoryVo = new CategoryVo();
subCategoryVo.setId(subCategory.getId());
subCategoryVo.setDesc(subCategory.getDesc());
@@ -64,7 +64,7 @@ public class AdminCategoryController {
categoryVoList.add(categoryVO);
}
return ResponseUtil.ok(categoryVoList);
return ResponseUtil.okList(categoryVoList);
}
private Object validate(LitemallCategory category) {
@@ -90,7 +90,7 @@ public class AdminCategoryController {
}
@RequiresPermissions("admin:category:create")
@RequiresPermissionsDesc(menu={"商场管理" , "类目管理"}, button="添加")
@RequiresPermissionsDesc(menu = {"商场管理", "类目管理"}, button = "添加")
@PostMapping("/create")
public Object create(@RequestBody LitemallCategory category) {
Object error = validate(category);
@@ -102,7 +102,7 @@ public class AdminCategoryController {
}
@RequiresPermissions("admin:category:read")
@RequiresPermissionsDesc(menu={"商场管理" , "类目管理"}, button="详情")
@RequiresPermissionsDesc(menu = {"商场管理", "类目管理"}, button = "详情")
@GetMapping("/read")
public Object read(@NotNull Integer id) {
LitemallCategory category = categoryService.findById(id);
@@ -110,7 +110,7 @@ public class AdminCategoryController {
}
@RequiresPermissions("admin:category:update")
@RequiresPermissionsDesc(menu={"商场管理" , "类目管理"}, button="编辑")
@RequiresPermissionsDesc(menu = {"商场管理", "类目管理"}, button = "编辑")
@PostMapping("/update")
public Object update(@RequestBody LitemallCategory category) {
Object error = validate(category);
@@ -125,7 +125,7 @@ public class AdminCategoryController {
}
@RequiresPermissions("admin:category:delete")
@RequiresPermissionsDesc(menu={"商场管理" , "类目管理"}, button="删除")
@RequiresPermissionsDesc(menu = {"商场管理", "类目管理"}, button = "删除")
@PostMapping("/delete")
public Object delete(@RequestBody LitemallCategory category) {
Integer id = category.getId();
@@ -148,6 +148,6 @@ public class AdminCategoryController {
d.put("label", category.getName());
data.add(d);
}
return ResponseUtil.ok(data);
return ResponseUtil.okList(data);
}
}

View File

@@ -1,6 +1,5 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authz.annotation.RequiresPermissions;
@@ -17,9 +16,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/admin/collect")
@@ -32,7 +29,7 @@ public class AdminCollectController {
@RequiresPermissions("admin:collect:list")
@RequiresPermissionsDesc(menu={"用户管理" , "用户收藏"}, button="查询")
@RequiresPermissionsDesc(menu = {"用户管理", "用户收藏"}, button = "查询")
@GetMapping("/list")
public Object list(String userId, String valueId,
@RequestParam(defaultValue = "1") Integer page,
@@ -40,11 +37,6 @@ public class AdminCollectController {
@Sort @RequestParam(defaultValue = "add_time") String sort,
@Order @RequestParam(defaultValue = "desc") String order) {
List<LitemallCollect> collectList = collectService.querySelective(userId, valueId, page, limit, sort, order);
long total = PageInfo.of(collectList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", collectList);
return ResponseUtil.ok(data);
return ResponseUtil.okList(collectList);
}
}

View File

@@ -1,6 +1,5 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authz.annotation.RequiresPermissions;
@@ -14,9 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/admin/comment")
@@ -28,24 +25,19 @@ public class AdminCommentController {
private LitemallCommentService commentService;
@RequiresPermissions("admin:comment:list")
@RequiresPermissionsDesc(menu={"商品管理" , "评论管理"}, button="查询")
@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> brandList = commentService.querySelective(userId, valueId, page, limit, sort, order);
long total = PageInfo.of(brandList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", brandList);
return ResponseUtil.ok(data);
List<LitemallComment> commentList = commentService.querySelective(userId, valueId, page, limit, sort, order);
return ResponseUtil.okList(commentList);
}
@RequiresPermissions("admin:comment:delete")
@RequiresPermissionsDesc(menu={"商品管理" , "评论管理"}, button="删除")
@RequiresPermissionsDesc(menu = {"商品管理", "评论管理"}, button = "删除")
@PostMapping("/delete")
public Object delete(@RequestBody LitemallComment comment) {
Integer id = comment.getId();

View File

@@ -24,7 +24,7 @@ public class AdminConfigController {
private LitemallSystemConfigService systemConfigService;
@RequiresPermissions("admin:config:mall:list")
@RequiresPermissionsDesc(menu={"配置管理" , "商场配置"}, button="详情")
@RequiresPermissionsDesc(menu = {"配置管理", "商场配置"}, button = "详情")
@GetMapping("/mall")
public Object listMall() {
Map<String, String> data = systemConfigService.listMail();
@@ -32,9 +32,9 @@ public class AdminConfigController {
}
@RequiresPermissions("admin:config:mall:updateConfigs")
@RequiresPermissionsDesc(menu={"配置管理" , "商场配置"}, button="编辑")
@RequiresPermissionsDesc(menu = {"配置管理", "商场配置"}, button = "编辑")
@PostMapping("/mall")
public Object updateMall(@RequestBody String body ) {
public Object updateMall(@RequestBody String body) {
Map<String, String> data = JacksonUtil.toMap(body);
systemConfigService.updateConfig(data);
SystemConfig.updateConfigs(data);
@@ -42,7 +42,7 @@ public class AdminConfigController {
}
@RequiresPermissions("admin:config:express:list")
@RequiresPermissionsDesc(menu={"配置管理" , "运费配置"}, button="详情")
@RequiresPermissionsDesc(menu = {"配置管理", "运费配置"}, button = "详情")
@GetMapping("/express")
public Object listExpress() {
Map<String, String> data = systemConfigService.listExpress();
@@ -50,7 +50,7 @@ public class AdminConfigController {
}
@RequiresPermissions("admin:config:express:updateConfigs")
@RequiresPermissionsDesc(menu={"配置管理" , "运费配置"}, button="编辑")
@RequiresPermissionsDesc(menu = {"配置管理", "运费配置"}, button = "编辑")
@PostMapping("/express")
public Object updateExpress(@RequestBody String body) {
Map<String, String> data = JacksonUtil.toMap(body);
@@ -60,7 +60,7 @@ public class AdminConfigController {
}
@RequiresPermissions("admin:config:order:list")
@RequiresPermissionsDesc(menu={"配置管理" , "订单配置"}, button="详情")
@RequiresPermissionsDesc(menu = {"配置管理", "订单配置"}, button = "详情")
@GetMapping("/order")
public Object lisOrder() {
Map<String, String> data = systemConfigService.listOrder();
@@ -68,7 +68,7 @@ public class AdminConfigController {
}
@RequiresPermissions("admin:config:order:updateConfigs")
@RequiresPermissionsDesc(menu={"配置管理" , "订单配置"}, button="编辑")
@RequiresPermissionsDesc(menu = {"配置管理", "订单配置"}, button = "编辑")
@PostMapping("/order")
public Object updateOrder(@RequestBody String body) {
Map<String, String> data = JacksonUtil.toMap(body);
@@ -77,7 +77,7 @@ public class AdminConfigController {
}
@RequiresPermissions("admin:config:wx:list")
@RequiresPermissionsDesc(menu={"配置管理" , "小程序配置"}, button="详情")
@RequiresPermissionsDesc(menu = {"配置管理", "小程序配置"}, button = "详情")
@GetMapping("/wx")
public Object listWx() {
Map<String, String> data = systemConfigService.listWx();
@@ -85,7 +85,7 @@ public class AdminConfigController {
}
@RequiresPermissions("admin:config:wx:updateConfigs")
@RequiresPermissionsDesc(menu={"配置管理" , "小程序配置"}, button="编辑")
@RequiresPermissionsDesc(menu = {"配置管理", "小程序配置"}, button = "编辑")
@PostMapping("/wx")
public Object updateWx(@RequestBody String body) {
Map<String, String> data = JacksonUtil.toMap(body);

View File

@@ -1,6 +1,5 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authz.annotation.RequiresPermissions;
@@ -19,9 +18,7 @@ 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;
@RestController
@RequestMapping("/admin/coupon")
@@ -35,7 +32,7 @@ public class AdminCouponController {
private LitemallCouponUserService couponUserService;
@RequiresPermissions("admin:coupon:list")
@RequiresPermissionsDesc(menu={"推广管理" , "优惠券管理"}, button="查询")
@RequiresPermissionsDesc(menu = {"推广管理", "优惠券管理"}, button = "查询")
@GetMapping("/list")
public Object list(String name, Short type, Short status,
@RequestParam(defaultValue = "1") Integer page,
@@ -43,41 +40,32 @@ public class AdminCouponController {
@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);
long total = PageInfo.of(couponList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", couponList);
return ResponseUtil.ok(data);
return ResponseUtil.okList(couponList);
}
@RequiresPermissions("admin:coupon:listuser")
@RequiresPermissionsDesc(menu={"推广管理" , "优惠券管理"}, button="查询用户")
@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);
long total = PageInfo.of(couponList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", couponList);
return ResponseUtil.ok(data);
@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)){
if (StringUtils.isEmpty(name)) {
return ResponseUtil.badArgument();
}
return null;
}
@RequiresPermissions("admin:coupon:create")
@RequiresPermissionsDesc(menu={"推广管理" , "优惠券管理"}, button="添加")
@RequiresPermissionsDesc(menu = {"推广管理", "优惠券管理"}, button = "添加")
@PostMapping("/create")
public Object create(@RequestBody LitemallCoupon coupon) {
Object error = validate(coupon);
@@ -86,7 +74,7 @@ public class AdminCouponController {
}
// 如果是兑换码类型,则这里需要生存一个兑换码
if (coupon.getType().equals(CouponConstant.TYPE_CODE)){
if (coupon.getType().equals(CouponConstant.TYPE_CODE)) {
String code = couponService.generateCode();
coupon.setCode(code);
}
@@ -96,7 +84,7 @@ public class AdminCouponController {
}
@RequiresPermissions("admin:coupon:read")
@RequiresPermissionsDesc(menu={"推广管理" , "优惠券管理"}, button="详情")
@RequiresPermissionsDesc(menu = {"推广管理", "优惠券管理"}, button = "详情")
@GetMapping("/read")
public Object read(@NotNull Integer id) {
LitemallCoupon coupon = couponService.findById(id);
@@ -104,7 +92,7 @@ public class AdminCouponController {
}
@RequiresPermissions("admin:coupon:update")
@RequiresPermissionsDesc(menu={"推广管理" , "优惠券管理"}, button="编辑")
@RequiresPermissionsDesc(menu = {"推广管理", "优惠券管理"}, button = "编辑")
@PostMapping("/update")
public Object update(@RequestBody LitemallCoupon coupon) {
Object error = validate(coupon);
@@ -118,7 +106,7 @@ public class AdminCouponController {
}
@RequiresPermissions("admin:coupon:delete")
@RequiresPermissionsDesc(menu={"推广管理" , "优惠券管理"}, button="删除")
@RequiresPermissionsDesc(menu = {"推广管理", "优惠券管理"}, button = "删除")
@PostMapping("/delete")
public Object delete(@RequestBody LitemallCoupon coupon) {
couponService.deleteById(coupon.getId());

View File

@@ -1,6 +1,5 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authz.annotation.RequiresPermissions;
@@ -17,9 +16,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Yogeek
@@ -35,19 +32,15 @@ public class AdminFeedbackController {
private LitemallFeedbackService feedbackService;
@RequiresPermissions("admin:feedback:list")
@RequiresPermissionsDesc(menu={"用户管理" , "意见反馈"}, button="查询")
@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);
long total = PageInfo.of(feedbackList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", feedbackList);
return ResponseUtil.ok(data);
List<LitemallFeedback> feedbackList = feedbackService.querySelective(userId, username, page, limit, sort,
order);
return ResponseUtil.okList(feedbackList);
}
}

View File

@@ -1,6 +1,5 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authz.annotation.RequiresPermissions;
@@ -17,9 +16,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/admin/footprint")
@@ -31,19 +28,15 @@ public class AdminFootprintController {
private LitemallFootprintService footprintService;
@RequiresPermissions("admin:footprint:list")
@RequiresPermissionsDesc(menu={"用户管理" , "用户足迹"}, button="查询")
@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);
long total = PageInfo.of(footprintList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", footprintList);
return ResponseUtil.ok(data);
List<LitemallFootprint> footprintList = footprintService.querySelective(userId, goodsId, page, limit, sort,
order);
return ResponseUtil.okList(footprintList);
}
}

View File

@@ -27,6 +27,7 @@ public class AdminGoodsController {
/**
* 查询商品
*
* @param goodsId
* @param goodsSn
* @param name
* @param page
@@ -38,12 +39,12 @@ public class AdminGoodsController {
@RequiresPermissions("admin:goods:list")
@RequiresPermissionsDesc(menu = {"商品管理", "商品管理"}, button = "查询")
@GetMapping("/list")
public Object list(String goodsSn, String name,
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(goodsSn, name, page, limit, sort, order);
return adminGoodsService.list(goodsId, goodsSn, name, page, limit, sort, order);
}
@GetMapping("/catAndBrand")

View File

@@ -1,10 +1,12 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
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;
@@ -14,12 +16,15 @@ 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;
@@ -37,46 +42,43 @@ public class AdminGrouponController {
private LitemallGoodsService goodsService;
@Autowired
private LitemallGrouponService grouponService;
@Autowired
private TaskService taskService;
@RequiresPermissions("admin:groupon:read")
@RequiresPermissionsDesc(menu={"推广管理" , "团购管理"}, button="详情")
@RequiresPermissionsDesc(menu = {"推广管理", "团购管理"}, button = "详情")
@GetMapping("/listRecord")
public Object listRecord(String grouponId,
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(grouponId, page, limit, sort, order);
long total = PageInfo.of(grouponList).getTotal();
List<LitemallGroupon> grouponList = grouponService.querySelective(grouponRuleId, page, limit, sort, order);
List<Map<String, Object>> records = new ArrayList<>();
List<Map<String, Object>> groupons = new ArrayList<>();
for (LitemallGroupon groupon : grouponList) {
try {
Map<String, Object> RecordData = new HashMap<>();
Map<String, Object> recordData = new HashMap<>();
List<LitemallGroupon> subGrouponList = grouponService.queryJoinRecord(groupon.getId());
LitemallGrouponRules rules = rulesService.queryById(groupon.getRulesId());
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);
recordData.put("groupon", groupon);
recordData.put("subGroupons", subGrouponList);
recordData.put("rules", rules);
recordData.put("goods", goods);
records.add(RecordData);
groupons.add(recordData);
} catch (Exception e) {
e.printStackTrace();
logger.error(e.getMessage(), e);
}
}
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", records);
return ResponseUtil.ok(data);
return ResponseUtil.okList(groupons, grouponList);
}
@RequiresPermissions("admin:groupon:list")
@RequiresPermissionsDesc(menu={"推广管理" , "团购管理"}, button="查询")
@RequiresPermissionsDesc(menu = {"推广管理", "团购管理"}, button = "查询")
@GetMapping("/list")
public Object list(String goodsId,
@RequestParam(defaultValue = "1") Integer page,
@@ -84,12 +86,7 @@ public class AdminGrouponController {
@Sort @RequestParam(defaultValue = "add_time") String sort,
@Order @RequestParam(defaultValue = "desc") String order) {
List<LitemallGrouponRules> rulesList = rulesService.querySelective(goodsId, page, limit, sort, order);
long total = PageInfo.of(rulesList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", rulesList);
return ResponseUtil.ok(data);
return ResponseUtil.okList(rulesList);
}
private Object validate(LitemallGrouponRules grouponRules) {
@@ -114,7 +111,7 @@ public class AdminGrouponController {
}
@RequiresPermissions("admin:groupon:update")
@RequiresPermissionsDesc(menu={"推广管理" , "团购管理"}, button="编辑")
@RequiresPermissionsDesc(menu = {"推广管理", "团购管理"}, button = "编辑")
@PostMapping("/update")
public Object update(@RequestBody LitemallGrouponRules grouponRules) {
Object error = validate(grouponRules);
@@ -122,6 +119,14 @@ public class AdminGrouponController {
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) {
@@ -139,7 +144,7 @@ public class AdminGrouponController {
}
@RequiresPermissions("admin:groupon:create")
@RequiresPermissionsDesc(menu={"推广管理" , "团购管理"}, button="添加")
@RequiresPermissionsDesc(menu = {"推广管理", "团购管理"}, button = "添加")
@PostMapping("/create")
public Object create(@RequestBody LitemallGrouponRules grouponRules) {
Object error = validate(grouponRules);
@@ -150,19 +155,28 @@ public class AdminGrouponController {
Integer goodsId = grouponRules.getGoodsId();
LitemallGoods goods = goodsService.findById(goodsId);
if (goods == null) {
return ResponseUtil.badArgumentValue();
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="删除")
@RequiresPermissionsDesc(menu = {"推广管理", "团购管理"}, button = "删除")
@PostMapping("/delete")
public Object delete(@RequestBody LitemallGrouponRules grouponRules) {
Integer id = grouponRules.getId();

View File

@@ -1,6 +1,5 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authz.annotation.RequiresPermissions;
@@ -16,9 +15,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/admin/history")
@@ -29,19 +26,15 @@ public class AdminHistoryController {
private LitemallSearchHistoryService searchHistoryService;
@RequiresPermissions("admin:history:list")
@RequiresPermissionsDesc(menu={"用户管理" , "搜索历史"}, button="查询")
@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> footprintList = searchHistoryService.querySelective(userId, keyword, page, limit, sort, order);
long total = PageInfo.of(footprintList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", footprintList);
return ResponseUtil.ok(data);
List<LitemallSearchHistory> historyList = searchHistoryService.querySelective(userId, keyword, page, limit,
sort, order);
return ResponseUtil.okList(historyList);
}
}

View File

@@ -51,14 +51,14 @@ public class AdminIndexController {
}
@RequiresPermissions("index:permission:read")
@RequiresPermissionsDesc(menu={"其他" , "权限测试"}, button="权限读")
@RequiresPermissionsDesc(menu = {"其他", "权限测试"}, button = "权限读")
@GetMapping("/read")
public Object read() {
return ResponseUtil.ok("hello world, this is admin service");
}
@RequiresPermissions("index:permission:write")
@RequiresPermissionsDesc(menu={"其他" , "权限测试"}, button="权限写")
@RequiresPermissionsDesc(menu = {"其他", "权限测试"}, button = "权限写")
@PostMapping("/write")
public Object write() {
return ResponseUtil.ok("hello world, this is admin service");

View File

@@ -1,6 +1,5 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authz.annotation.RequiresPermissions;
@@ -16,9 +15,7 @@ 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;
@RestController
@RequestMapping("/admin/issue")
@@ -30,7 +27,7 @@ public class AdminIssueController {
private LitemallIssueService issueService;
@RequiresPermissions("admin:issue:list")
@RequiresPermissionsDesc(menu={"商场管理" , "通用问题"}, button="查询")
@RequiresPermissionsDesc(menu = {"商场管理", "通用问题"}, button = "查询")
@GetMapping("/list")
public Object list(String question,
@RequestParam(defaultValue = "1") Integer page,
@@ -38,12 +35,7 @@ public class AdminIssueController {
@Sort @RequestParam(defaultValue = "add_time") String sort,
@Order @RequestParam(defaultValue = "desc") String order) {
List<LitemallIssue> issueList = issueService.querySelective(question, page, limit, sort, order);
long total = PageInfo.of(issueList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", issueList);
return ResponseUtil.ok(data);
return ResponseUtil.okList(issueList);
}
private Object validate(LitemallIssue issue) {
@@ -59,7 +51,7 @@ public class AdminIssueController {
}
@RequiresPermissions("admin:issue:create")
@RequiresPermissionsDesc(menu={"商场管理" , "通用问题"}, button="添加")
@RequiresPermissionsDesc(menu = {"商场管理", "通用问题"}, button = "添加")
@PostMapping("/create")
public Object create(@RequestBody LitemallIssue issue) {
Object error = validate(issue);
@@ -78,7 +70,7 @@ public class AdminIssueController {
}
@RequiresPermissions("admin:issue:update")
@RequiresPermissionsDesc(menu={"商场管理" , "通用问题"}, button="编辑")
@RequiresPermissionsDesc(menu = {"商场管理", "通用问题"}, button = "编辑")
@PostMapping("/update")
public Object update(@RequestBody LitemallIssue issue) {
Object error = validate(issue);
@@ -93,7 +85,7 @@ public class AdminIssueController {
}
@RequiresPermissions("admin:issue:delete")
@RequiresPermissionsDesc(menu={"商场管理" , "通用问题"}, button="删除")
@RequiresPermissionsDesc(menu = {"商场管理", "通用问题"}, button = "删除")
@PostMapping("/delete")
public Object delete(@RequestBody LitemallIssue issue) {
Integer id = issue.getId();

View File

@@ -1,6 +1,5 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authz.annotation.RequiresPermissions;
@@ -16,9 +15,7 @@ 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;
@RestController
@RequestMapping("/admin/keyword")
@@ -30,7 +27,7 @@ public class AdminKeywordController {
private LitemallKeywordService keywordService;
@RequiresPermissions("admin:keyword:list")
@RequiresPermissionsDesc(menu={"商场管理" , "关键词"}, button="查询")
@RequiresPermissionsDesc(menu = {"商场管理", "关键词"}, button = "查询")
@GetMapping("/list")
public Object list(String keyword, String url,
@RequestParam(defaultValue = "1") Integer page,
@@ -38,12 +35,7 @@ public class AdminKeywordController {
@Sort @RequestParam(defaultValue = "add_time") String sort,
@Order @RequestParam(defaultValue = "desc") String order) {
List<LitemallKeyword> keywordList = keywordService.querySelective(keyword, url, page, limit, sort, order);
long total = PageInfo.of(keywordList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", keywordList);
return ResponseUtil.ok(data);
return ResponseUtil.okList(keywordList);
}
private Object validate(LitemallKeyword keywords) {
@@ -55,19 +47,19 @@ public class AdminKeywordController {
}
@RequiresPermissions("admin:keyword:create")
@RequiresPermissionsDesc(menu={"商场管理" , "关键词"}, button="添加")
@RequiresPermissionsDesc(menu = {"商场管理", "关键词"}, button = "添加")
@PostMapping("/create")
public Object create(@RequestBody LitemallKeyword keywords) {
Object error = validate(keywords);
public Object create(@RequestBody LitemallKeyword keyword) {
Object error = validate(keyword);
if (error != null) {
return error;
}
keywordService.add(keywords);
return ResponseUtil.ok(keywords);
keywordService.add(keyword);
return ResponseUtil.ok(keyword);
}
@RequiresPermissions("admin:keyword:read")
@RequiresPermissionsDesc(menu={"商场管理" , "关键词"}, button="详情")
@RequiresPermissionsDesc(menu = {"商场管理", "关键词"}, button = "详情")
@GetMapping("/read")
public Object read(@NotNull Integer id) {
LitemallKeyword keyword = keywordService.findById(id);
@@ -75,21 +67,21 @@ public class AdminKeywordController {
}
@RequiresPermissions("admin:keyword:update")
@RequiresPermissionsDesc(menu={"商场管理" , "关键词"}, button="编辑")
@RequiresPermissionsDesc(menu = {"商场管理", "关键词"}, button = "编辑")
@PostMapping("/update")
public Object update(@RequestBody LitemallKeyword keywords) {
Object error = validate(keywords);
public Object update(@RequestBody LitemallKeyword keyword) {
Object error = validate(keyword);
if (error != null) {
return error;
}
if (keywordService.updateById(keywords) == 0) {
if (keywordService.updateById(keyword) == 0) {
return ResponseUtil.updatedDataFailed();
}
return ResponseUtil.ok(keywords);
return ResponseUtil.ok(keyword);
}
@RequiresPermissions("admin:keyword:delete")
@RequiresPermissionsDesc(menu={"商场管理" , "关键词"}, button="删除")
@RequiresPermissionsDesc(menu = {"商场管理", "关键词"}, button = "删除")
@PostMapping("/delete")
public Object delete(@RequestBody LitemallKeyword keyword) {
Integer id = keyword.getId();

View File

@@ -1,6 +1,5 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authz.annotation.RequiresPermissions;
@@ -12,12 +11,12 @@ 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.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/admin/log")
@@ -29,7 +28,7 @@ public class AdminLogController {
private LitemallLogService logService;
@RequiresPermissions("admin:log:list")
@RequiresPermissionsDesc(menu={"系统管理" , "操作日志"}, button="查询")
@RequiresPermissionsDesc(menu = {"系统管理", "操作日志"}, button = "查询")
@GetMapping("/list")
public Object list(String name,
@RequestParam(defaultValue = "1") Integer page,
@@ -37,11 +36,6 @@ public class AdminLogController {
@Sort @RequestParam(defaultValue = "add_time") String sort,
@Order @RequestParam(defaultValue = "desc") String order) {
List<LitemallLog> logList = logService.querySelective(name, page, limit, sort, order);
long total = PageInfo.of(logList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", logList);
return ResponseUtil.ok(data);
return ResponseUtil.okList(logList);
}
}

View File

@@ -5,6 +5,9 @@ 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;
@@ -22,6 +25,8 @@ public class AdminOrderController {
@Autowired
private AdminOrderService adminOrderService;
@Autowired
private ExpressService expressService;
/**
* 查询订单
@@ -47,6 +52,16 @@ public class AdminOrderController {
return adminOrderService.list(userId, orderSn, orderStatusArray, page, limit, sort, order);
}
/**
* 查询物流公司
*
* @return
*/
@GetMapping("/channel")
public Object channel() {
return ResponseUtil.ok(expressService.getVendors());
}
/**
* 订单详情
*
@@ -99,5 +114,4 @@ public class AdminOrderController {
public Object reply(@RequestBody String body) {
return adminOrderService.reply(body);
}
}

View File

@@ -37,17 +37,7 @@ public class AdminRegionController {
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));
List<LitemallRegion> provinceList = regionService.queryByPid(0);
for (LitemallRegion province : provinceList) {
RegionVo provinceVO = new RegionVo();
provinceVO.setId(province.getId());
@@ -57,7 +47,7 @@ public class AdminRegionController {
List<LitemallRegion> cityList = cityListMap.get(province.getId());
List<RegionVo> cityVOList = new ArrayList<>();
for (LitemallRegion cityVo : cityList) {
for (LitemallRegion city : cityList) {
RegionVo cityVO = new RegionVo();
cityVO.setId(cityVo.getId());
cityVO.setName(cityVo.getName());

View File

@@ -1,14 +1,13 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
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.vo.PermVo;
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;
@@ -45,7 +44,7 @@ public class AdminRoleController {
private LitemallAdminService adminService;
@RequiresPermissions("admin:role:list")
@RequiresPermissionsDesc(menu={"系统管理" , "角色管理"}, button="角色查询")
@RequiresPermissionsDesc(menu = {"系统管理", "角色管理"}, button = "角色查询")
@GetMapping("/list")
public Object list(String name,
@RequestParam(defaultValue = "1") Integer page,
@@ -53,16 +52,11 @@ public class AdminRoleController {
@Sort @RequestParam(defaultValue = "add_time") String sort,
@Order @RequestParam(defaultValue = "desc") String order) {
List<LitemallRole> roleList = roleService.querySelective(name, page, limit, sort, order);
long total = PageInfo.of(roleList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", roleList);
return ResponseUtil.ok(data);
return ResponseUtil.okList(roleList);
}
@GetMapping("/options")
public Object options(){
public Object options() {
List<LitemallRole> roleList = roleService.queryAll();
List<Map<String, Object>> options = new ArrayList<>(roleList.size());
@@ -73,11 +67,11 @@ public class AdminRoleController {
options.add(option);
}
return ResponseUtil.ok(options);
return ResponseUtil.okList(options);
}
@RequiresPermissions("admin:role:read")
@RequiresPermissionsDesc(menu={"系统管理" , "角色管理"}, button="角色详情")
@RequiresPermissionsDesc(menu = {"系统管理", "角色管理"}, button = "角色详情")
@GetMapping("/read")
public Object read(@NotNull Integer id) {
LitemallRole role = roleService.findById(id);
@@ -95,7 +89,7 @@ public class AdminRoleController {
}
@RequiresPermissions("admin:role:create")
@RequiresPermissionsDesc(menu={"系统管理" , "角色管理"}, button="角色添加")
@RequiresPermissionsDesc(menu = {"系统管理", "角色管理"}, button = "角色添加")
@PostMapping("/create")
public Object create(@RequestBody LitemallRole role) {
Object error = validate(role);
@@ -103,7 +97,7 @@ public class AdminRoleController {
return error;
}
if (roleService.checkExist(role.getName())){
if (roleService.checkExist(role.getName())) {
return ResponseUtil.fail(ROLE_NAME_EXIST, "角色已经存在");
}
@@ -113,7 +107,7 @@ public class AdminRoleController {
}
@RequiresPermissions("admin:role:update")
@RequiresPermissionsDesc(menu={"系统管理" , "角色管理"}, button="角色编辑")
@RequiresPermissionsDesc(menu = {"系统管理", "角色管理"}, button = "角色编辑")
@PostMapping("/update")
public Object update(@RequestBody LitemallRole role) {
Object error = validate(role);
@@ -126,7 +120,7 @@ public class AdminRoleController {
}
@RequiresPermissions("admin:role:delete")
@RequiresPermissionsDesc(menu={"系统管理" , "角色管理"}, button="角色删除")
@RequiresPermissionsDesc(menu = {"系统管理", "角色管理"}, button = "角色删除")
@PostMapping("/delete")
public Object delete(@RequestBody LitemallRole role) {
Integer id = role.getId();
@@ -136,10 +130,10 @@ public class AdminRoleController {
// 如果当前角色所对应管理员仍存在,则拒绝删除角色。
List<LitemallAdmin> adminList = adminService.all();
for(LitemallAdmin admin : adminList){
for (LitemallAdmin admin : adminList) {
Integer[] roleIds = admin.getRoleIds();
for(Integer roleId : roleIds){
if(id.equals(roleId)){
for (Integer roleId : roleIds) {
if (id.equals(roleId)) {
return ResponseUtil.fail(ROLE_USER_EXIST, "当前角色存在管理员,不能删除");
}
}
@@ -155,9 +149,9 @@ public class AdminRoleController {
private List<PermVo> systemPermissions = null;
private Set<String> systemPermissionsString = null;
private List<PermVo> getSystemPermissions(){
private List<PermVo> getSystemPermissions() {
final String basicPackage = "org.linlinjava.litemall.admin";
if(systemPermissions == null){
if (systemPermissions == null) {
List<Permission> permissions = PermissionUtil.listPermission(context, basicPackage);
systemPermissions = PermissionUtil.listPermVo(permissions);
systemPermissionsString = PermissionUtil.listPermissionString(permissions);
@@ -165,15 +159,14 @@ public class AdminRoleController {
return systemPermissions;
}
private Set<String> getAssignedPermissions(Integer roleId){
private Set<String> getAssignedPermissions(Integer roleId) {
// 这里需要注意的是,如果存在超级权限*,那么这里需要转化成当前所有系统权限。
// 之所以这么做,是因为前端不能识别超级权限,所以这里需要转换一下。
Set<String> assignedPermissions = null;
if(permissionService.checkSuperPermission(roleId)){
if (permissionService.checkSuperPermission(roleId)) {
getSystemPermissions();
assignedPermissions = systemPermissionsString;
}
else{
} else {
assignedPermissions = permissionService.queryByRoleId(roleId);
}
@@ -186,7 +179,7 @@ public class AdminRoleController {
* @return 系统所有权限列表和管理员已分配权限
*/
@RequiresPermissions("admin:role:permission:get")
@RequiresPermissionsDesc(menu={"系统管理" , "角色管理"}, button="权限详情")
@RequiresPermissionsDesc(menu = {"系统管理", "角色管理"}, button = "权限详情")
@GetMapping("/permissions")
public Object getPermissions(Integer roleId) {
List<PermVo> systemPermissions = getSystemPermissions();
@@ -206,23 +199,23 @@ public class AdminRoleController {
* @return
*/
@RequiresPermissions("admin:role:permission:update")
@RequiresPermissionsDesc(menu={"系统管理" , "角色管理"}, button="权限变更")
@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){
if (roleId == null || permissions == null) {
return ResponseUtil.badArgument();
}
// 如果修改的角色是超级权限,则拒绝修改。
if(permissionService.checkSuperPermission(roleId)){
if (permissionService.checkSuperPermission(roleId)) {
return ResponseUtil.fail(AdminResponseCode.ROLE_SUPER_SUPERMISSION, "当前角色的超级权限不能变更");
}
// 先删除旧的权限,再更新新的权限
permissionService.deleteByRoleId(roleId);
for(String permission : permissions){
for (String permission : permissions) {
LitemallPermission litemallPermission = new LitemallPermission();
litemallPermission.setRoleId(roleId);
litemallPermission.setPermission(permission);

View File

@@ -26,7 +26,7 @@ public class AdminStatController {
private StatService statService;
@RequiresPermissions("admin:stat:user")
@RequiresPermissionsDesc(menu={"统计管理" , "用户统计"}, button="查询")
@RequiresPermissionsDesc(menu = {"统计管理", "用户统计"}, button = "查询")
@GetMapping("/user")
public Object statUser() {
List<Map> rows = statService.statUser();
@@ -38,7 +38,7 @@ public class AdminStatController {
}
@RequiresPermissions("admin:stat:order")
@RequiresPermissionsDesc(menu={"统计管理" , "订单统计"}, button="查询")
@RequiresPermissionsDesc(menu = {"统计管理", "订单统计"}, button = "查询")
@GetMapping("/order")
public Object statOrder() {
List<Map> rows = statService.statOrder();
@@ -51,7 +51,7 @@ public class AdminStatController {
}
@RequiresPermissions("admin:stat:goods")
@RequiresPermissionsDesc(menu={"统计管理" , "商品统计"}, button="查询")
@RequiresPermissionsDesc(menu = {"统计管理", "商品统计"}, button = "查询")
@GetMapping("/goods")
public Object statGoods() {
List<Map> rows = statService.statGoods();

View File

@@ -1,6 +1,5 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authz.annotation.RequiresPermissions;
@@ -19,9 +18,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/admin/storage")
@@ -35,7 +32,7 @@ public class AdminStorageController {
private LitemallStorageService litemallStorageService;
@RequiresPermissions("admin:storage:list")
@RequiresPermissionsDesc(menu={"系统管理" , "对象存储"}, button="查询")
@RequiresPermissionsDesc(menu = {"系统管理", "对象存储"}, button = "查询")
@GetMapping("/list")
public Object list(String key, String name,
@RequestParam(defaultValue = "1") Integer page,
@@ -43,25 +40,21 @@ public class AdminStorageController {
@Sort @RequestParam(defaultValue = "add_time") String sort,
@Order @RequestParam(defaultValue = "desc") String order) {
List<LitemallStorage> storageList = litemallStorageService.querySelective(key, name, page, limit, sort, order);
long total = PageInfo.of(storageList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", storageList);
return ResponseUtil.ok(data);
return ResponseUtil.okList(storageList);
}
@RequiresPermissions("admin:storage:create")
@RequiresPermissionsDesc(menu={"系统管理" , "对象存储"}, button="上传")
@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);
LitemallStorage litemallStorage = storageService.store(file.getInputStream(), file.getSize(),
file.getContentType(), originalFilename);
return ResponseUtil.ok(litemallStorage);
}
@RequiresPermissions("admin:storage:read")
@RequiresPermissionsDesc(menu={"系统管理" , "对象存储"}, button="详情")
@RequiresPermissionsDesc(menu = {"系统管理", "对象存储"}, button = "详情")
@PostMapping("/read")
public Object read(@NotNull Integer id) {
LitemallStorage storageInfo = litemallStorageService.findById(id);
@@ -72,7 +65,7 @@ public class AdminStorageController {
}
@RequiresPermissions("admin:storage:update")
@RequiresPermissionsDesc(menu={"系统管理" , "对象存储"}, button="编辑")
@RequiresPermissionsDesc(menu = {"系统管理", "对象存储"}, button = "编辑")
@PostMapping("/update")
public Object update(@RequestBody LitemallStorage litemallStorage) {
if (litemallStorageService.update(litemallStorage) == 0) {
@@ -82,7 +75,7 @@ public class AdminStorageController {
}
@RequiresPermissions("admin:storage:delete")
@RequiresPermissionsDesc(menu={"系统管理" , "对象存储"}, button="删除")
@RequiresPermissionsDesc(menu = {"系统管理", "对象存储"}, button = "删除")
@PostMapping("/delete")
public Object delete(@RequestBody LitemallStorage litemallStorage) {
String key = litemallStorage.getKey();

View File

@@ -1,6 +1,5 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authz.annotation.RequiresPermissions;
@@ -8,7 +7,9 @@ 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.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;
@@ -17,6 +18,7 @@ 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;
@@ -29,9 +31,11 @@ public class AdminTopicController {
@Autowired
private LitemallTopicService topicService;
@Autowired
private LitemallGoodsService goodsService;
@RequiresPermissions("admin:topic:list")
@RequiresPermissionsDesc(menu={"推广管理" , "专题管理"}, button="查询")
@RequiresPermissionsDesc(menu = {"推广管理", "专题管理"}, button = "查询")
@GetMapping("/list")
public Object list(String title, String subtitle,
@RequestParam(defaultValue = "1") Integer page,
@@ -39,12 +43,7 @@ public class AdminTopicController {
@Sort @RequestParam(defaultValue = "add_time") String sort,
@Order @RequestParam(defaultValue = "desc") String order) {
List<LitemallTopic> topicList = topicService.querySelective(title, subtitle, page, limit, sort, order);
long total = PageInfo.of(topicList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", topicList);
return ResponseUtil.ok(data);
return ResponseUtil.okList(topicList);
}
private Object validate(LitemallTopic topic) {
@@ -64,7 +63,7 @@ public class AdminTopicController {
}
@RequiresPermissions("admin:topic:create")
@RequiresPermissionsDesc(menu={"推广管理" , "专题管理"}, button="添加")
@RequiresPermissionsDesc(menu = {"推广管理", "专题管理"}, button = "添加")
@PostMapping("/create")
public Object create(@RequestBody LitemallTopic topic) {
Object error = validate(topic);
@@ -76,15 +75,25 @@ public class AdminTopicController {
}
@RequiresPermissions("admin:topic:read")
@RequiresPermissionsDesc(menu={"推广管理" , "专题管理"}, button="详情")
@RequiresPermissionsDesc(menu = {"推广管理", "专题管理"}, button = "详情")
@GetMapping("/read")
public Object read(@NotNull Integer id) {
LitemallTopic topic = topicService.findById(id);
return ResponseUtil.ok(topic);
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="编辑")
@RequiresPermissionsDesc(menu = {"推广管理", "专题管理"}, button = "编辑")
@PostMapping("/update")
public Object update(@RequestBody LitemallTopic topic) {
Object error = validate(topic);
@@ -98,7 +107,7 @@ public class AdminTopicController {
}
@RequiresPermissions("admin:topic:delete")
@RequiresPermissionsDesc(menu={"推广管理" , "专题管理"}, button="删除")
@RequiresPermissionsDesc(menu = {"推广管理", "专题管理"}, button = "删除")
@PostMapping("/delete")
public Object delete(@RequestBody LitemallTopic topic) {
topicService.deleteById(topic.getId());

View File

@@ -1,28 +1,22 @@
package org.linlinjava.litemall.admin.web;
import com.github.pagehelper.PageInfo;
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.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.LitemallUser;
import org.linlinjava.litemall.db.service.LitemallUserService;
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.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotEmpty;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.linlinjava.litemall.admin.util.AdminResponseCode.*;
@RestController
@RequestMapping("/admin/user")
@@ -34,7 +28,7 @@ public class AdminUserController {
private LitemallUserService userService;
@RequiresPermissions("admin:user:list")
@RequiresPermissionsDesc(menu={"用户管理" , "会员管理"}, button="查询")
@RequiresPermissionsDesc(menu = {"用户管理", "会员管理"}, button = "查询")
@GetMapping("/list")
public Object list(String username, String mobile,
@RequestParam(defaultValue = "1") Integer page,
@@ -42,11 +36,6 @@ public class AdminUserController {
@Sort @RequestParam(defaultValue = "add_time") String sort,
@Order @RequestParam(defaultValue = "desc") String order) {
List<LitemallUser> userList = userService.querySelective(username, mobile, page, limit, sort, order);
long total = PageInfo.of(userList).getTotal();
Map<String, Object> data = new HashMap<>();
data.put("total", total);
data.put("items", userList);
return ResponseUtil.ok(data);
return ResponseUtil.okList(userList);
}
}

View File

@@ -2,9 +2,9 @@ package org.linlinjava.litemall.admin;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.linlinjava.litemall.admin.vo.PermVo;
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;

View File

@@ -1,17 +0,0 @@
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"],
"env": {
"development":{
"plugins": ["dynamic-import-node"]
}
}
}

View File

@@ -1,4 +1,4 @@
# http://editorconfig.org
# https://editorconfig.org
root = true
[*]

View File

@@ -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'

View File

@@ -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

View File

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

View File

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

View File

@@ -21,7 +21,10 @@ module.exports = {
"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,
@@ -44,7 +47,7 @@ module.exports = {
'curly': [2, 'multi-line'],
'dot-location': [2, 'property'],
'eol-last': 2,
'eqeqeq': [2, 'allow-null'],
'eqeqeq': ["error", "always", {"null": "ignore"}],
'generator-star-spacing': [2, {
'before': true,
'after': true

View File

@@ -6,8 +6,8 @@ yarn-debug.log*
yarn-error.log*
**/*.log
test/unit/coverage
test/e2e/reports
tests/**/coverage/
tests/e2e/reports
selenium-debug.log
# Editor directories and files
@@ -17,5 +17,7 @@ selenium-debug.log
*.ntvs*
*.njsproj
*.sln
*.local
package-lock.json
yarn.lock

View File

@@ -1,10 +0,0 @@
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-url": {},
// to edit target browsers: use "browserslist" field in package.json
"autoprefixer": {}
}
}

View File

@@ -1,5 +0,0 @@
language: node_js
node_js: stable
script: npm run test
notifications:
email: false

View File

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

View File

@@ -1,67 +1,35 @@
'use strict'
require('./check-versions')()
const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const { run } = require('runjs')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')
var connect = require('connect')
var serveStatic = require('serve-static')
const config = require('../vue.config.js')
const rawArgv = process.argv.slice(2)
const args = rawArgv.join(' ')
const spinner = ora(
'building for ' + process.env.env_config + ' environment...'
)
spinner.start()
if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
const report = rawArgv.includes('--report')
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
process.stdout.write(
stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n\n'
)
run(`vue-cli-service build ${args}`)
if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1)
const port = 9526
const publicPath = config.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`))
}
console.log(chalk.cyan(' Build complete.\n'))
console.log(
chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
" Opening index.html over file:// won't work.\n"
)
)
if (process.env.npm_config_preview) {
const port = 9526
const host = 'http://localhost:' + port
const basePath = config.build.assetsPublicPath
const app = connect()
app.use(
basePath,
serveStatic('./dist', {
index: ['index.html', '/']
})
)
app.listen(port, function() {
console.log(
chalk.green(`> Listening at http://localhost:${port}${basePath}`)
)
})
}
})
})
} else {
run(`vue-cli-service build ${args}`)
}

View File

@@ -1,64 +0,0 @@
'use strict'
const chalk = require('chalk')
const semver = require('semver')
const packageConfig = require('../package.json')
const shell = require('shelljs')
function exec(cmd) {
return require('child_process')
.execSync(cmd)
.toString()
.trim()
}
const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function() {
const warnings = []
for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(
mod.name +
': ' +
chalk.red(mod.currentVersion) +
' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(
chalk.yellow(
'To use this template, you must update following to modules:'
)
)
console.log()
for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -1,108 +0,0 @@
'use strict'
const path = require('path')
const config = require('../config')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const packageConfig = require('../package.json')
exports.assetsPath = function(_path) {
const assetsSubDirectory =
process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function(options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders(loader, loaderOptions) {
const loaders = []
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
loaders.push(MiniCssExtractPlugin.loader)
} else {
loaders.push('vue-style-loader')
}
loaders.push(cssLoader)
if (options.usePostCSS) {
loaders.push(postcssLoader)
}
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
return loaders
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', {
indentedSyntax: true
}),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function(options) {
const output = []
const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
exports.createNotifierCallback = () => {
const notifier = require('node-notifier')
return (severity, errors) => {
if (severity !== 'error') return
const error = errors[0]
const filename = error.file && error.file.split('!').pop()
notifier.notify({
title: packageConfig.name,
message: severity + ': ' + error.name,
subtitle: filename || '',
icon: path.join(__dirname, 'logo.png')
})
}
}

View File

@@ -1,5 +0,0 @@
'use strict'
module.exports = {
//You can set the vue-loader configuration by yourself.
}

View File

@@ -1,107 +0,0 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const { VueLoaderPlugin } = require('vue-loader')
const vueLoaderConfig = require('./vue-loader.conf')
function resolve(dir) {
return path.join(__dirname, '..', dir)
}
const createLintingRule = () => ({
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter'),
emitWarning: !config.dev.showEslintErrorsInOverlay
}
})
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath:
process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src')
}
},
module: {
rules: [
...(config.dev.useEslint ? [createLintingRule()] : []),
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader?cacheDirectory',
include: [
resolve('src'),
resolve('test'),
resolve('node_modules/webpack-dev-server/client')
]
},
{
test: /\.svg$/,
loader: 'svg-sprite-loader',
include: [resolve('src/icons')],
options: {
symbolId: 'icon-[name]'
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
exclude: [resolve('src/icons')],
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
plugins: [new VueLoaderPlugin()],
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}

View File

@@ -1,98 +0,0 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
function resolve(dir) {
return path.join(__dirname, '..', dir)
}
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
mode: 'development',
module: {
rules: utils.styleLoaders({
sourceMap: config.dev.cssSourceMap,
usePostCSS: true
})
},
// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,
// these devServer options should be customized in /config/index.js
devServer: {
clientLogLevel: 'warning',
historyApiFallback: true,
hot: true,
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
new webpack.HotModuleReplacementPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true,
favicon: resolve('favicon.ico'),
title: 'vue-element-admin',
templateParameters: {
BASE_URL: config.dev.assetsPublicPath + config.dev.assetsSubDirectory,
},
}),
]
})
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => {
if (err) {
reject(err)
} else {
// publish the new Port, necessary for e2e tests
process.env.PORT = port
// add port to devServer config
devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push(
new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [
`Your application is running here: http://${
devWebpackConfig.devServer.host
}:${port}`
]
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
})
)
resolve(devWebpackConfig)
}
})
})

View File

@@ -1,188 +0,0 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
function resolve(dir) {
return path.join(__dirname, '..', dir)
}
const env = require('../config/' + process.env.env_config + '.env')
// For NamedChunksPlugin
const seen = new Set()
const nameLength = 4
const webpackConfig = merge(baseWebpackConfig, {
mode: 'production',
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash:8].js'),
chunkFilename: utils.assetsPath('js/[name].[chunkhash:8].js')
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
'process.env': env
}),
// extract css into its own file
new MiniCssExtractPlugin({
filename: utils.assetsPath('css/[name].[contenthash:8].css'),
chunkFilename: utils.assetsPath('css/[name].[contenthash:8].css')
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
inject: true,
favicon: resolve('favicon.ico'),
title: 'vue-element-admin',
templateParameters: {
BASE_URL: config.build.assetsPublicPath + config.build.assetsSubDirectory,
},
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
}
// default sort mode uses toposort which cannot handle cyclic deps
// in certain cases, and in webpack 4, chunk order in HTML doesn't
// matter anyway
}),
new ScriptExtHtmlWebpackPlugin({
//`runtime` must same as runtimeChunk name. default is `runtime`
inline: /runtime\..*\.js$/
}),
// keep chunk.id stable when chunk has no name
new webpack.NamedChunksPlugin(chunk => {
if (chunk.name) {
return chunk.name
}
const modules = Array.from(chunk.modulesIterable)
if (modules.length > 1) {
const hash = require('hash-sum')
const joinedHash = hash(modules.map(m => m.id).join('_'))
let len = nameLength
while (seen.has(joinedHash.substr(0, len))) len++
seen.add(joinedHash.substr(0, len))
return `chunk-${joinedHash.substr(0, len)}`
} else {
return modules[0].id
}
}),
// keep module.id stable when vender modules does not change
new webpack.HashedModuleIdsPlugin(),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
],
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial' // 只打包初始时依赖的第三方
},
elementUI: {
name: 'chunk-elementUI', // 单独将 elementUI 拆包
priority: 20, // 权重要大于 libs 和 app 不然会被打包进 libs 或者 app
test: /[\\/]node_modules[\\/]element-ui[\\/]/
},
commons: {
name: 'chunk-commons',
test: resolve('src/components'), // 可自定义拓展你的规则
minChunks: 3, // 最小公用次数
priority: 5,
reuseExistingChunk: true
}
}
},
runtimeChunk: 'single',
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
mangle: {
safari10: true
}
},
sourceMap: config.build.productionSourceMap,
cache: true,
parallel: true
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSAssetsPlugin()
]
}
})
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' + config.build.productionGzipExtensions.join('|') + ')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.generateAnalyzerReport || config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
.BundleAnalyzerPlugin
if (config.build.bundleAnalyzerReport) {
webpackConfig.plugins.push(
new BundleAnalyzerPlugin({
analyzerPort: 8080,
generateStatsFile: false
})
)
}
if (config.build.generateAnalyzerReport) {
webpackConfig.plugins.push(
new BundleAnalyzerPlugin({
analyzerMode: 'static',
reportFilename: 'bundle-report.html',
openAnalyzer: false
})
)
}
}
module.exports = webpackConfig

View File

@@ -1,5 +0,0 @@
module.exports = {
NODE_ENV: '"production"',
ENV_CONFIG: '"dep"',
BASE_API: '"http://118.24.0.153:8080/admin"'
}

View File

@@ -1,5 +0,0 @@
module.exports = {
NODE_ENV: '"development"',
ENV_CONFIG: '"dev"',
BASE_API: '"http://localhost:8080/admin"'
}

View File

@@ -1,88 +0,0 @@
'use strict'
// Template version: 1.2.6
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
// Various Dev Server settings
// can be overwritten by process.env.HOST
// if you want dev by ip, please set host: '0.0.0.0'
host: 'localhost',
port: 9527, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: true,
errorOverlay: true,
notifyOnErrors: false,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
// Use Eslint Loader?
// If true, your code will be linted during bundling and
// linting errors and warnings will be shown in the console.
useEslint: true,
// If true, eslint errors and warnings will also be shown in the error overlay
// in the browser.
showEslintErrorsInOverlay: false,
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-source-map',
// CSS Sourcemaps off by default because relative paths are "buggy"
// with this option, according to the CSS-Loader README
// (https://github.com/webpack/css-loader#sourcemaps)
// In our experience, they generally work as expected,
// just be aware of this issue when enabling this option.
cssSourceMap: false
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
/**
* You can set by youself according to actual condition
* You will need to set this if you plan to deploy your site under a sub path,
* for example GitHub pages. If you plan to deploy your site to https://foo.github.io/bar/,
* then assetsPublicPath should be set to "/bar/".
* In most cases please use '/' !!!
*/
assetsPublicPath: '/',
/**
* Source Maps
*/
productionSourceMap: false,
// https://webpack.js.org/configuration/devtool/#production
devtool: 'source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build:prod --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report || false,
// `npm run build:prod --generate_report`
generateAnalyzerReport: process.env.npm_config_generate_report || false
}
}

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