23
CHANGELOG.md
23
CHANGELOG.md
@@ -1,5 +1,28 @@
|
||||
# 更新日志
|
||||
|
||||
## V 1.5.0
|
||||
|
||||
*2019-11-15*, 持续优化轻商城模块,以及推荐项目Flutter_Mall
|
||||
|
||||
#### Bug 修复
|
||||
|
||||
* `小商城`优惠券绑定绑定优惠券ID(#157 by @pkwenda)
|
||||
* `小商城`评论列表不能正确显示
|
||||
* `轻商城`修正取消订单接口 (#256 by @1037621594)
|
||||
|
||||
#### 优化
|
||||
|
||||
* `小商城`采用延迟队列实现支付超时取消订单功能(参考#275 by @alexzhu0592)
|
||||
* `小商城`分享按钮可选配置 (#239 by @galenzhao)
|
||||
|
||||
#### 新特性
|
||||
|
||||
* `基础系统`支持阿里云短信
|
||||
* `轻商城`接入微信支付H5支付 (#291 by @pkwenda)
|
||||
* `小商城`团购拼团超期取消 (#284 by @pkwenda)
|
||||
* `管理后台`订单详情新增打印 (#274 by @fanchenggang )
|
||||
* README文档推荐项目Flutter_Mall
|
||||
|
||||
## V 1.4.0
|
||||
|
||||
*2019-05-16*,支持移动端轻商城
|
||||
|
||||
12
README.md
12
README.md
@@ -31,7 +31,7 @@ litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端
|
||||
|
||||

|
||||
|
||||
或者浏览器采用手机模式访问以下网址: [http://118.24.0.153:8080/vue/index.html#/](http://118.24.0.153:8080/vue/index.html#/)
|
||||
或者浏览器采用手机模式访问以下网址: [http://122.51.199.160:8080/vue/index.html#/](http://122.51.199.160:8080/vue/index.html#/)
|
||||
|
||||
注意:
|
||||
> 1. 由于第一次加载数据量较大,建议wifi网络访问,且耐心等待数秒。
|
||||
@@ -41,7 +41,7 @@ litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端
|
||||
|
||||

|
||||
|
||||
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`
|
||||
> 注意:此实例只是测试管理后台,不是前两个小商城的管理后台。
|
||||
|
||||
@@ -154,7 +154,7 @@ litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端
|
||||
|
||||
## 开发计划
|
||||
|
||||
当前版本[v1.4.0](https://linlinjava.gitbook.io/litemall/changelog)
|
||||
当前版本[v1.5.0](https://linlinjava.gitbook.io/litemall/changelog)
|
||||
|
||||
目前项目开发中,存在诸多不足,以下是目前规划的开发计划。
|
||||
|
||||
@@ -219,7 +219,13 @@ V 3.0.0 完成以下目标:
|
||||
项目介绍:基于有赞 vant 组件库的移动商城。
|
||||
|
||||
项目参考:litemall项目的litemall-vue模块基于vant--mobile-mall项目开发。
|
||||
|
||||
## 推荐
|
||||
|
||||
1. [Flutter_Mall](https://github.com/youxinLu/mall)
|
||||
|
||||
项目介绍:Flutter_Mall是一款Flutter开源在线商城应用程序。
|
||||
|
||||
## 问题
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -14,7 +14,7 @@ PASSWORD=
|
||||
|
||||
if test -z "$PASSWORD"
|
||||
then
|
||||
echo "请设置云主机MySQL的root账号密码"
|
||||
echo "请设置云服务器MySQL的root账号密码"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ litemall:
|
||||
app-secret: e04004829d4c383b4db7769d88dfbca1
|
||||
mch-id: 111111
|
||||
mch-key: xxxxxx
|
||||
notify-url: http://118.24.0.153:8080/wx/order/pay-notify
|
||||
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
|
||||
@@ -61,8 +61,10 @@ litemall:
|
||||
# 短信息用于通知客户,例如发货短信通知,注意配置格式;template-name,template-templateId 请参考 NotifyType 枚举值
|
||||
sms:
|
||||
enable: false
|
||||
appid: 111111111
|
||||
appkey: xxxxxxxxxxxxxx
|
||||
# 如果是腾讯云短信,则设置active的值tencent
|
||||
# 如果是阿里云短信,则设置active的值aliyun
|
||||
active: tencent
|
||||
sign: litemall
|
||||
template:
|
||||
- name: paySucceed
|
||||
templateId: 156349
|
||||
@@ -72,6 +74,13 @@ litemall:
|
||||
templateId: 158002
|
||||
- name: refund
|
||||
templateId: 159447
|
||||
tencent:
|
||||
appid: 111111111
|
||||
appkey: xxxxxxxxxxxxxx
|
||||
aliyun:
|
||||
regionId: xxx
|
||||
accessKeyId: xxx
|
||||
accessKeySecret: xxx
|
||||
|
||||
# 微信模版通知配置
|
||||
# 微信模版用于通知客户或者运营者,注意配置格式;template-name,template-templateId 请参考 NotifyType 枚举值
|
||||
@@ -127,7 +136,7 @@ litemall:
|
||||
# 本地对象存储配置信息
|
||||
local:
|
||||
storagePath: storage
|
||||
address: http://118.24.0.153:8080/wx/storage/fetch/
|
||||
address: http://122.51.199.160:8080/wx/storage/fetch/
|
||||
# 阿里云对象存储配置信息
|
||||
aliyun:
|
||||
endpoint: oss-cn-shenzhen.aliyuncs.com
|
||||
|
||||
@@ -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,13 +14,13 @@ ID_RSA=
|
||||
|
||||
if test -z "$REMOTE"
|
||||
then
|
||||
echo "请设置云主机登录IP地址和账户"
|
||||
echo "请设置云服务器登录IP地址和账户"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$ID_RSA"
|
||||
then
|
||||
echo "请设置云主机登录IP地址和账户"
|
||||
echo "请设置云服务器登录IP地址和账户"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -33,11 +33,11 @@ echo "LITEMALL_HOME $LITEMALL_HOME"
|
||||
cd $LITEMALL_HOME || exit 2
|
||||
./deploy/util/package.sh
|
||||
|
||||
# 上传云主机
|
||||
# 上传云服务器
|
||||
cd $LITEMALL_HOME || exit 2
|
||||
scp -i $ID_RSA -r ./deploy $REMOTE:/home/ubuntu/
|
||||
|
||||
# 远程登录云主机并执行reset脚本
|
||||
# 远程登录云服务器并执行reset脚本
|
||||
ssh $REMOTE -i $ID_RSA << eeooff
|
||||
cd /home/ubuntu
|
||||
sudo ./deploy/bin/reset.sh
|
||||
|
||||
@@ -1153,7 +1153,7 @@ public interface Storage {
|
||||
|
||||
## 2.4 litemall-all
|
||||
|
||||
在章节1.5中讨论的部署方案中设计了一种单主机单服务方案,
|
||||
在章节1.5中讨论的部署方案中设计了一种单服务器单服务方案,
|
||||
也就是说两个后台服务和静态文件都部署在一个Spring Boot可执行jar包中。
|
||||
|
||||
查看litemall-all模块,代码仅仅只有一个Application类。
|
||||
|
||||
158
doc/project.md
158
doc/project.md
@@ -243,7 +243,7 @@ Spring Boot技术栈参考以下文档或者项目:
|
||||
|
||||
* dep
|
||||
|
||||
即deploy或者deployment,这里指部署(测试阶段),通常代码已经编译打包运行在远程主机中,
|
||||
即deploy或者deployment,这里指部署(测试阶段),通常代码已经编译打包运行在远程服务器中,
|
||||
可以对外服务。此外,这里服务访问地址通常是IP地址。如果IP是公网IP,那么
|
||||
部署以后就可以对外服务;如果是内网地址,那么只能内网访问。这里的“用户”主要是
|
||||
指开发者本身、测试者;当然,如果是局域网或者不介意IP访问的,那么这里的“用户”
|
||||
@@ -251,7 +251,7 @@ Spring Boot技术栈参考以下文档或者项目:
|
||||
|
||||
* prod
|
||||
|
||||
即product或者production,这里指上线阶段,通常也是代码编译打包运行在远处主机中可以对外服务。
|
||||
即product或者production,这里指上线阶段,通常也是代码编译打包运行在远处服务器中可以对外服务。
|
||||
此外,这里服务访问地址通常是域名地址,同时端口是80web端口。上线以后直接面向的是最终用户。
|
||||
虽然服务的代码本身和dep是完全一样的,但是考虑到场景的不同,上线阶段可能在运行环境方面需要做
|
||||
调整,例如采用反向代理屏蔽内部实际项目结构。此外,最大的不同应该是上线环境下要使用域名和80端口,
|
||||
@@ -435,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/';
|
||||
|
||||
@@ -589,24 +589,33 @@ litemall:
|
||||
# 短信息用于通知客户,例如发货短信通知,注意配置格式;template-name,template-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等值。
|
||||
这里只测试过腾讯云短信平台和阿里云短信平台,开发者需要自行测试其他短信云平台。
|
||||
|
||||
应用场景:
|
||||
目前短信通知场景只支持支付成功、验证码、订单发送、退款成功四种情况。
|
||||
@@ -616,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 微信通知配置
|
||||
|
||||
微信通知是微信上收到的服务通知。
|
||||
@@ -717,7 +737,7 @@ litemall:
|
||||
在litemall-core模块的`application-core.yml`文件中配置对象存储服务:
|
||||
|
||||
* 本地对象存储配置
|
||||
如果开发者采用当前主机保存上传的文件,则需要配置:
|
||||
如果开发者采用当前服务器保存上传的文件,则需要配置:
|
||||
```
|
||||
litemall:
|
||||
storage:
|
||||
@@ -810,7 +830,7 @@ litemall:
|
||||
* litemall-wx模块部署在微信开发者工具中,此外数据API地址指向litemall-wx-api所在服务qi地址
|
||||
* litemall-admin编译出的静态文件放在web服务器或者tomcat服务器,此外服务器地址设置指向3中litemall-admin-api所在地址
|
||||
|
||||
最后,**如果项目部署云主机,则根据开发者的部署环境在以下文件中或代码中修改相应的配置。**
|
||||
最后,**如果项目部署云服务器,则根据开发者的部署环境在以下文件中或代码中修改相应的配置。**
|
||||
|
||||
1. MySQL数据库设置合适的用户名和密码信息;
|
||||
2. 后端服务模块设置合适的配置信息;
|
||||
@@ -819,33 +839,34 @@ litemall:
|
||||
|
||||
实际上,最终的部署方案是灵活的:
|
||||
|
||||
* 可以是同一云主机中安装一个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服务。
|
||||
|
||||

|
||||
|
||||
#### 1.5.1.1 主机
|
||||
#### 1.5.1.1 云服务器
|
||||
|
||||
请参考腾讯云官方文档进行相关操作。
|
||||
|
||||
1. 创建云主机虚拟机
|
||||
1. 创建云服务器
|
||||
|
||||
请参考腾讯云、阿里云或者其他云平台的官方文档进行相关操作。
|
||||
建议最低配置是**1核2G**。
|
||||
|
||||
2. 安装操作系统
|
||||
|
||||
本项目采用ubuntu 16.04.1,但是并不限制其他操作系统。
|
||||
@@ -854,48 +875,55 @@ litemall:
|
||||
|
||||

|
||||
|
||||
目前允许的端口:8081,8082,8083,8080,80,443,22,3306
|
||||
目前允许的端口:8080,80,443,22,3306
|
||||
|
||||
注意:
|
||||
这里其实只需要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
|
||||
@@ -920,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
|
||||
@@ -951,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
|
||||
@@ -970,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
|
||||
@@ -1010,8 +1038,8 @@ cd litemall
|
||||
1. 专门的云数据库部署数据
|
||||
2. 专门的云存储方案
|
||||
3. 专门的CDN分发管理后台的静态文件
|
||||
4. 一台云主机部署管理后台的后端服务
|
||||
5. 一台或多台云主机部署小商场的后端服务
|
||||
4. 一台云服务器部署管理后台的后端服务
|
||||
5. 一台或多台云服务器部署小商场的后端服务
|
||||
|
||||
虽然由于环境原因没有正式测试过,但是这种简单的集群式场景应该是可行的。
|
||||
在1.5.2节中所演示的三个服务是独立的,因此延伸到这里分布式是非常容易的。
|
||||
@@ -1060,7 +1088,7 @@ sudo apt-get update
|
||||
sudo apt-get install nginx
|
||||
```
|
||||
|
||||
有的文档会指出需要防火墙设置,但是腾讯云主机防火墙默认没有开启。
|
||||
有的文档会指出需要防火墙设置,但是腾讯云服务器防火墙默认没有开启。
|
||||
开发者这里自己可以开启设置,或者直接不开启。
|
||||
|
||||
打开浏览器,输入以下地址:
|
||||
@@ -1330,12 +1358,12 @@ litemall-admin编译得到的前端文件在第一次加载时相当耗时,这
|
||||
|
||||
#### 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部署方式比较简单不灵活,开发者可以参考开发自己的项目脚本。
|
||||
|
||||
|
||||
@@ -83,9 +83,9 @@
|
||||
|
||||
2. 启动后台服务
|
||||
|
||||
3. 部署后台服务到云主机
|
||||
3. 部署后台服务到云服务器
|
||||
|
||||
4. litemall-wx的api.js设置云主机的域名。
|
||||
4. litemall-wx的api.js设置云服务器的域名。
|
||||
编译运行,尝试微信支付。
|
||||
|
||||
### 3.0.3 微信退款配置
|
||||
@@ -236,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地址。
|
||||
|
||||
|
||||
@@ -3,11 +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.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;
|
||||
@@ -30,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(rollbackFor = Exception.class)
|
||||
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;
|
||||
|
||||
/**
|
||||
* 自动确认订单
|
||||
@@ -79,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) {
|
||||
@@ -120,4 +84,55 @@ public class OrderJob {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 团购订单拼团超期自动取消
|
||||
*/
|
||||
@Scheduled(initialDelay = 5000, fixedDelay = 10 * 60 * 1000)
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void checkGrouponOrderTimeout() {
|
||||
logger.info("系统开启定时任务检查团购订单是否已经拼团超期自动取消订单");
|
||||
|
||||
List<LitemallGroupon> grouponList = grouponService.queryJoinRecord(0);
|
||||
for (LitemallGroupon groupon : grouponList) {
|
||||
LitemallGrouponRules rules = rulesService.queryById(groupon.getRulesId());
|
||||
if (rulesService.isExpired(rules)) {
|
||||
List<LitemallGroupon> subGrouponList = grouponService.queryJoinRecord(groupon.getId());
|
||||
for (LitemallGroupon subGroupon : subGrouponList) {
|
||||
cancelGrouponScope(subGroupon);
|
||||
}
|
||||
|
||||
cancelGrouponScope(groupon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelGrouponScope(LitemallGroupon groupon) {
|
||||
LitemallOrder order = orderService.findById(groupon.getOrderId());
|
||||
if (order.getOrderStatus().equals(OrderUtil.STATUS_PAY_GROUPON)) {
|
||||
order.setOrderStatus(OrderUtil.STATUS_TIMEOUT_GROUPON);
|
||||
order.setEndTime(LocalDateTime.now());
|
||||
|
||||
cancelOrderScope(order);
|
||||
|
||||
logger.info("团购订单 ID" + order.getId() + " 已经拼团超期自动取消订单");
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelOrderScope(LitemallOrder order) {
|
||||
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("商品货品库存增加失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
NODE_ENV: '"production"',
|
||||
ENV_CONFIG: '"dep"',
|
||||
BASE_API: '"http://118.24.0.153:8080/admin"'
|
||||
BASE_API: '"http://122.51.199.160:8080/admin"'
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@ import * as filters from './filters' // global filters
|
||||
|
||||
import permission from '@/directive/permission/index.js' // 权限判断指令
|
||||
|
||||
import Print from '@/utils/print' // 打印
|
||||
|
||||
Vue.use(Print)
|
||||
|
||||
Vue.use(Element, {
|
||||
size: Cookies.get('size') || 'medium' // set element-ui default size
|
||||
})
|
||||
|
||||
135
litemall-admin/src/utils/print.js
Normal file
135
litemall-admin/src/utils/print.js
Normal file
@@ -0,0 +1,135 @@
|
||||
// 打印类属性、方法定义
|
||||
/* eslint-disable */
|
||||
const Print = function (dom, options) {
|
||||
if (!(this instanceof Print)) return new Print(dom, options);
|
||||
|
||||
this.options = this.extend({
|
||||
'noPrint': '.no-print'
|
||||
}, options);
|
||||
|
||||
if ((typeof dom) === "string") {
|
||||
this.dom = document.querySelector(dom);
|
||||
} else {
|
||||
this.isDOM(dom)
|
||||
this.dom = this.isDOM(dom) ? dom : dom.$el;
|
||||
}
|
||||
|
||||
this.init();
|
||||
};
|
||||
Print.prototype = {
|
||||
init: function () {
|
||||
var content = this.getStyle() + this.getHtml();
|
||||
this.writeIframe(content);
|
||||
},
|
||||
extend: function (obj, obj2) {
|
||||
for (var k in obj2) {
|
||||
obj[k] = obj2[k];
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
getStyle: function () {
|
||||
var str = "",
|
||||
styles = document.querySelectorAll('style,link');
|
||||
for (var i = 0; i < styles.length; i++) {
|
||||
str += styles[i].outerHTML;
|
||||
}
|
||||
str += "<style>" + (this.options.noPrint ? this.options.noPrint : '.no-print') + "{display:none;}</style>";
|
||||
|
||||
return str;
|
||||
},
|
||||
|
||||
getHtml: function () {
|
||||
var inputs = document.querySelectorAll('input');
|
||||
var textareas = document.querySelectorAll('textarea');
|
||||
var selects = document.querySelectorAll('select');
|
||||
|
||||
for (var k = 0; k < inputs.length; k++) {
|
||||
if (inputs[k].type == "checkbox" || inputs[k].type == "radio") {
|
||||
if (inputs[k].checked == true) {
|
||||
inputs[k].setAttribute('checked', "checked")
|
||||
} else {
|
||||
inputs[k].removeAttribute('checked')
|
||||
}
|
||||
} else if (inputs[k].type == "text") {
|
||||
inputs[k].setAttribute('value', inputs[k].value)
|
||||
} else {
|
||||
inputs[k].setAttribute('value', inputs[k].value)
|
||||
}
|
||||
}
|
||||
|
||||
for (var k2 = 0; k2 < textareas.length; k2++) {
|
||||
if (textareas[k2].type == 'textarea') {
|
||||
textareas[k2].innerHTML = textareas[k2].value
|
||||
}
|
||||
}
|
||||
|
||||
for (var k3 = 0; k3 < selects.length; k3++) {
|
||||
if (selects[k3].type == 'select-one') {
|
||||
var child = selects[k3].children;
|
||||
for (var i in child) {
|
||||
if (child[i].tagName == 'OPTION') {
|
||||
if (child[i].selected == true) {
|
||||
child[i].setAttribute('selected', "selected")
|
||||
} else {
|
||||
child[i].removeAttribute('selected')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.dom.outerHTML;
|
||||
},
|
||||
|
||||
writeIframe: function (content) {
|
||||
var w, doc, iframe = document.createElement('iframe'),
|
||||
f = document.body.appendChild(iframe);
|
||||
iframe.id = "myIframe";
|
||||
//iframe.style = "position:absolute;width:0;height:0;top:-10px;left:-10px;";
|
||||
iframe.setAttribute('style', 'position:absolute;width:0;height:0;top:-10px;left:-10px;');
|
||||
w = f.contentWindow || f.contentDocument;
|
||||
doc = f.contentDocument || f.contentWindow.document;
|
||||
doc.open();
|
||||
doc.write(content);
|
||||
doc.close();
|
||||
var _this = this
|
||||
iframe.onload = function(){
|
||||
_this.toPrint(w);
|
||||
setTimeout(function () {
|
||||
document.body.removeChild(iframe)
|
||||
}, 100)
|
||||
}
|
||||
},
|
||||
|
||||
toPrint: function (frameWindow) {
|
||||
try {
|
||||
setTimeout(function () {
|
||||
frameWindow.focus();
|
||||
try {
|
||||
if (!frameWindow.document.execCommand('print', false, null)) {
|
||||
frameWindow.print();
|
||||
}
|
||||
} catch (e) {
|
||||
frameWindow.print();
|
||||
}
|
||||
frameWindow.close();
|
||||
}, 10);
|
||||
} catch (err) {
|
||||
console.log('err', err);
|
||||
}
|
||||
},
|
||||
isDOM: (typeof HTMLElement === 'object') ?
|
||||
function (obj) {
|
||||
return obj instanceof HTMLElement;
|
||||
} :
|
||||
function (obj) {
|
||||
return obj && typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string';
|
||||
}
|
||||
};
|
||||
const MyPlugin = {}
|
||||
MyPlugin.install = function (Vue, options) {
|
||||
// 4. 添加实例方法
|
||||
Vue.prototype.$print = Print
|
||||
}
|
||||
export default MyPlugin
|
||||
@@ -50,18 +50,21 @@ export default {
|
||||
update() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return
|
||||
return false
|
||||
}
|
||||
updateExpress(this.dataForm).then(response => {
|
||||
this.$notify.success({
|
||||
title: '成功',
|
||||
message: '运费配置修改成功'
|
||||
})
|
||||
}).catch(response => {
|
||||
this.$notify.error({
|
||||
title: '失败',
|
||||
message: response.data.errmsg
|
||||
})
|
||||
this.doUpdate()
|
||||
})
|
||||
},
|
||||
doUpdate() {
|
||||
updateExpress(this.dataForm).then(response => {
|
||||
this.$notify.success({
|
||||
title: '成功',
|
||||
message: '运费配置修改成功'
|
||||
})
|
||||
}).catch(response => {
|
||||
this.$notify.error({
|
||||
title: '失败',
|
||||
message: response.data.errmsg
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -33,6 +33,20 @@ export default {
|
||||
litemall_mall_address: '',
|
||||
litemall_mall_phone: '',
|
||||
litemall_mall_qq: ''
|
||||
},
|
||||
rules: {
|
||||
litemall_mall_name: [
|
||||
{ required: true, message: '不能为空', trigger: 'blur' }
|
||||
],
|
||||
litemall_mall_address: [
|
||||
{ required: true, message: '不能为空', trigger: 'blur' }
|
||||
],
|
||||
litemall_mall_phone: [
|
||||
{ required: true, message: '不能为空', trigger: 'blur' }
|
||||
],
|
||||
litemall_mall_qq: [
|
||||
{ required: true, message: '不能为空', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -49,6 +63,14 @@ export default {
|
||||
this.init()
|
||||
},
|
||||
update() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.doUpdate()
|
||||
})
|
||||
},
|
||||
doUpdate() {
|
||||
updateMall(this.dataForm)
|
||||
.then(response => {
|
||||
this.$notify.success({
|
||||
|
||||
@@ -34,7 +34,22 @@ export default {
|
||||
name: 'ConfigOrder',
|
||||
data() {
|
||||
return {
|
||||
dataForm: {}
|
||||
dataForm: {
|
||||
litemall_order_unpaid: 0,
|
||||
litemall_order_unconfirm: 0,
|
||||
litemall_order_comment: 0
|
||||
},
|
||||
rules: {
|
||||
litemall_order_unpaid: [
|
||||
{ required: true, message: '不能为空', trigger: 'blur' }
|
||||
],
|
||||
litemall_order_unconfirm: [
|
||||
{ required: true, message: '不能为空', trigger: 'blur' }
|
||||
],
|
||||
litemall_order_comment: [
|
||||
{ required: true, message: '不能为空', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -50,6 +65,14 @@ export default {
|
||||
this.init()
|
||||
},
|
||||
update() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.doUpdate()
|
||||
})
|
||||
},
|
||||
doUpdate() {
|
||||
updateOrder(this.dataForm)
|
||||
.then(response => {
|
||||
this.$notify.success({
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
:rules="rules"
|
||||
:model="dataForm"
|
||||
status-icon
|
||||
label-width="300px"
|
||||
|
||||
>
|
||||
label-width="300px">
|
||||
<el-tabs tab-position="left" >
|
||||
<el-tab-pane label="首页配置">
|
||||
<el-form-item label="新品首发栏目商品显示数量" prop="litemall_wx_index_new">
|
||||
@@ -51,7 +49,35 @@ export default {
|
||||
name: 'ConfigWx',
|
||||
data() {
|
||||
return {
|
||||
dataForm: { }
|
||||
dataForm: {
|
||||
litemall_wx_index_new: 0,
|
||||
litemall_wx_index_hot: 0,
|
||||
litemall_wx_index_brand: 0,
|
||||
litemall_wx_index_topic: 0,
|
||||
litemall_wx_catlog_list: 0,
|
||||
litemall_wx_catlog_goods: 0,
|
||||
litemall_wx_share: false
|
||||
},
|
||||
rules: {
|
||||
litemall_wx_index_new: [
|
||||
{ required: true, message: '不能为空', trigger: 'blur' }
|
||||
],
|
||||
litemall_wx_index_hot: [
|
||||
{ required: true, message: '不能为空', trigger: 'blur' }
|
||||
],
|
||||
litemall_wx_index_brand: [
|
||||
{ required: true, message: '不能为空', trigger: 'blur' }
|
||||
],
|
||||
litemall_wx_index_topic: [
|
||||
{ required: true, message: '不能为空', trigger: 'blur' }
|
||||
],
|
||||
litemall_wx_catlog_list: [
|
||||
{ required: true, message: '不能为空', trigger: 'blur' }
|
||||
],
|
||||
litemall_wx_catlog_goods: [
|
||||
{ required: true, message: '不能为空', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -67,6 +93,14 @@ export default {
|
||||
this.init()
|
||||
},
|
||||
update() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.doUpdate()
|
||||
})
|
||||
},
|
||||
doUpdate() {
|
||||
updateWx(this.dataForm)
|
||||
.then(response => {
|
||||
this.$notify.success({
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<template slot-scope="scope">
|
||||
<el-button v-permission="['GET /admin/order/detail']" type="primary" size="mini" @click="handleDetail(scope.row)">详情</el-button>
|
||||
<el-button v-permission="['POST /admin/order/ship']" v-if="scope.row.orderStatus==201" type="primary" size="mini" @click="handleShip(scope.row)">发货</el-button>
|
||||
<el-button v-permission="['POST /admin/order/refund']" v-if="scope.row.orderStatus==202" type="primary" size="mini" @click="handleRefund(scope.row)">退款</el-button>
|
||||
<el-button v-permission="['POST /admin/order/refund']" v-if="scope.row.orderStatus==202||scope.row.orderStatus==204" type="primary" size="mini" @click="handleRefund(scope.row)">退款</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -48,61 +48,66 @@
|
||||
|
||||
<!-- 订单详情对话框 -->
|
||||
<el-dialog :visible.sync="orderDialogVisible" title="订单详情" width="800">
|
||||
|
||||
<el-form :data="orderDetail" label-position="left">
|
||||
<el-form-item label="订单编号">
|
||||
<span>{{ orderDetail.order.orderSn }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="订单状态">
|
||||
<el-tag>{{ orderDetail.order.orderStatus | orderStatusFilter }}</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item label="订单用户">
|
||||
<span>{{ orderDetail.user.nickname }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户留言">
|
||||
<span>{{ orderDetail.order.message }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="收货信息">
|
||||
<span>(收货人){{ orderDetail.order.consignee }}</span>
|
||||
<span>(手机号){{ orderDetail.order.mobile }}</span>
|
||||
<span>(地址){{ orderDetail.order.address }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品信息">
|
||||
<el-table :data="orderDetail.orderGoods" border fit highlight-current-row>
|
||||
<el-table-column align="center" label="商品名称" prop="goodsName" />
|
||||
<el-table-column align="center" label="商品编号" prop="goodsSn" />
|
||||
<el-table-column align="center" label="货品规格" prop="specifications" />
|
||||
<el-table-column align="center" label="货品价格" prop="price" />
|
||||
<el-table-column align="center" label="货品数量" prop="number" />
|
||||
<el-table-column align="center" label="货品图片" prop="picUrl">
|
||||
<template slot-scope="scope">
|
||||
<img :src="scope.row.picUrl" width="40">
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
<el-form-item label="费用信息">
|
||||
<span>
|
||||
(实际费用){{ orderDetail.order.actualPrice }}元 =
|
||||
(商品总价){{ orderDetail.order.goodsPrice }}元 +
|
||||
(快递费用){{ orderDetail.order.freightPrice }}元 -
|
||||
(优惠减免){{ orderDetail.order.couponPrice }}元 -
|
||||
(积分减免){{ orderDetail.order.integralPrice }}元
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="支付信息">
|
||||
<span>(支付渠道)微信支付</span>
|
||||
<span>(支付时间){{ orderDetail.order.payTime }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="快递信息">
|
||||
<span>(快递公司){{ orderDetail.order.shipChannel }}</span>
|
||||
<span>(快递单号){{ orderDetail.order.shipSn }}</span>
|
||||
<span>(发货时间){{ orderDetail.order.shipTime }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="收货信息">
|
||||
<span>(确认收货时间){{ orderDetail.order.confirmTime }}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<section ref="print">
|
||||
<el-form :data="orderDetail" label-position="left">
|
||||
<el-form-item label="订单编号">
|
||||
<span>{{ orderDetail.order.orderSn }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="订单状态">
|
||||
<el-tag>{{ orderDetail.order.orderStatus | orderStatusFilter }}</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item label="订单用户">
|
||||
<span>{{ orderDetail.user.nickname }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户留言">
|
||||
<span>{{ orderDetail.order.message }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="收货信息">
|
||||
<span>(收货人){{ orderDetail.order.consignee }}</span>
|
||||
<span>(手机号){{ orderDetail.order.mobile }}</span>
|
||||
<span>(地址){{ orderDetail.order.address }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品信息">
|
||||
<el-table :data="orderDetail.orderGoods" border fit highlight-current-row>
|
||||
<el-table-column align="center" label="商品名称" prop="goodsName" />
|
||||
<el-table-column align="center" label="商品编号" prop="goodsSn" />
|
||||
<el-table-column align="center" label="货品规格" prop="specifications" />
|
||||
<el-table-column align="center" label="货品价格" prop="price" />
|
||||
<el-table-column align="center" label="货品数量" prop="number" />
|
||||
<el-table-column align="center" label="货品图片" prop="picUrl">
|
||||
<template slot-scope="scope">
|
||||
<img :src="scope.row.picUrl" width="40">
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
<el-form-item label="费用信息">
|
||||
<span>
|
||||
(实际费用){{ orderDetail.order.actualPrice }}元 =
|
||||
(商品总价){{ orderDetail.order.goodsPrice }}元 +
|
||||
(快递费用){{ orderDetail.order.freightPrice }}元 -
|
||||
(优惠减免){{ orderDetail.order.couponPrice }}元 -
|
||||
(积分减免){{ orderDetail.order.integralPrice }}元
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="支付信息">
|
||||
<span>(支付渠道)微信支付</span>
|
||||
<span>(支付时间){{ orderDetail.order.payTime }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="快递信息">
|
||||
<span>(快递公司){{ orderDetail.order.shipChannel }}</span>
|
||||
<span>(快递单号){{ orderDetail.order.shipSn }}</span>
|
||||
<span>(发货时间){{ orderDetail.order.shipTime }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="收货信息">
|
||||
<span>(确认收货时间){{ orderDetail.order.confirmTime }}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</section>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="orderDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="printOrder">打 印</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 发货对话框 -->
|
||||
@@ -138,7 +143,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listOrder, shipOrder, refundOrder, detailOrder } from '@/api/order'
|
||||
import { detailOrder, listOrder, refundOrder, shipOrder } from '@/api/order'
|
||||
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
|
||||
import checkPermission from '@/utils/permission' // 权限判断函数
|
||||
|
||||
@@ -146,9 +151,11 @@ const statusMap = {
|
||||
101: '未付款',
|
||||
102: '用户取消',
|
||||
103: '系统取消',
|
||||
200: '已付款团购',
|
||||
201: '已付款',
|
||||
202: '申请退款',
|
||||
203: '已退款',
|
||||
204: '已超时团购',
|
||||
301: '已发货',
|
||||
401: '用户收货',
|
||||
402: '系统收货'
|
||||
@@ -289,6 +296,10 @@ export default {
|
||||
excel.export_json_to_excel2(tHeader, this.list, filterVal, '订单信息')
|
||||
this.downloadLoading = false
|
||||
})
|
||||
},
|
||||
printOrder() {
|
||||
this.$print(this.$refs.print)
|
||||
this.orderDialogVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,27 @@
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-resources-vue</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${basedir}/target/classes/static/vue</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>../litemall-vue/dist</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>aliyun-java-sdk-core</artifactId>
|
||||
<version>4.0.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package org.linlinjava.litemall.core.notify;
|
||||
|
||||
import com.aliyuncs.CommonRequest;
|
||||
import com.aliyuncs.CommonResponse;
|
||||
import com.aliyuncs.DefaultAcsClient;
|
||||
import com.aliyuncs.IAcsClient;
|
||||
import com.aliyuncs.exceptions.ClientException;
|
||||
import com.aliyuncs.exceptions.ServerException;
|
||||
import com.aliyuncs.http.MethodType;
|
||||
import com.aliyuncs.profile.DefaultProfile;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.linlinjava.litemall.core.util.JacksonUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* 阿里云短信服务
|
||||
*/
|
||||
public class AliyunSmsSender implements SmsSender {
|
||||
private final Log logger = LogFactory.getLog(AliyunSmsSender.class);
|
||||
|
||||
private String regionId;
|
||||
private String accessKeyId;
|
||||
private String accessKeySecret;
|
||||
private String sign;
|
||||
|
||||
public String getRegionId() {
|
||||
return regionId;
|
||||
}
|
||||
|
||||
public void setRegionId(String regionId) {
|
||||
this.regionId = regionId;
|
||||
}
|
||||
|
||||
public String getAccessKeyId() {
|
||||
return accessKeyId;
|
||||
}
|
||||
|
||||
public void setAccessKeyId(String accessKeyId) {
|
||||
this.accessKeyId = accessKeyId;
|
||||
}
|
||||
|
||||
public String getAccessKeySecret() {
|
||||
return accessKeySecret;
|
||||
}
|
||||
|
||||
public void setAccessKeySecret(String accessKeySecret) {
|
||||
this.accessKeySecret = accessKeySecret;
|
||||
}
|
||||
|
||||
public String getSign() {
|
||||
return sign;
|
||||
}
|
||||
|
||||
public void setSign(String sign) {
|
||||
this.sign = sign;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsResult send(String phone, String content) {
|
||||
SmsResult smsResult = new SmsResult();
|
||||
smsResult.setSuccessful(false);
|
||||
return smsResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsResult sendWithTemplate(String phone, String templateId, String[] params) {
|
||||
DefaultProfile profile = DefaultProfile.getProfile(this.regionId, this.accessKeyId, this.accessKeySecret);
|
||||
IAcsClient client = new DefaultAcsClient(profile);
|
||||
|
||||
CommonRequest request = new CommonRequest();
|
||||
request.setMethod(MethodType.POST);
|
||||
request.setDomain("dysmsapi.aliyuncs.com");
|
||||
request.setVersion("2017-05-25");
|
||||
request.setAction("SendSms");
|
||||
request.putQueryParameter("RegionId", this.regionId);
|
||||
request.putQueryParameter("PhoneNumbers", phone);
|
||||
request.putQueryParameter("SignName", this.sign);
|
||||
request.putQueryParameter("TemplateCode", templateId);
|
||||
/*
|
||||
NOTE:阿里云短信和腾讯云短信这里存在不一致
|
||||
腾讯云短信模板参数是数组,因此短信模板形式如 “短信参数{1}, 短信参数{2}”
|
||||
阿里云短信模板参数是JSON,因此短信模板形式如“短信参数{param1}, 短信参数{param2}”
|
||||
为了保持统一,我们假定阿里云短信里面的参数是code,code1,code2...
|
||||
|
||||
如果开发者在阿里云短信申请的模板参数是其他命名,请开发者自行调整这里的代码,或者直接写死。
|
||||
*/
|
||||
String templateParam = "{}";
|
||||
if(params.length == 1){
|
||||
Map<String, String> data = new HashMap<>();
|
||||
data.put("code", params[0]);
|
||||
templateParam = JacksonUtil.toJson(data);
|
||||
}
|
||||
else if(params.length > 1){
|
||||
Map<String, String> data = new HashMap<>();
|
||||
data.put("code", params[0]);
|
||||
for(int i = 1; i < params.length; i++){
|
||||
data.put("code" + i, params[i]);
|
||||
}
|
||||
templateParam = JacksonUtil.toJson(data);
|
||||
}
|
||||
request.putQueryParameter("TemplateParam", templateParam);
|
||||
|
||||
try {
|
||||
CommonResponse response = client.getCommonResponse(request);
|
||||
SmsResult smsResult = new SmsResult();
|
||||
smsResult.setSuccessful(true);
|
||||
smsResult.setResult(response);
|
||||
return smsResult;
|
||||
} catch (ServerException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClientException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
SmsResult smsResult = new SmsResult();
|
||||
smsResult.setSuccessful(false);
|
||||
return smsResult;
|
||||
}
|
||||
}
|
||||
@@ -66,8 +66,7 @@ public class NotifyService {
|
||||
return;
|
||||
}
|
||||
|
||||
int templateId = Integer.parseInt(templateIdStr);
|
||||
smsSender.sendWithTemplate(phoneNumber, templateId, params);
|
||||
smsSender.sendWithTemplate(phoneNumber, templateIdStr, params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,9 +81,7 @@ public class NotifyService {
|
||||
if (smsSender == null)
|
||||
return null;
|
||||
|
||||
int templateId = Integer.parseInt(getTemplateId(notifyType, smsTemplate));
|
||||
|
||||
return smsSender.sendWithTemplate(phoneNumber, templateId, params);
|
||||
return smsSender.sendWithTemplate(phoneNumber, getTemplateId(notifyType, smsTemplate), params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,10 +13,9 @@ public interface SmsSender {
|
||||
|
||||
/**
|
||||
* 通过短信模版发送短信息
|
||||
*
|
||||
* @param phone 接收通知的电话号码
|
||||
* @param phone 接收通知的电话号码
|
||||
* @param templateId 通知模板ID
|
||||
* @param params 通知模版内容里的参数,类似"您的验证码为{1}"中{1}的值
|
||||
*/
|
||||
SmsResult sendWithTemplate(String phone, int templateId, String[] params);
|
||||
SmsResult sendWithTemplate(String phone, String templateId, String[] params);
|
||||
}
|
||||
@@ -15,6 +15,7 @@ public class TencentSmsSender implements SmsSender {
|
||||
private final Log logger = LogFactory.getLog(TencentSmsSender.class);
|
||||
|
||||
private SmsSingleSender sender;
|
||||
private String sign;
|
||||
|
||||
public SmsSingleSender getSender() {
|
||||
return sender;
|
||||
@@ -38,13 +39,15 @@ public class TencentSmsSender implements SmsSender {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
return null;
|
||||
SmsResult smsResult = new SmsResult();
|
||||
smsResult.setSuccessful(false);
|
||||
return smsResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsResult sendWithTemplate(String phone, int templateId, String[] params) {
|
||||
public SmsResult sendWithTemplate(String phone, String templateId, String[] params) {
|
||||
try {
|
||||
SmsSingleSenderResult result = sender.sendWithParam("86", phone, templateId, params, "", "", "");
|
||||
SmsSingleSenderResult result = sender.sendWithParam("86", phone, Integer.parseInt(templateId), params, this.sign, "", "");
|
||||
logger.debug(result);
|
||||
|
||||
SmsResult smsResult = new SmsResult();
|
||||
@@ -55,6 +58,12 @@ public class TencentSmsSender implements SmsSender {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
return null;
|
||||
SmsResult smsResult = new SmsResult();
|
||||
smsResult.setSuccessful(false);
|
||||
return smsResult;
|
||||
}
|
||||
|
||||
public void setSign(String sign) {
|
||||
this.sign = sign;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.linlinjava.litemall.core.notify.config;
|
||||
|
||||
import com.github.qcloudsms.SmsSingleSender;
|
||||
import org.linlinjava.litemall.core.notify.AliyunSmsSender;
|
||||
import org.linlinjava.litemall.core.notify.NotifyService;
|
||||
import org.linlinjava.litemall.core.notify.TencentSmsSender;
|
||||
import org.linlinjava.litemall.core.notify.WxTemplateSender;
|
||||
@@ -33,7 +34,13 @@ public class NotifyAutoConfiguration {
|
||||
|
||||
NotifyProperties.Sms smsConfig = properties.getSms();
|
||||
if (smsConfig.isEnable()) {
|
||||
notifyService.setSmsSender(tencentSmsSender());
|
||||
if(smsConfig.getActive().equals("tencent")) {
|
||||
notifyService.setSmsSender(tencentSmsSender());
|
||||
}
|
||||
else if(smsConfig.getActive().equals("aliyun")) {
|
||||
notifyService.setSmsSender(aliyunSmsSender());
|
||||
}
|
||||
|
||||
notifyService.setSmsTemplate(smsConfig.getTemplate());
|
||||
}
|
||||
|
||||
@@ -65,7 +72,21 @@ public class NotifyAutoConfiguration {
|
||||
public TencentSmsSender tencentSmsSender() {
|
||||
NotifyProperties.Sms smsConfig = properties.getSms();
|
||||
TencentSmsSender smsSender = new TencentSmsSender();
|
||||
smsSender.setSender(new SmsSingleSender(smsConfig.getAppid(), smsConfig.getAppkey()));
|
||||
NotifyProperties.Sms.Tencent tencent = smsConfig.getTencent();
|
||||
smsSender.setSender(new SmsSingleSender(tencent.getAppid(), tencent.getAppkey()));
|
||||
smsSender.setSign(smsConfig.getSign());
|
||||
return smsSender;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AliyunSmsSender aliyunSmsSender() {
|
||||
NotifyProperties.Sms smsConfig = properties.getSms();
|
||||
AliyunSmsSender smsSender = new AliyunSmsSender();
|
||||
NotifyProperties.Sms.Aliyun aliyun = smsConfig.getAliyun();
|
||||
smsSender.setSign(smsConfig.getSign());
|
||||
smsSender.setRegionId(aliyun.getRegionId());
|
||||
smsSender.setAccessKeyId(aliyun.getAccessKeyId());
|
||||
smsSender.setAccessKeySecret(aliyun.getAccessKeySecret());
|
||||
return smsSender;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +95,10 @@ public class NotifyProperties {
|
||||
|
||||
public static class Sms {
|
||||
private boolean enable;
|
||||
private int appid;
|
||||
private String appkey;
|
||||
private String active;
|
||||
private String sign;
|
||||
private Tencent tencent;
|
||||
private Aliyun aliyun;
|
||||
private List<Map<String, String>> template = new ArrayList<>();
|
||||
|
||||
public boolean isEnable() {
|
||||
@@ -107,22 +109,6 @@ public class NotifyProperties {
|
||||
this.enable = enable;
|
||||
}
|
||||
|
||||
public int getAppid() {
|
||||
return appid;
|
||||
}
|
||||
|
||||
public void setAppid(int appid) {
|
||||
this.appid = appid;
|
||||
}
|
||||
|
||||
public String getAppkey() {
|
||||
return appkey;
|
||||
}
|
||||
|
||||
public void setAppkey(String appkey) {
|
||||
this.appkey = appkey;
|
||||
}
|
||||
|
||||
public List<Map<String, String>> getTemplate() {
|
||||
return template;
|
||||
}
|
||||
@@ -130,6 +116,89 @@ public class NotifyProperties {
|
||||
public void setTemplate(List<Map<String, String>> template) {
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
public String getActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActive(String active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
public String getSign() {
|
||||
return sign;
|
||||
}
|
||||
|
||||
public void setSign(String sign) {
|
||||
this.sign = sign;
|
||||
}
|
||||
|
||||
public Tencent getTencent() {
|
||||
return tencent;
|
||||
}
|
||||
|
||||
public void setTencent(Tencent tencent) {
|
||||
this.tencent = tencent;
|
||||
}
|
||||
|
||||
public Aliyun getAliyun() {
|
||||
return aliyun;
|
||||
}
|
||||
|
||||
public void setAliyun(Aliyun aliyun) {
|
||||
this.aliyun = aliyun;
|
||||
}
|
||||
|
||||
public static class Tencent {
|
||||
private int appid;
|
||||
private String appkey;
|
||||
|
||||
public int getAppid() {
|
||||
return appid;
|
||||
}
|
||||
|
||||
public void setAppid(int appid) {
|
||||
this.appid = appid;
|
||||
}
|
||||
|
||||
public String getAppkey() {
|
||||
return appkey;
|
||||
}
|
||||
|
||||
public void setAppkey(String appkey) {
|
||||
this.appkey = appkey;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Aliyun {
|
||||
private String regionId;
|
||||
private String accessKeyId;
|
||||
private String accessKeySecret;
|
||||
|
||||
public String getRegionId() {
|
||||
return regionId;
|
||||
}
|
||||
|
||||
public void setRegionId(String regionId) {
|
||||
this.regionId = regionId;
|
||||
}
|
||||
|
||||
public String getAccessKeyId() {
|
||||
return accessKeyId;
|
||||
}
|
||||
|
||||
public void setAccessKeyId(String accessKeyId) {
|
||||
this.accessKeyId = accessKeyId;
|
||||
}
|
||||
|
||||
public String getAccessKeySecret() {
|
||||
return accessKeySecret;
|
||||
}
|
||||
|
||||
public void setAccessKeySecret(String accessKeySecret) {
|
||||
this.accessKeySecret = accessKeySecret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Wx {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.linlinjava.litemall.core.task;
|
||||
|
||||
import java.util.concurrent.Delayed;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public abstract class Task implements Delayed, Runnable{
|
||||
private String id = "";
|
||||
private long start = 0;
|
||||
|
||||
public Task(String id, long delayInMilliseconds){
|
||||
this.id = id;
|
||||
this.start = System.currentTimeMillis() + delayInMilliseconds;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDelay(TimeUnit unit) {
|
||||
long diff = this.start - System.currentTimeMillis();
|
||||
return unit.convert(diff, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Delayed o) {
|
||||
return (int)(this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null) return false;
|
||||
if (!(o instanceof Task)) {
|
||||
return false;
|
||||
}
|
||||
Task t = (Task)o;
|
||||
return this.id.equals(t.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.id.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.linlinjava.litemall.core.task;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.concurrent.DelayQueue;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@Component
|
||||
public class TaskService {
|
||||
private TaskService taskService;
|
||||
private DelayQueue<Task> delayQueue = new DelayQueue<Task>();
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
taskService = this;
|
||||
|
||||
Executors.newSingleThreadExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
Task task = delayQueue.take();
|
||||
task.run();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void addTask(Task task){
|
||||
if(delayQueue.contains(task)){
|
||||
return;
|
||||
}
|
||||
delayQueue.add(task);
|
||||
}
|
||||
|
||||
public void removeTask(Task task){
|
||||
delayQueue.remove(task);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.linlinjava.litemall.core.util;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class BeanUtil implements ApplicationContextAware {
|
||||
protected static ApplicationContext context;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
context = applicationContext;
|
||||
}
|
||||
|
||||
public static Object getBean(String name) {
|
||||
return context.getBean(name);
|
||||
}
|
||||
|
||||
public static <T> T getBean(Class<T> c){
|
||||
return context.getBean(c);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.linlinjava.litemall.core.util;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -161,4 +162,13 @@ public class JacksonUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String toJson(Object data) {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
try {
|
||||
return objectMapper.writeValueAsString(data);
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,10 @@ litemall:
|
||||
# 短信息用于通知客户,例如发货短信通知,注意配置格式;template-name,template-templateId 请参考 NotifyType 枚举值
|
||||
sms:
|
||||
enable: false
|
||||
appid: 111111111
|
||||
appkey: xxxxxxxxxxxxxx
|
||||
# 如果是腾讯云短信,则设置active的值tencent
|
||||
# 如果是阿里云短信,则设置active的值aliyun
|
||||
active: tencent
|
||||
sign: litemall
|
||||
template:
|
||||
- name: paySucceed
|
||||
templateId: 156349
|
||||
@@ -36,6 +38,14 @@ litemall:
|
||||
templateId: 158002
|
||||
- name: refund
|
||||
templateId: 159447
|
||||
tencent:
|
||||
appid: 111111111
|
||||
appkey: xxxxxxxxxxxxxx
|
||||
aliyun:
|
||||
regionId: xxx
|
||||
accessKeyId: xxx
|
||||
accessKeySecret: xxx
|
||||
|
||||
|
||||
# 微信模版通知配置
|
||||
# 微信模版用于通知客户或者运营者,注意配置格式;template-name,template-templateId 请参考 NotifyType 枚举值
|
||||
@@ -102,10 +112,10 @@ litemall:
|
||||
# 腾讯对象存储配置信息
|
||||
# 请参考 https://cloud.tencent.com/document/product/436/6249
|
||||
tencent:
|
||||
secretId: 111111
|
||||
secretKey: xxxxxx
|
||||
region: xxxxxx
|
||||
bucketName: litemall
|
||||
secretId: AKIDOccMr856uoU1Tsa2MQL5aqseBUWRrb5i
|
||||
secretKey: XqtgEhIdrupTs4ygaWlkUUXv3w3FiwuD
|
||||
region: ap-shanghai
|
||||
bucketName: vytech-1300096589
|
||||
# 七牛云对象存储配置信息
|
||||
qiniu:
|
||||
endpoint: http://pd5cb6ulu.bkt.clouddn.com
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package org.linlinjava.litemall.core;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.linlinjava.litemall.core.task.Task;
|
||||
import org.linlinjava.litemall.core.task.TaskService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Delayed;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@WebAppConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest
|
||||
public class TaskTest {
|
||||
private final Log logger = LogFactory.getLog(TaskTest.class);
|
||||
@Autowired
|
||||
private TaskService taskService;
|
||||
|
||||
private class DemoTask extends Task {
|
||||
|
||||
DemoTask(String id, long delayInMilliseconds){
|
||||
super(id, delayInMilliseconds);
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
String now = df.format(LocalDateTime.now());
|
||||
System.out.println("task id=" + this.getId() + " at time=" + now);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
String now = df.format(LocalDateTime.now());
|
||||
System.out.println("start at time=" + now);
|
||||
|
||||
taskService.addTask(new DemoTask("3", 1000));
|
||||
taskService.addTask(new DemoTask("2", 2000));
|
||||
taskService.addTask(new DemoTask("1", 3000));
|
||||
|
||||
taskService.addTask(new DemoTask("4", 1500));
|
||||
taskService.addTask(new DemoTask("5", 2500));
|
||||
taskService.addTask(new DemoTask("6", 3500));
|
||||
|
||||
try {
|
||||
Thread.sleep(10 * 1000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1() {
|
||||
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
String now = df.format(LocalDateTime.now());
|
||||
System.out.println("start at time=" + now);
|
||||
|
||||
taskService.addTask(new DemoTask("3", 0));
|
||||
taskService.addTask(new DemoTask("2", 0));
|
||||
taskService.addTask(new DemoTask("1", 0));
|
||||
|
||||
taskService.addTask(new DemoTask("4", 0));
|
||||
taskService.addTask(new DemoTask("5", 0));
|
||||
taskService.addTask(new DemoTask("6", 0));
|
||||
|
||||
try {
|
||||
Thread.sleep(10 * 1000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,10 +25,20 @@ public class CouponVerifyService {
|
||||
* @param checkedGoodsPrice
|
||||
* @return
|
||||
*/
|
||||
public LitemallCoupon checkCoupon(Integer userId, Integer couponId, BigDecimal checkedGoodsPrice) {
|
||||
public LitemallCoupon checkCoupon(Integer userId, Integer couponId, Integer userCouponId, BigDecimal checkedGoodsPrice) {
|
||||
LitemallCoupon coupon = couponService.findById(couponId);
|
||||
LitemallCouponUser couponUser = couponUserService.queryOne(userId, couponId);
|
||||
if (coupon == null || couponUser == null) {
|
||||
if (coupon == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LitemallCouponUser couponUser = couponUserService.findById(userCouponId);
|
||||
if (couponUser == null) {
|
||||
couponUser = couponUserService.queryOne(userId, couponId);
|
||||
} else if (!couponId.equals(couponUser.getCouponId())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (couponUser == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -126,10 +126,8 @@ public class LitemallOrderService {
|
||||
}
|
||||
|
||||
public List<LitemallOrder> queryUnpaid(int minutes) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime expired = now.minusMinutes(minutes);
|
||||
LitemallOrderExample example = new LitemallOrderExample();
|
||||
example.or().andOrderStatusEqualTo(OrderUtil.STATUS_CREATE).andAddTimeLessThan(expired).andDeletedEqualTo(false);
|
||||
example.or().andOrderStatusEqualTo(OrderUtil.STATUS_CREATE).andDeletedEqualTo(false);
|
||||
return litemallOrderMapper.selectByExample(example);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ public class OrderUtil {
|
||||
public static final Short STATUS_REFUND = 202;
|
||||
public static final Short STATUS_REFUND_CONFIRM = 203;
|
||||
public static final Short STATUS_AUTO_CONFIRM = 402;
|
||||
public static final Short STATUS_PAY_GROUPON = 200;
|
||||
public static final Short STATUS_TIMEOUT_GROUPON = 204;
|
||||
|
||||
|
||||
public static String orderStatusText(LitemallOrder order) {
|
||||
@@ -47,6 +49,10 @@ public class OrderUtil {
|
||||
return "已取消(系统)";
|
||||
}
|
||||
|
||||
if (status == 200) {
|
||||
return "已付款团购";
|
||||
}
|
||||
|
||||
if (status == 201) {
|
||||
return "已付款";
|
||||
}
|
||||
@@ -59,6 +65,10 @@ public class OrderUtil {
|
||||
return "已退款";
|
||||
}
|
||||
|
||||
if (status == 204) {
|
||||
return "已超时团购";
|
||||
}
|
||||
|
||||
if (status == 301) {
|
||||
return "已发货";
|
||||
}
|
||||
@@ -86,10 +96,10 @@ public class OrderUtil {
|
||||
} else if (status == 102 || status == 103) {
|
||||
// 如果订单已经取消或是已完成,则可删除
|
||||
handleOption.setDelete(true);
|
||||
} else if (status == 201) {
|
||||
} else if (status == 200 || status == 201) {
|
||||
// 如果订单已付款,没有发货,则可退款
|
||||
handleOption.setRefund(true);
|
||||
} else if (status == 202) {
|
||||
} else if (status == 202 || status == 204) {
|
||||
// 如果订单申请退款中,没有相关操作
|
||||
} else if (status == 203) {
|
||||
// 如果订单已经退款,则可删除
|
||||
@@ -144,6 +154,12 @@ public class OrderUtil {
|
||||
return OrderUtil.STATUS_CREATE == litemallOrder.getOrderStatus().shortValue();
|
||||
}
|
||||
|
||||
public static boolean hasPayed(LitemallOrder order) {
|
||||
return OrderUtil.STATUS_CREATE != order.getOrderStatus().shortValue()
|
||||
&& OrderUtil.STATUS_CANCEL != order.getOrderStatus().shortValue()
|
||||
&& OrderUtil.STATUS_AUTO_CANCEL != order.getOrderStatus().shortValue();
|
||||
}
|
||||
|
||||
public static boolean isPayStatus(LitemallOrder litemallOrder) {
|
||||
return OrderUtil.STATUS_PAY == litemallOrder.getOrderStatus().shortValue();
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
ENV = 'production'
|
||||
|
||||
# base api
|
||||
VUE_APP_BASE_API = 'http://118.24.0.153:8080/'
|
||||
VUE_APP_BASE_API = 'http://122.51.199.160:8080/'
|
||||
@@ -300,6 +300,14 @@ export function orderPrepay(data) {
|
||||
data
|
||||
})
|
||||
}
|
||||
const OrderH5pay = 'wx/order/h5pay'; // h5支付
|
||||
export function orderH5pay(data) {
|
||||
return request({
|
||||
url: OrderH5pay,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
export const OrderList='wx/order/list'; //订单列表
|
||||
export function orderList(query) {
|
||||
return request({
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
placeholder="请输入短信验证码"
|
||||
>
|
||||
<div slot="rightIcon" @click="getCode" class="getCode red">
|
||||
<countdown v-if="counting" :time="60000" @countdownend="countdownend">
|
||||
<countdown v-if="counting" :time="60000" @end="countdownend">
|
||||
<template slot-scope="props">{{ +props.seconds || 60 }}秒后获取</template>
|
||||
</countdown>
|
||||
<span v-else>获取验证码</span>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<md-field-group class="register_submit">
|
||||
<md-field v-model="code" icon="mobile" placeholder="请输入验证码">
|
||||
<div slot="rightIcon" @click="getCode" class="getCode red">
|
||||
<countdown v-if="counting" :time="60000" @countdownend="countdownend">
|
||||
<countdown v-if="counting" :time="60000" @end="countdownend">
|
||||
<template slot-scope="props">{{ +props.seconds || 60 }}秒后获取</template>
|
||||
</countdown>
|
||||
<span v-else>获取验证码</span>
|
||||
|
||||
@@ -105,7 +105,7 @@ export default {
|
||||
|
||||
methods: {
|
||||
onSubmit() {
|
||||
const {AddressId, CartId, CouponId} = getLocalStorage('AddressId', 'CartId', 'CouponId');
|
||||
const {AddressId, CartId, CouponId, UserCouponId} = getLocalStorage('AddressId', 'CartId', 'CouponId', 'UserCouponId');
|
||||
|
||||
if (AddressId === null) {
|
||||
Toast.fail('请设置收货地址');
|
||||
@@ -119,6 +119,7 @@ export default {
|
||||
addressId: AddressId,
|
||||
cartId: CartId,
|
||||
couponId: CouponId,
|
||||
userCouponId: UserCouponId,
|
||||
grouponLinkId: 0,
|
||||
grouponRulesId: 0,
|
||||
message: this.message
|
||||
@@ -155,15 +156,17 @@ export default {
|
||||
getCoupons() {
|
||||
const {AddressId, CartId, CouponId} = getLocalStorage('AddressId', 'CartId', 'CouponId');
|
||||
couponSelectList({cartId: CartId, grouponRulesId: 0}).then(res => {
|
||||
var cList = res.data.data
|
||||
var cList = res.data.data.list;
|
||||
this.coupons = []
|
||||
this.disabledCoupons = [];
|
||||
for(var i = 0; i < cList.length; i++){
|
||||
var c = cList[i]
|
||||
|
||||
var coupon = {
|
||||
id: c.id,
|
||||
cid: c.cid,
|
||||
name: c.name,
|
||||
condition: c.min,
|
||||
condition: '满' + c.min + '元可用',
|
||||
value: c.discount * 100,
|
||||
description: c.desc,
|
||||
startAt: new Date(c.startTime).getTime()/1000,
|
||||
@@ -171,11 +174,10 @@ export default {
|
||||
valueDesc: c.discount,
|
||||
unitDesc: '元'
|
||||
}
|
||||
this.coupons.push(coupon)
|
||||
|
||||
if(c.id === this.couponId){
|
||||
this.chosenCoupon = i;
|
||||
break;
|
||||
if (c.available) {
|
||||
this.coupons.push(coupon);
|
||||
} else {
|
||||
this.disabledCoupons.push(coupon);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,9 +185,9 @@ export default {
|
||||
})
|
||||
},
|
||||
init() {
|
||||
const {AddressId, CartId, CouponId} = getLocalStorage('AddressId', 'CartId', 'CouponId');
|
||||
const {AddressId, CartId, CouponId, UserCouponId} = getLocalStorage('AddressId', 'CartId', 'CouponId', 'UserCouponId');
|
||||
|
||||
cartCheckout({cartId: CartId, addressId: AddressId, couponId: CouponId, grouponRulesId: 0}).then(res => {
|
||||
cartCheckout({cartId: CartId, addressId: AddressId, couponId: CouponId, userCouponId: UserCouponId, grouponRulesId: 0}).then(res => {
|
||||
var data = res.data.data
|
||||
|
||||
this.checkedGoodsList = data.checkedGoodsList;
|
||||
@@ -198,7 +200,7 @@ export default {
|
||||
this.goodsTotalPrice= data.goodsTotalPrice;
|
||||
this.orderTotalPrice= data.orderTotalPrice;
|
||||
|
||||
setLocalStorage({AddressId: data.addressId, CartId: data.cartId, CouponId: data.couponId});
|
||||
setLocalStorage({AddressId: data.addressId, CartId: data.cartId, CouponId: data.couponId, UserCouponId: data.userCouponId});
|
||||
});
|
||||
|
||||
},
|
||||
@@ -207,11 +209,12 @@ export default {
|
||||
this.chosenCoupon = index;
|
||||
|
||||
if(index === -1 ){
|
||||
setLocalStorage({CouponId: -1});
|
||||
setLocalStorage({CouponId: -1, UserCouponId: -1});
|
||||
}
|
||||
else{
|
||||
const couponId = this.coupons[index].id;
|
||||
setLocalStorage({CouponId: couponId});
|
||||
const couponId = this.coupons[index].cid;
|
||||
const userCouponId = this.coupons[index].id;
|
||||
setLocalStorage({CouponId: couponId, UserCouponId: userCouponId});
|
||||
}
|
||||
|
||||
this.init()
|
||||
@@ -226,7 +229,7 @@ export default {
|
||||
[SubmitBar.name]: SubmitBar,
|
||||
[Card.name]: Card,
|
||||
[Field.name]: Field,
|
||||
[Tag.name]: Field,
|
||||
[Tag.name]: Tag,
|
||||
[CouponCell.name]: CouponCell,
|
||||
[CouponList.name]: CouponList,
|
||||
[Popup.name]: Popup
|
||||
|
||||
@@ -39,8 +39,9 @@
|
||||
|
||||
<script>
|
||||
import { Radio, RadioGroup, Dialog } from 'vant';
|
||||
import { orderDetail, orderPrepay } from '@/api/api';
|
||||
import { orderDetail, orderPrepay, orderH5pay } from '@/api/api';
|
||||
import _ from 'lodash';
|
||||
import { getLocalStorage, setLocalStorage } from '@/utils/local-storage';
|
||||
|
||||
export default {
|
||||
name: 'payment',
|
||||
@@ -72,20 +73,109 @@ export default {
|
||||
Dialog.alert({
|
||||
message: '你选择了' + (this.payWay === 'wx' ? '微信支付' : '支付宝支付')
|
||||
}).then(() => {
|
||||
if (this.payWay === 'wx') {
|
||||
let ua = navigator.userAgent.toLowerCase();
|
||||
let isWeixin = ua.indexOf('micromessenger') != -1;
|
||||
if (isWeixin) {
|
||||
orderPrepay({ orderId: this.orderId })
|
||||
.then(res => {
|
||||
let data = res.data.data;
|
||||
let prepay_data = JSON.stringify({
|
||||
appId: data.appId,
|
||||
timeStamp: data.timeStamp,
|
||||
nonceStr: data.nonceStr,
|
||||
package: data.packageValue,
|
||||
signType: 'MD5',
|
||||
paySign: data.paySign
|
||||
});
|
||||
setLocalStorage({ prepay_data: prepay_data });
|
||||
|
||||
this.$router.push({
|
||||
name: 'paymentStatus',
|
||||
params: {
|
||||
status: 'success'
|
||||
if (typeof WeixinJSBridge == 'undefined') {
|
||||
if (document.addEventListener) {
|
||||
document.addEventListener(
|
||||
'WeixinJSBridgeReady',
|
||||
this.onBridgeReady,
|
||||
false
|
||||
);
|
||||
} else if (document.attachEvent) {
|
||||
document.attachEvent(
|
||||
'WeixinJSBridgeReady',
|
||||
this.onBridgeReady
|
||||
);
|
||||
document.attachEvent(
|
||||
'onWeixinJSBridgeReady',
|
||||
this.onBridgeReady
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.onBridgeReady();
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
Dialog.alert({ message: err.data.errmsg });
|
||||
that.$router.replace({
|
||||
name: 'paymentStatus',
|
||||
params: {
|
||||
status: 'failed'
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
orderH5pay({ orderId: this.orderId })
|
||||
.then(res => {
|
||||
let data = res.data.data;
|
||||
window.location.replace(
|
||||
data.mwebUrl +
|
||||
'&redirect_url=' +
|
||||
encodeURIComponent(
|
||||
window.location.origin +
|
||||
'/#/?orderId=' +
|
||||
this.orderId +
|
||||
'&tip=yes'
|
||||
)
|
||||
);
|
||||
})
|
||||
.catch(err => {
|
||||
Dialog.alert({ message: err.data.errmsg });
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
//todo : alipay
|
||||
}
|
||||
});
|
||||
|
||||
// // 利用weixin-js-sdk调用微信支付
|
||||
// orderPrepay({orderId: this.orderId}).then(res => {
|
||||
// var payParams = res.data.data;
|
||||
|
||||
// });
|
||||
},
|
||||
onBridgeReady() {
|
||||
let that = this;
|
||||
let data = getLocalStorage('prepay_data');
|
||||
// eslint-disable-next-line no-undef
|
||||
WeixinJSBridge.invoke(
|
||||
'getBrandWCPayRequest',
|
||||
JSON.parse(data.prepay_data),
|
||||
function(res) {
|
||||
if (res.err_msg == 'get_brand_wcpay_request:ok') {
|
||||
that.$router.replace({
|
||||
name: 'paymentStatus',
|
||||
params: {
|
||||
status: 'success'
|
||||
}
|
||||
});
|
||||
} else if (res.err_msg == 'get_brand_wcpay_request:cancel') {
|
||||
that.$router.replace({
|
||||
name: 'paymentStatus',
|
||||
params: {
|
||||
status: 'cancel'
|
||||
}
|
||||
});
|
||||
} else {
|
||||
that.$router.replace({
|
||||
name: 'paymentStatus',
|
||||
params: {
|
||||
status: 'failed'
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
class="verifi_code red"
|
||||
:class="{verifi_code_counting: counting}"
|
||||
@click="getCode">
|
||||
<countdown v-if="counting" :time="60000" @countdownend="countdownend">
|
||||
<countdown v-if="counting" :time="60000" @end="countdownend">
|
||||
<template slot-scope="props">{{ +props.seconds || 60 }}秒后获取</template>
|
||||
</countdown>
|
||||
<span v-else>获取验证码</span>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
class="verifi_code red"
|
||||
:class="{verifi_code_counting: counting}"
|
||||
@click="getCode">
|
||||
<countdown v-if="counting" :time="60000" @countdownend="countdownend">
|
||||
<countdown v-if="counting" :time="60000" @end="countdownend">
|
||||
<template slot-scope="props">{{ +props.seconds || 60 }}秒后获取</template>
|
||||
</countdown>
|
||||
<span v-else>获取验证码</span>
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.linlinjava.litemall.wx.service;
|
||||
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
|
||||
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
|
||||
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
|
||||
import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult;
|
||||
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
|
||||
import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
|
||||
import com.github.binarywang.wxpay.constant.WxPayConstants;
|
||||
@@ -17,6 +18,7 @@ import org.linlinjava.litemall.core.notify.NotifyService;
|
||||
import org.linlinjava.litemall.core.notify.NotifyType;
|
||||
import org.linlinjava.litemall.core.qcode.QCodeService;
|
||||
import org.linlinjava.litemall.core.system.SystemConfig;
|
||||
import org.linlinjava.litemall.core.task.TaskService;
|
||||
import org.linlinjava.litemall.core.util.DateTimeUtil;
|
||||
import org.linlinjava.litemall.core.util.JacksonUtil;
|
||||
import org.linlinjava.litemall.core.util.ResponseUtil;
|
||||
@@ -26,6 +28,7 @@ import org.linlinjava.litemall.db.util.CouponUserConstant;
|
||||
import org.linlinjava.litemall.db.util.OrderHandleOption;
|
||||
import org.linlinjava.litemall.db.util.OrderUtil;
|
||||
import org.linlinjava.litemall.core.util.IpUtil;
|
||||
import org.linlinjava.litemall.wx.task.OrderUnpaidTask;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -104,6 +107,8 @@ public class WxOrderService {
|
||||
private LitemallCouponUserService couponUserService;
|
||||
@Autowired
|
||||
private CouponVerifyService couponVerifyService;
|
||||
@Autowired
|
||||
private TaskService taskService;
|
||||
|
||||
/**
|
||||
* 订单列表
|
||||
@@ -152,6 +157,7 @@ public class WxOrderService {
|
||||
orderGoodsVo.put("number", orderGoods.getNumber());
|
||||
orderGoodsVo.put("picUrl", orderGoods.getPicUrl());
|
||||
orderGoodsVo.put("specifications", orderGoods.getSpecifications());
|
||||
orderGoodsVo.put("price",orderGoods.getPrice());
|
||||
orderGoodsVoList.add(orderGoodsVo);
|
||||
}
|
||||
orderVo.put("goodsList", orderGoodsVoList);
|
||||
@@ -185,6 +191,7 @@ public class WxOrderService {
|
||||
Map<String, Object> orderVo = new HashMap<String, Object>();
|
||||
orderVo.put("id", order.getId());
|
||||
orderVo.put("orderSn", order.getOrderSn());
|
||||
orderVo.put("message", order.getMessage());
|
||||
orderVo.put("addTime", order.getAddTime());
|
||||
orderVo.put("consignee", order.getConsignee());
|
||||
orderVo.put("mobile", order.getMobile());
|
||||
@@ -239,6 +246,7 @@ public class WxOrderService {
|
||||
Integer cartId = JacksonUtil.parseInteger(body, "cartId");
|
||||
Integer addressId = JacksonUtil.parseInteger(body, "addressId");
|
||||
Integer couponId = JacksonUtil.parseInteger(body, "couponId");
|
||||
Integer userCouponId = JacksonUtil.parseInteger(body, "userCouponId");
|
||||
String message = JacksonUtil.parseString(body, "message");
|
||||
Integer grouponRulesId = JacksonUtil.parseInteger(body, "grouponRulesId");
|
||||
Integer grouponLinkId = JacksonUtil.parseInteger(body, "grouponLinkId");
|
||||
@@ -300,7 +308,7 @@ public class WxOrderService {
|
||||
BigDecimal couponPrice = new BigDecimal(0.00);
|
||||
// 如果couponId=0则没有优惠券,couponId=-1则不使用优惠券
|
||||
if (couponId != 0 && couponId != -1) {
|
||||
LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, couponId, checkedGoodsPrice);
|
||||
LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, couponId, userCouponId, checkedGoodsPrice);
|
||||
if (coupon == null) {
|
||||
return ResponseUtil.badArgumentValue();
|
||||
}
|
||||
@@ -389,7 +397,7 @@ public class WxOrderService {
|
||||
|
||||
// 如果使用了优惠券,设置优惠券使用状态
|
||||
if (couponId != 0 && couponId != -1) {
|
||||
LitemallCouponUser couponUser = couponUserService.queryOne(userId, couponId);
|
||||
LitemallCouponUser couponUser = couponUserService.findById(userCouponId);
|
||||
couponUser.setStatus(CouponUserConstant.STATUS_USED);
|
||||
couponUser.setUsedTime(LocalDateTime.now());
|
||||
couponUser.setOrderId(orderId);
|
||||
@@ -419,6 +427,9 @@ public class WxOrderService {
|
||||
grouponService.createGroupon(groupon);
|
||||
}
|
||||
|
||||
// 订单支付超期任务
|
||||
taskService.addTask(new OrderUnpaidTask(orderId));
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("orderId", orderId);
|
||||
return ResponseUtil.ok(data);
|
||||
@@ -560,6 +571,60 @@ public class WxOrderService {
|
||||
return ResponseUtil.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信H5支付
|
||||
*
|
||||
* @param userId
|
||||
* @param body
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@Transactional
|
||||
public Object h5pay(Integer userId, String body, HttpServletRequest request) {
|
||||
if (userId == null) {
|
||||
return ResponseUtil.unlogin();
|
||||
}
|
||||
Integer orderId = JacksonUtil.parseInteger(body, "orderId");
|
||||
if (orderId == null) {
|
||||
return ResponseUtil.badArgument();
|
||||
}
|
||||
|
||||
LitemallOrder order = orderService.findById(orderId);
|
||||
if (order == null) {
|
||||
return ResponseUtil.badArgumentValue();
|
||||
}
|
||||
if (!order.getUserId().equals(userId)) {
|
||||
return ResponseUtil.badArgumentValue();
|
||||
}
|
||||
|
||||
// 检测是否能够取消
|
||||
OrderHandleOption handleOption = OrderUtil.build(order);
|
||||
if (!handleOption.isPay()) {
|
||||
return ResponseUtil.fail(ORDER_INVALID_OPERATION, "订单不能支付");
|
||||
}
|
||||
|
||||
WxPayMwebOrderResult result = null;
|
||||
try {
|
||||
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
|
||||
orderRequest.setOutTradeNo(order.getOrderSn());
|
||||
orderRequest.setTradeType("MWEB");
|
||||
orderRequest.setBody("订单:" + order.getOrderSn());
|
||||
// 元转成分
|
||||
int fee = 0;
|
||||
BigDecimal actualPrice = order.getActualPrice();
|
||||
fee = actualPrice.multiply(new BigDecimal(100)).intValue();
|
||||
orderRequest.setTotalFee(fee);
|
||||
orderRequest.setSpbillCreateIp(IpUtil.getIpAddr(request));
|
||||
|
||||
result = wxPayService.createOrder(orderRequest);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return ResponseUtil.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信付款成功或失败回调接口
|
||||
* <p>
|
||||
@@ -612,7 +677,7 @@ public class WxOrderService {
|
||||
}
|
||||
|
||||
// 检查这个订单是否已经处理过
|
||||
if (OrderUtil.isPayStatus(order) && order.getPayId() != null) {
|
||||
if (OrderUtil.hasPayed(order)) {
|
||||
return WxPayNotifyResponse.success("订单已经处理成功!");
|
||||
}
|
||||
|
||||
@@ -658,6 +723,37 @@ public class WxOrderService {
|
||||
if (grouponService.updateById(groupon) == 0) {
|
||||
return WxPayNotifyResponse.fail("更新数据已失效");
|
||||
}
|
||||
|
||||
// 团购已达成,更新关联订单支付状态
|
||||
if (groupon.getGrouponId() > 0) {
|
||||
List<LitemallGroupon> grouponList = grouponService.queryJoinRecord(groupon.getGrouponId());
|
||||
if (grouponList.size() >= grouponRules.getDiscountMember() - 1) {
|
||||
for (LitemallGroupon grouponActivity : grouponList) {
|
||||
if (grouponActivity.getOrderId().equals(order.getId())) {
|
||||
//当前订单
|
||||
continue;
|
||||
}
|
||||
|
||||
LitemallOrder grouponOrder = orderService.findById(grouponActivity.getOrderId());
|
||||
if (grouponOrder.getOrderStatus().equals(OrderUtil.STATUS_PAY_GROUPON)) {
|
||||
grouponOrder.setOrderStatus(OrderUtil.STATUS_PAY);
|
||||
orderService.updateWithOptimisticLocker(grouponOrder);
|
||||
}
|
||||
}
|
||||
|
||||
LitemallGroupon grouponSource = grouponService.queryById(groupon.getGrouponId());
|
||||
LitemallOrder grouponOrder = orderService.findById(grouponSource.getOrderId());
|
||||
if (grouponOrder.getOrderStatus().equals(OrderUtil.STATUS_PAY_GROUPON)) {
|
||||
grouponOrder.setOrderStatus(OrderUtil.STATUS_PAY);
|
||||
orderService.updateWithOptimisticLocker(grouponOrder);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
order = orderService.findBySn(orderSn);
|
||||
order.setOrderStatus(OrderUtil.STATUS_PAY_GROUPON);
|
||||
orderService.updateWithOptimisticLocker(order);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO 发送邮件和短信通知,这里采用异步发送
|
||||
@@ -678,6 +774,9 @@ public class WxOrderService {
|
||||
|
||||
notifyService.notifyWxTemplate(result.getOpenid(), NotifyType.PAY_SUCCEED, parms, "pages/index/index?orderId=" + order.getId());
|
||||
|
||||
// 取消订单超时未支付任务
|
||||
taskService.removeTask(new OrderUnpaidTask(order.getId()));
|
||||
|
||||
return WxPayNotifyResponse.success("处理成功!");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.linlinjava.litemall.wx.task;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.linlinjava.litemall.core.system.SystemConfig;
|
||||
import org.linlinjava.litemall.core.task.Task;
|
||||
import org.linlinjava.litemall.core.util.BeanUtil;
|
||||
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.util.OrderUtil;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
public class OrderUnpaidTask extends Task {
|
||||
private final Log logger = LogFactory.getLog(OrderUnpaidTask.class);
|
||||
private int orderId = -1;
|
||||
|
||||
public OrderUnpaidTask(Integer orderId, long delayInMilliseconds){
|
||||
super("OrderUnpaidTask-" + orderId, delayInMilliseconds);
|
||||
this.orderId = orderId;
|
||||
}
|
||||
|
||||
public OrderUnpaidTask(Integer orderId){
|
||||
super("OrderUnpaidTask-" + orderId, SystemConfig.getOrderUnpaid() * 60 * 1000);
|
||||
this.orderId = orderId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
logger.info("系统开始处理延时任务---订单超时未付款---" + this.orderId);
|
||||
|
||||
LitemallOrderService orderService = BeanUtil.getBean(LitemallOrderService.class);
|
||||
LitemallOrderGoodsService orderGoodsService = BeanUtil.getBean(LitemallOrderGoodsService.class);
|
||||
LitemallGoodsProductService productService = BeanUtil.getBean(LitemallGoodsProductService.class);
|
||||
|
||||
LitemallOrder order = orderService.findById(this.orderId);
|
||||
if(order == null){
|
||||
return;
|
||||
}
|
||||
if(!OrderUtil.isCreateStatus(order)){
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置订单已取消状态
|
||||
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("系统结束处理延时任务---订单超时未付款---" + this.orderId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.linlinjava.litemall.wx.task;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.linlinjava.litemall.core.system.SystemConfig;
|
||||
import org.linlinjava.litemall.core.task.TaskService;
|
||||
import org.linlinjava.litemall.db.domain.LitemallOrder;
|
||||
import org.linlinjava.litemall.db.service.LitemallOrderService;
|
||||
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 TaskStartupRunner implements ApplicationRunner {
|
||||
|
||||
@Autowired
|
||||
private LitemallOrderService orderService;
|
||||
@Autowired
|
||||
private TaskService taskService;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
List<LitemallOrder> orderList = orderService.queryUnpaid(SystemConfig.getOrderUnpaid());
|
||||
for(LitemallOrder order : orderList){
|
||||
LocalDateTime add = order.getAddTime();
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime expire = add.plusMinutes(SystemConfig.getOrderUnpaid());
|
||||
if(expire.isBefore(now)) {
|
||||
// 已经过期,则加入延迟队列
|
||||
taskService.addTask(new OrderUnpaidTask(order.getId(), 0));
|
||||
}
|
||||
else{
|
||||
// 还没过期,则加入延迟队列
|
||||
long delay = ChronoUnit.MILLIS.between(now, expire);
|
||||
taskService.addTask(new OrderUnpaidTask(order.getId(), delay));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import java.time.LocalDateTime;
|
||||
|
||||
public class CouponVo {
|
||||
private Integer id;
|
||||
private Integer cid;
|
||||
private String name;
|
||||
private String desc;
|
||||
private String tag;
|
||||
@@ -11,6 +12,7 @@ public class CouponVo {
|
||||
private String discount;
|
||||
private LocalDateTime startTime;
|
||||
private LocalDateTime endTime;
|
||||
private boolean available;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
@@ -20,6 +22,14 @@ public class CouponVo {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Integer getCid() {
|
||||
return cid;
|
||||
}
|
||||
|
||||
public void setCid(Integer cid) {
|
||||
this.cid = cid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -75,4 +85,12 @@ public class CouponVo {
|
||||
public void setEndTime(LocalDateTime endTime) {
|
||||
this.endTime = endTime;
|
||||
}
|
||||
|
||||
public boolean isAvailable() {
|
||||
return available;
|
||||
}
|
||||
|
||||
public void setAvailable(boolean available) {
|
||||
this.available = available;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,10 +239,12 @@ public class WxAuthController {
|
||||
String password = JacksonUtil.parseString(body, "password");
|
||||
String mobile = JacksonUtil.parseString(body, "mobile");
|
||||
String code = JacksonUtil.parseString(body, "code");
|
||||
// 如果是小程序注册,则必须非空
|
||||
// 其他情况,可以为空
|
||||
String wxCode = JacksonUtil.parseString(body, "wxCode");
|
||||
|
||||
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password) || StringUtils.isEmpty(mobile)
|
||||
|| StringUtils.isEmpty(wxCode) || StringUtils.isEmpty(code)) {
|
||||
|| StringUtils.isEmpty(code)) {
|
||||
return ResponseUtil.badArgument();
|
||||
}
|
||||
|
||||
@@ -264,24 +266,28 @@ public class WxAuthController {
|
||||
return ResponseUtil.fail(AUTH_CAPTCHA_UNMATCH, "验证码错误");
|
||||
}
|
||||
|
||||
String openId = null;
|
||||
try {
|
||||
WxMaJscode2SessionResult result = this.wxService.getUserService().getSessionInfo(wxCode);
|
||||
openId = result.getOpenid();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return ResponseUtil.fail(AUTH_OPENID_UNACCESS, "openid 获取失败");
|
||||
}
|
||||
userList = userService.queryByOpenid(openId);
|
||||
if (userList.size() > 1) {
|
||||
return ResponseUtil.serious();
|
||||
}
|
||||
if (userList.size() == 1) {
|
||||
LitemallUser checkUser = userList.get(0);
|
||||
String checkUsername = checkUser.getUsername();
|
||||
String checkPassword = checkUser.getPassword();
|
||||
if (!checkUsername.equals(openId) || !checkPassword.equals(openId)) {
|
||||
return ResponseUtil.fail(AUTH_OPENID_BINDED, "openid已绑定账号");
|
||||
String openId = "";
|
||||
// 非空,则是小程序注册
|
||||
// 继续验证openid
|
||||
if(!StringUtils.isEmpty(wxCode)) {
|
||||
try {
|
||||
WxMaJscode2SessionResult result = this.wxService.getUserService().getSessionInfo(wxCode);
|
||||
openId = result.getOpenid();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return ResponseUtil.fail(AUTH_OPENID_UNACCESS, "openid 获取失败");
|
||||
}
|
||||
userList = userService.queryByOpenid(openId);
|
||||
if (userList.size() > 1) {
|
||||
return ResponseUtil.serious();
|
||||
}
|
||||
if (userList.size() == 1) {
|
||||
LitemallUser checkUser = userList.get(0);
|
||||
String checkUsername = checkUser.getUsername();
|
||||
String checkPassword = checkUser.getPassword();
|
||||
if (!checkUsername.equals(openId) || !checkPassword.equals(openId)) {
|
||||
return ResponseUtil.fail(AUTH_OPENID_BINDED, "openid已绑定账号");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.linlinjava.litemall.wx.web;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.linlinjava.litemall.core.system.SystemConfig;
|
||||
@@ -14,10 +15,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import static org.linlinjava.litemall.wx.util.WxResponseCode.GOODS_NO_STOCK;
|
||||
import static org.linlinjava.litemall.wx.util.WxResponseCode.GOODS_UNSHELVE;
|
||||
@@ -60,7 +58,22 @@ public class WxCartController {
|
||||
return ResponseUtil.unlogin();
|
||||
}
|
||||
|
||||
List<LitemallCart> cartList = cartService.queryByUid(userId);
|
||||
List<LitemallCart> list = cartService.queryByUid(userId);
|
||||
List<LitemallCart> cartList = new ArrayList<>();
|
||||
// TODO
|
||||
// 如果系统检查商品已删除或已下架,则系统自动删除。
|
||||
// 更好的效果应该是告知用户商品失效,允许用户点击按钮来清除失效商品。
|
||||
for (LitemallCart cart : list) {
|
||||
LitemallGoods goods = goodsService.findById(cart.getGoodsId());
|
||||
if (goods == null || !goods.getIsOnSale()) {
|
||||
cartService.deleteById(cart.getId());
|
||||
logger.debug("系统自动删除失效购物车商品 goodsId=" + cart.getGoodsId() + " productId=" + cart.getProductId());
|
||||
}
|
||||
else{
|
||||
cartList.add(cart);
|
||||
}
|
||||
}
|
||||
|
||||
Integer goodsCount = 0;
|
||||
BigDecimal goodsAmount = new BigDecimal(0.00);
|
||||
Integer checkedGoodsCount = 0;
|
||||
@@ -133,7 +146,12 @@ public class WxCartController {
|
||||
cart.setId(null);
|
||||
cart.setGoodsSn(goods.getGoodsSn());
|
||||
cart.setGoodsName((goods.getName()));
|
||||
cart.setPicUrl(goods.getPicUrl());
|
||||
if(StringUtils.isEmpty(product.getUrl())){
|
||||
cart.setPicUrl(goods.getPicUrl());
|
||||
}
|
||||
else{
|
||||
cart.setPicUrl(product.getUrl());
|
||||
}
|
||||
cart.setPrice(product.getPrice());
|
||||
cart.setSpecifications(product.getSpecifications());
|
||||
cart.setUserId(userId);
|
||||
@@ -202,7 +220,12 @@ public class WxCartController {
|
||||
cart.setId(null);
|
||||
cart.setGoodsSn(goods.getGoodsSn());
|
||||
cart.setGoodsName((goods.getName()));
|
||||
cart.setPicUrl(goods.getPicUrl());
|
||||
if(StringUtils.isEmpty(product.getUrl())){
|
||||
cart.setPicUrl(goods.getPicUrl());
|
||||
}
|
||||
else{
|
||||
cart.setPicUrl(product.getUrl());
|
||||
}
|
||||
cart.setPrice(product.getPrice());
|
||||
cart.setSpecifications(product.getSpecifications());
|
||||
cart.setUserId(userId);
|
||||
@@ -386,7 +409,7 @@ public class WxCartController {
|
||||
* @return 购物车操作结果
|
||||
*/
|
||||
@GetMapping("checkout")
|
||||
public Object checkout(@LoginUser Integer userId, Integer cartId, Integer addressId, Integer couponId, Integer grouponRulesId) {
|
||||
public Object checkout(@LoginUser Integer userId, Integer cartId, Integer addressId, Integer couponId, Integer userCouponId, Integer grouponRulesId) {
|
||||
if (userId == null) {
|
||||
return ResponseUtil.unlogin();
|
||||
}
|
||||
@@ -445,10 +468,12 @@ public class WxCartController {
|
||||
// 计算优惠券可用情况
|
||||
BigDecimal tmpCouponPrice = new BigDecimal(0.00);
|
||||
Integer tmpCouponId = 0;
|
||||
Integer tmpUserCouponId = 0;
|
||||
int tmpCouponLength = 0;
|
||||
List<LitemallCouponUser> couponUserList = couponUserService.queryAll(userId);
|
||||
for(LitemallCouponUser couponUser : couponUserList){
|
||||
LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, couponUser.getCouponId(), checkedGoodsPrice);
|
||||
tmpUserCouponId = couponUser.getId();
|
||||
LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, couponUser.getCouponId(), tmpUserCouponId, checkedGoodsPrice);
|
||||
if(coupon == null){
|
||||
continue;
|
||||
}
|
||||
@@ -468,17 +493,20 @@ public class WxCartController {
|
||||
// 3. 用户已选择优惠券,则测试优惠券是否合适
|
||||
if (couponId == null || couponId.equals(-1)){
|
||||
couponId = -1;
|
||||
userCouponId = -1;
|
||||
}
|
||||
else if (couponId.equals(0)) {
|
||||
couponPrice = tmpCouponPrice;
|
||||
couponId = tmpCouponId;
|
||||
userCouponId = tmpUserCouponId;
|
||||
}
|
||||
else {
|
||||
LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, couponId, checkedGoodsPrice);
|
||||
LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, couponId, userCouponId, checkedGoodsPrice);
|
||||
// 用户选择的优惠券有问题,则选择合适优惠券,否则使用用户选择的优惠券
|
||||
if(coupon == null){
|
||||
couponPrice = tmpCouponPrice;
|
||||
couponId = tmpCouponId;
|
||||
userCouponId = tmpUserCouponId;
|
||||
}
|
||||
else {
|
||||
couponPrice = coupon.getDiscount();
|
||||
@@ -502,6 +530,7 @@ public class WxCartController {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("addressId", addressId);
|
||||
data.put("couponId", couponId);
|
||||
data.put("userCouponId", userCouponId);
|
||||
data.put("cartId", cartId);
|
||||
data.put("grouponRulesId", grouponRulesId);
|
||||
data.put("grouponPrice", grouponPrice);
|
||||
|
||||
@@ -30,6 +30,20 @@ public class WxCatalogController {
|
||||
@Autowired
|
||||
private LitemallCategoryService categoryService;
|
||||
|
||||
@GetMapping("/getfirstcategory")
|
||||
public Object getFirstCategory() {
|
||||
// 所有一级分类目录
|
||||
List<LitemallCategory> l1CatList = categoryService.queryL1();
|
||||
return ResponseUtil.ok(l1CatList);
|
||||
}
|
||||
|
||||
@GetMapping("/getsecondcategory")
|
||||
public Object getSecondCategory(@NotNull Integer id) {
|
||||
// 所有二级分类目录
|
||||
List<LitemallCategory> currentSubCategory = categoryService.queryByPid(id);
|
||||
return ResponseUtil.ok(currentSubCategory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分类详情
|
||||
*
|
||||
|
||||
@@ -79,7 +79,7 @@ public class WxCouponController {
|
||||
*/
|
||||
@GetMapping("mylist")
|
||||
public Object mylist(@LoginUser Integer userId,
|
||||
@NotNull Short status,
|
||||
Short status,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer limit,
|
||||
@Sort @RequestParam(defaultValue = "add_time") String sort,
|
||||
@@ -99,7 +99,8 @@ public class WxCouponController {
|
||||
Integer couponId = couponUser.getCouponId();
|
||||
LitemallCoupon coupon = couponService.findById(couponId);
|
||||
CouponVo couponVo = new CouponVo();
|
||||
couponVo.setId(coupon.getId());
|
||||
couponVo.setId(couponUser.getId());
|
||||
couponVo.setCid(coupon.getId());
|
||||
couponVo.setName(coupon.getName());
|
||||
couponVo.setDesc(coupon.getDesc());
|
||||
couponVo.setTag(coupon.getTag());
|
||||
@@ -160,17 +161,12 @@ public class WxCouponController {
|
||||
|
||||
// 计算优惠券可用情况
|
||||
List<LitemallCouponUser> couponUserList = couponUserService.queryAll(userId);
|
||||
List<LitemallCouponUser> availableCouponUserList = new ArrayList<>(couponUserList.size());
|
||||
for (LitemallCouponUser couponUser : couponUserList) {
|
||||
LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, couponUser.getCouponId(), checkedGoodsPrice);
|
||||
if (coupon == null) {
|
||||
continue;
|
||||
}
|
||||
availableCouponUserList.add(couponUser);
|
||||
List<CouponVo> couponVoList = change(couponUserList);
|
||||
for (CouponVo cv : couponVoList) {
|
||||
LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, cv.getCid(), cv.getId(), checkedGoodsPrice);
|
||||
cv.setAvailable(coupon != null);
|
||||
}
|
||||
|
||||
List<CouponVo> couponVoList = change(availableCouponUserList);
|
||||
|
||||
return ResponseUtil.okList(couponVoList);
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +90,18 @@ public class WxOrderController {
|
||||
return wxOrderService.prepay(userId, body, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信H5支付
|
||||
* @param userId
|
||||
* @param body
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("h5pay")
|
||||
public Object h5pay(@LoginUser Integer userId, @RequestBody String body, HttpServletRequest request) {
|
||||
return wxOrderService.h5pay(userId, body, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信付款成功或失败回调接口
|
||||
* <p>
|
||||
|
||||
@@ -4,7 +4,7 @@ var WxApiRoot = 'http://localhost:8080/wx/';
|
||||
// 局域网测试使用
|
||||
// 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/';
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<text class="name">{{item.goodsName}}</text>
|
||||
<text class="num">x{{item.number}}</text>
|
||||
</view>
|
||||
<view class="attr">{{ isEditCart ? '已选择:' : ''}}{{item.goodsSpecificationValues||''}}</view>
|
||||
<view class="attr">{{ isEditCart ? '已选择:' : ''}}{{item.specifications||''}}</view>
|
||||
<view class="b">
|
||||
<text class="price">¥{{item.price}}</text>
|
||||
<view class="selnum">
|
||||
|
||||
@@ -17,6 +17,7 @@ Page({
|
||||
cartId: 0,
|
||||
addressId: 0,
|
||||
couponId: 0,
|
||||
userCouponId: 0,
|
||||
message: '',
|
||||
grouponLinkId: 0, //参与的团购,如果是发起则为0
|
||||
grouponRulesId: 0 //团购规则ID
|
||||
@@ -32,6 +33,7 @@ Page({
|
||||
cartId: that.data.cartId,
|
||||
addressId: that.data.addressId,
|
||||
couponId: that.data.couponId,
|
||||
userCouponId: that.data.userCouponId,
|
||||
grouponRulesId: that.data.grouponRulesId
|
||||
}).then(function(res) {
|
||||
if (res.errno === 0) {
|
||||
@@ -47,6 +49,7 @@ Page({
|
||||
orderTotalPrice: res.data.orderTotalPrice,
|
||||
addressId: res.data.addressId,
|
||||
couponId: res.data.couponId,
|
||||
userCouponId: res.data.userCouponId,
|
||||
grouponRulesId: res.data.grouponRulesId,
|
||||
});
|
||||
}
|
||||
@@ -90,6 +93,10 @@ Page({
|
||||
if (couponId === "") {
|
||||
couponId = 0;
|
||||
}
|
||||
var userCouponId = wx.getStorageSync('userCouponId');
|
||||
if (userCouponId === "") {
|
||||
userCouponId = 0;
|
||||
}
|
||||
var grouponRulesId = wx.getStorageSync('grouponRulesId');
|
||||
if (grouponRulesId === "") {
|
||||
grouponRulesId = 0;
|
||||
@@ -103,6 +110,7 @@ Page({
|
||||
cartId: cartId,
|
||||
addressId: addressId,
|
||||
couponId: couponId,
|
||||
userCouponId: userCouponId,
|
||||
grouponRulesId: grouponRulesId,
|
||||
grouponLinkId: grouponLinkId
|
||||
});
|
||||
@@ -131,6 +139,7 @@ Page({
|
||||
cartId: this.data.cartId,
|
||||
addressId: this.data.addressId,
|
||||
couponId: this.data.couponId,
|
||||
userCouponId: this.data.userCouponId,
|
||||
message: this.data.message,
|
||||
grouponRulesId: this.data.grouponRulesId,
|
||||
grouponLinkId: this.data.grouponLinkId
|
||||
|
||||
@@ -45,15 +45,15 @@ Page({
|
||||
|
||||
if (that.data.showType == 0) {
|
||||
that.setData({
|
||||
allCommentList: that.data.allCommentList.concat(res.data.data),
|
||||
allPage: res.data.currentPage,
|
||||
comments: that.data.allCommentList.concat(res.data.data)
|
||||
allCommentList: that.data.allCommentList.concat(res.data.list),
|
||||
allPage: res.data.page,
|
||||
comments: that.data.allCommentList.concat(res.data.list)
|
||||
});
|
||||
} else {
|
||||
that.setData({
|
||||
picCommentList: that.data.picCommentList.concat(res.data.data),
|
||||
picPage: res.data.currentPage,
|
||||
comments: that.data.picCommentList.concat(res.data.data)
|
||||
picCommentList: that.data.picCommentList.concat(res.data.list),
|
||||
picPage: res.data.page,
|
||||
comments: that.data.picCommentList.concat(res.data.list)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -84,21 +84,30 @@ Page({
|
||||
// 页面关闭
|
||||
|
||||
},
|
||||
switchTab: function() {
|
||||
this.setData({
|
||||
showType: this.data.showType == 1 ? 0 : 1
|
||||
});
|
||||
|
||||
switchTab: function () {
|
||||
let that = this;
|
||||
if (that.data.showType == 0) {
|
||||
that.setData({
|
||||
allCommentList: [],
|
||||
allPage: 1,
|
||||
comments: [],
|
||||
showType: 1
|
||||
});
|
||||
} else {
|
||||
that.setData({
|
||||
picCommentList: [],
|
||||
picPage: 1,
|
||||
comments: [],
|
||||
showType: 0
|
||||
});
|
||||
}
|
||||
this.getCommentList();
|
||||
},
|
||||
onReachBottom: function() {
|
||||
console.log('onPullDownRefresh');
|
||||
if (this.data.showType == 0) {
|
||||
|
||||
if (this.data.allCount / this.data.limit < this.data.allPage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.setData({
|
||||
allPage: this.data.allPage + 1
|
||||
});
|
||||
@@ -106,14 +115,10 @@ Page({
|
||||
if (this.data.hasPicCount / this.data.limit < this.data.picPage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.setData({
|
||||
picPage: this.data.picPage + 1
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.getCommentList();
|
||||
}
|
||||
})
|
||||
@@ -1,4 +1,12 @@
|
||||
<view class="comments">
|
||||
<view class="h">
|
||||
<view class="item {{ showType == 0 ? 'active' : ''}}" bindtap="switchTab">
|
||||
<view class="txt">全部({{allCount}})</view>
|
||||
</view>
|
||||
<view class="item {{ showType == 0 ? '' : 'active'}}" bindtap="switchTab">
|
||||
<view class="txt">有图({{hasPicCount}})</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="b">
|
||||
<view class="item" wx:for="{{comments}}" wx:key="id">
|
||||
<view class="info">
|
||||
@@ -10,7 +18,7 @@
|
||||
</view>
|
||||
<view class="comment">{{item.content}}</view>
|
||||
<view class="imgs" wx:if="{{item.picList.length > 0}}">
|
||||
<image class="img" wx:for="{{item.picList}}" wx:key="id" wx:for-item="pitem" src="{{pitem.picUrl}}"></image>
|
||||
<image class="img" wx:for="{{item.picList}}" wx:key="*this" wx:for-item="pitem" src="{{pitem}}"></image>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
@@ -6,7 +6,44 @@
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
|
||||
|
||||
.comments .h {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
background: #fff;
|
||||
height: 84rpx;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.comments .h .item {
|
||||
display: inline-block;
|
||||
height: 82rpx;
|
||||
width: 50%;
|
||||
padding: 0 15rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.comments .h .item .txt {
|
||||
display: inline-block;
|
||||
height: 82rpx;
|
||||
padding: 0 20rpx;
|
||||
line-height: 82rpx;
|
||||
color: #333;
|
||||
font-size: 30rpx;
|
||||
width: 170rpx;
|
||||
}
|
||||
|
||||
.comments .h .item.active .txt {
|
||||
color: #ab2b2b;
|
||||
border-bottom: 4rpx solid #ab2b2b;
|
||||
}
|
||||
|
||||
.comments .b {
|
||||
margin-top: 85rpx;
|
||||
height: auto;
|
||||
width: 720rpx;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ Page({
|
||||
couponList: [],
|
||||
cartId: 0,
|
||||
couponId: 0,
|
||||
userCouponId: 0,
|
||||
grouponLinkId: 0,
|
||||
scrollTop: 0
|
||||
},
|
||||
@@ -46,6 +47,11 @@ Page({
|
||||
couponId = 0;
|
||||
}
|
||||
|
||||
var userCouponId = wx.getStorageSync('userCouponId');
|
||||
if (!userCouponId) {
|
||||
userCouponId = 0;
|
||||
}
|
||||
|
||||
var grouponRulesId = wx.getStorageSync('grouponRulesId');
|
||||
if (!grouponRulesId) {
|
||||
grouponRulesId = 0;
|
||||
@@ -54,6 +60,7 @@ Page({
|
||||
this.setData({
|
||||
cartId: cartId,
|
||||
couponId: couponId,
|
||||
userCouponId: userCouponId,
|
||||
grouponRulesId: grouponRulesId
|
||||
});
|
||||
|
||||
@@ -116,8 +123,14 @@ Page({
|
||||
grouponRulesId: that.data.grouponRulesId,
|
||||
}).then(function (res) {
|
||||
if (res.errno === 0) {
|
||||
let list = [];
|
||||
for (var i = 0; i < res.data.list.length; i++) {
|
||||
if (res.data.list[i].available) {
|
||||
list.push(res.data.list[i]);
|
||||
}
|
||||
}
|
||||
that.setData({
|
||||
couponList: res.data.list
|
||||
couponList: list
|
||||
});
|
||||
}
|
||||
wx.hideToast();
|
||||
@@ -126,7 +139,8 @@ Page({
|
||||
},
|
||||
selectCoupon: function (e) {
|
||||
try {
|
||||
wx.setStorageSync('couponId', e.currentTarget.dataset.id);
|
||||
wx.setStorageSync('couponId', e.currentTarget.dataset.cid);
|
||||
wx.setStorageSync('userCouponId', e.currentTarget.dataset.id);
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
@@ -137,6 +151,7 @@ Page({
|
||||
// 如果优惠券ID设置-1,则表示订单不使用优惠券
|
||||
try {
|
||||
wx.setStorageSync('couponId', -1);
|
||||
wx.setStorageSync('userCouponId', -1);
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<view class="unselect" bindtap='unselectCoupon'>不选择优惠券</view>
|
||||
|
||||
<view class="item" wx:for="{{couponList}}" wx:for-index="index" wx:for-item="item" wx:key="id" bindtap="selectCoupon" data-id="{{item.id}}">
|
||||
<view class="item" wx:for="{{couponList}}" wx:for-index="index" wx:for-item="item" wx:key="id" bindtap="selectCoupon" data-id="{{item.id}}" data-cid="{{item.cid}}">
|
||||
<view class="tag">{{item.tag}}</view>
|
||||
<view class="content">
|
||||
<view class="left">
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<view class="container">
|
||||
<view class="order-info">
|
||||
<view class="item-a">下单时间:{{orderInfo.addTime}}</view>
|
||||
<view class="item-b">订单编号:{{orderInfo.orderSn}}</view>
|
||||
<view class="item">下单时间:{{orderInfo.addTime}}</view>
|
||||
<view class="item">订单编号:{{orderInfo.orderSn}}</view>
|
||||
<view class="item">订单留言:{{orderInfo.message}}</view>
|
||||
<view class="item-c">
|
||||
<view class="l">实付:
|
||||
<text class="cost">¥{{orderInfo.actualPrice}}</text>
|
||||
|
||||
@@ -11,7 +11,7 @@ page {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.item-a {
|
||||
.item {
|
||||
padding-left: 31.25rpx;
|
||||
height: 42.5rpx;
|
||||
padding-bottom: 12.5rpx;
|
||||
@@ -20,16 +20,6 @@ page {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.item-b {
|
||||
padding-left: 31.25rpx;
|
||||
height: 29rpx;
|
||||
line-height: 29rpx;
|
||||
margin-top: 12.5rpx;
|
||||
margin-bottom: 41.5rpx;
|
||||
font-size: 30rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.item-c {
|
||||
margin-left: 31.25rpx;
|
||||
border-top: 1px solid #f4f4f4;
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -115,7 +115,7 @@
|
||||
<dependency>
|
||||
<groupId>com.qcloud</groupId>
|
||||
<artifactId>cos_api</artifactId>
|
||||
<version>5.4.4</version>
|
||||
<version>5.6.8</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<text class="name">{{item.goodsName}}</text>
|
||||
<text class="num">x{{item.number}}</text>
|
||||
</view>
|
||||
<view class="attr">{{ isEditCart ? '已选择:' : ''}}{{item.goodsSpecificationValues||''}}</view>
|
||||
<view class="attr">{{ isEditCart ? '已选择:' : ''}}{{item.specifications||''}}</view>
|
||||
<view class="b">
|
||||
<text class="price">¥{{item.price}}</text>
|
||||
<view class="selnum">
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<view class="container">
|
||||
<view class="order-info">
|
||||
<view class="item-a">下单时间:{{orderInfo.addTime}}</view>
|
||||
<view class="item-b">订单编号:{{orderInfo.orderSn}}</view>
|
||||
<view class="item">下单时间:{{orderInfo.addTime}}</view>
|
||||
<view class="item">订单编号:{{orderInfo.orderSn}}</view>
|
||||
<view class="item">订单留言:{{orderInfo.message}}</view>
|
||||
<view class="item-c">
|
||||
<view class="l">实付:
|
||||
<text class="cost">¥{{orderInfo.actualPrice}}</text>
|
||||
|
||||
@@ -11,7 +11,7 @@ page {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.item-a {
|
||||
.item {
|
||||
padding-left: 31.25rpx;
|
||||
height: 42.5rpx;
|
||||
padding-bottom: 12.5rpx;
|
||||
@@ -20,16 +20,6 @@ page {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.item-b {
|
||||
padding-left: 31.25rpx;
|
||||
height: 29rpx;
|
||||
line-height: 29rpx;
|
||||
margin-top: 12.5rpx;
|
||||
margin-bottom: 41.5rpx;
|
||||
font-size: 30rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.item-c {
|
||||
margin-left: 31.25rpx;
|
||||
border-top: 1px solid #f4f4f4;
|
||||
|
||||
Reference in New Issue
Block a user