diff --git a/.gitbook.yaml b/.gitbook.yaml new file mode 100644 index 00000000..10f02b39 --- /dev/null +++ b/.gitbook.yaml @@ -0,0 +1,2 @@ +structure: + summary: doc/README.md \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ddbc0aa..ccee4258 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,25 @@ -## 更新日志 +# 更新日志 -### V 0.5.0 +## V 0.7.0 + +*2018-06-30*,数据库再次简化 + + * `管理后台`支持商品上架和商品编辑 + * `项目`数据库再次简化、小商城和管理后台代码进行相应调整 + + +## V 0.6.0 + +*2018-06-30*,项目支持商品上架和统计功能 + + * `小商场`因wx.getUserInfo接口调整,微信登录调整 + * `小商场`支持手机号码验证 + * `管理后台`支持简单的统计 + * `管理后台`支持商品上架 + * `管理后台`基于官方tinymce-vue实现富文本编辑 + * `项目`支持docker部署 + +## V 0.5.0 *2018-05-11*,项目支持微信支付和修复小程序的一些BUG @@ -18,7 +37,7 @@ * `项目`文档整理部署方案和上线方案 * `项目`文档支持更新日志、贡献指南和常见问题 -### V 0.4.0 +## V 0.4.0 *2018-04-21*,项目结构调整,增加了两个模块 @@ -33,7 +52,7 @@ * `基础系统`新增litemall-all模块作为包裹模块,支持三个后台服务和静态文件 打包成一个war项目包 -### V 0.3.0 +## V 0.3.0 *2018-04-07*,业务模块从物理删除调整成逻辑删除 @@ -46,7 +65,7 @@ * `基础系统`数据库中除regions几个表,其他所有表都添加`add_time`和`deleted`字段 * `基础系统`litemall-db模块不支持数据物理删除,删除则设置`deleted`,而查询则过滤`deleted` -### V 0.2.0 +## V 0.2.0 *2018-04-02*,修复一些小商场出现的问题。 @@ -59,6 +78,6 @@ * `管理后台`禁止管理员修改超级管理员信息 * `基础系统`自动脚本util/lazy.sh和util/upload.sh -### V 0.1.0 +## V 0.1.0 *2018-03-24*, 项目架构基本完成。 \ No newline at end of file diff --git a/CONTRIBUTE.md b/CONTRIBUTE.md index 5150dec4..ba3ee769 100644 --- a/CONTRIBUTE.md +++ b/CONTRIBUTE.md @@ -1,4 +1,4 @@ -## 贡献 +# 贡献指南 任何形式的贡献都欢迎,包括: @@ -13,7 +13,7 @@ 目前项目基本框架已经建立,但是还不完整,因此非常欢迎人一起讨论技术和业务。 但是交流合作之前,请查看以下碎碎念。 -### 项目理念 +## 项目理念 litemall的设计受众是小微型企业,而不是互联网企业,因此litemall的开发理念是一个小而美: @@ -68,7 +68,7 @@ litemall的设计受众是小微型企业,而不是互联网企业,因此lit 当然,如果开发者不认同这里的一些理念,也可以基于自己的技术选型来开发自己的版本。 如果认为自己开发的好,可以在本项目提交链接地址,让其他开发者了解。 -### 项目业务 +## 项目业务 本项目所设想的基本业务功能已经在文档中列举完成。 @@ -78,11 +78,11 @@ litemall的设计受众是小微型企业,而不是互联网企业,因此lit 当然更希望开发者直接提供代码,丰富完善目前的基础业务功能。 -### 代码贡献 +## 代码贡献 * 代码规范目前没有严格要求,后续会补充。 -### Issue规范 +## Issue规范 * 在提交issue之前,请搜索是否存在相关问题。 * 提交的Issue请尽可能给出详细信息 diff --git a/README.md b/README.md index f3c16f73..dc48e648 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,18 @@ -litemall -===== +# litemall 又一个小商场系统。 -项目代码 -==== +litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端 + +## 项目代码 * [码云](https://gitee.com/linlinjava/litemall) * [GitHub](https://github.com/linlinjava/litemall) -项目架构 -==== +## 项目架构 ![](./doc/pic/1.png) -技术栈 -=== +## 技术栈 > 1. Spring Boot > 2. Vue @@ -22,8 +20,7 @@ litemall ![](doc/pic/2.png) -效果 -== +## 效果 ### 小商城效果 @@ -33,53 +30,28 @@ litemall * 专题列表、专题详情 * 分类列表、分类详情 * 品牌列表、品牌详情 -* 新品首发 -* 人气推荐 -* 商品搜索 +* 新品首发、人气推荐 +* 搜索 * 商品详情 * 商品评价列表、商品评价 * 购物车 * 下单 -* 我的主页 +* 个人 * 订单列表、订单详情 * 地址列表、地址添加、地址删除 -* 我的收藏 -* 我的足迹 - +* 收藏、足迹 ### 管理平台效果 ![](doc/pic/4.png) * 会员管理 - * 会员管理 - * 收货地址管理 - * 会员收藏 - * 会员足迹 - * 搜索历史 - * 购物车 * 商城管理 - * 区域配置 - * 品牌制造商 - * 订单管理 - * 商品类目 - * 通用问题 - * 关键词 * 商品管理 - * 商品管理 - * 商品参数 - * 商品规格 - * 货品管理 - * 用户评论 * 推广管理 - * 广告列表 - * 专题管理 * 系统管理 - * 管理员 - * 对象存储 -云演示 -== +## 云演示 ### 小商城演示访问 @@ -89,6 +61,7 @@ litemall 2. 项目配置,启用“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书” 3. 点击“编译”,即可在微信开发工具预览效果; 4. 也可以点击“预览”,然后手机扫描登录。 + 注意,手机需要打开调试功能。 ![](doc/pic/5.gif) @@ -97,35 +70,30 @@ litemall 1. 浏览器打开,输入以下网址[http://122.152.206.172:8080/#/login](http://122.152.206.172:8080/#/login) 2. 管理员名称`admin123`,管理员密码`admin123` -文档 -== +## 文档 -1. [系统架构](doc/1.md) -2. [基础平台](doc/2.md) -3. [小商场](doc/3.md) -4. [管理后台](doc/4.md) -5. [商场](doc/5.md) -6. [下一步计划](doc/6.md) -7. [FAQ](doc/7.md) +[在线文档](https://linlinjava.gitbook.io/litemall/) -文档内容采用 [署名-禁止演绎 4.0 国际协议许可](https://creativecommons.org/licenses/by-nd/4.0/deed.zh) +文档采用 [署名-禁止演绎 4.0 国际协议许可](https://creativecommons.org/licenses/by-nd/4.0/deed.zh) -开发计划 -==== +## 开发计划 目前项目开发中,存在诸多不足,以下是目前规划的开发计划。 V 1.0.0 完成以下目标: -1. 除了部分功能(如支付和优惠券等),小商城的优化和改进基本结束; + +1. 除了部分功能(如优惠券等),小商城的优化和改进基本结束; 2. 管理后台基本实现所有表的CRUD操作; 3. 后台服务能够对参数进行检验。 V 2.0.0 完成以下目标: + 1. 小商城能够完成基本的业务功能; 2. 管理后台实现较好的业务操作和交互效果,而不是简单的CRUD; 3. 管理后台实现统计功能、日志功能 V 3.0.0 完成以下目标: + 1. 管理后台一些辅助功能 2. 后台服务加强安全功能、配置功能 3. 项目代码重构和清理 @@ -134,6 +102,7 @@ V 3.0.0 完成以下目标: 项目结束,已经是一个真正可工作的项目,此时进入维护阶段。 如果真的坚持到维护阶段,那么存在三种可能性: + 1. 或者开发 V 4.0.0,实现web商场子系统; 2. 或者重新开发一个新的独立项目,引入企业级功能,如缓存、权限、对象存储云服务等; 3. 或者结束,不再开发,仅仅维护。 @@ -141,37 +110,30 @@ V 3.0.0 完成以下目标: 警告: > ** 以上仅仅是个人规划的开发计划,实际可能出现任何情况,例如能力有限而放弃开发。** -开发进度更新 -====== +## 开发进度 查看[更新日志](CHANGELOG.md) -目前V0.5.0 +目前V0.6.0 -警告 -== +## 警告 > 1. 本项目仅用于学习练习 > 2. 数据库数据来自nideshop > 3. 项目代码目前还不完善,仍处在开发中 > 4. 项目开源(MIT),但不承担任何使用后果 -致谢 -== +## 致谢 本项目基于或参考以下项目: > 1. [nideshop-mini-program](https://github.com/tumobi/nideshop-mini-program) > 如果后端希望采用nodejs,开发者可以访问nideshop项目 -> 2. [platform](https://gitee.com/fuyang_lipengjun/platform) -> 如果后端希望采用非spring boot版的普通spring版或者更多功能, -> 开发者可以访问platform项目 -> 3. [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) +> 2. [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) > 一个基于Vue和Element的后台集成方案 本项目所依赖的其他开源项目见相关章节 -问题 -== +## 问题 开发者有问题或者好的建议可以用Issues反馈交流,请给出详细信息,本人会尽可能解决。 * 如果问题是共性问题(如代码bug或文档不全),本人会及时解决。 @@ -183,8 +145,7 @@ V 3.0.0 完成以下目标: * 请查看相关技术的官方文档,例如微信小程序的官方文档; * 请提问前尽可能做一些DEBUG或者思考分析,然后提问时给出详细的错误相关信息以及个人对问题的理解。 -贡献 -== +## 贡献 任何形式的贡献都欢迎,查看[贡献指南](CONTRIBUTE.md) @@ -195,7 +156,6 @@ V 3.0.0 完成以下目标: > * 在开发交流群中应讨论开发、业务和合作问题。 > * 交流结果如果是共识性的则在文档中记录,如果是开放性的则会在Issue中记录。 -相关项目 -==== +## 相关项目 [HubertYoung](https://github.com/HubertYoung)正在开发Android端[Litemall-Android](https://github.com/HubertYoung/Litemall-Android) \ No newline at end of file diff --git a/deploy/.gitignore b/deploy/.gitignore index 98fd476d..d898f065 100644 --- a/deploy/.gitignore +++ b/deploy/.gitignore @@ -2,5 +2,6 @@ /litemall-os-api/litemall-os-api.jar /litemall-wx-api/litemall-wx-api.jar /litemall-admin/dist.tar -/litemall-db/litemall.sql /litemall-db/litemall_schema.sql +/litemall-db/litemall_table.sql +/litemall-db/litemall_data.sql diff --git a/deploy/bin/reset.sh b/deploy/bin/reset.sh index a89a6318..b1ac2e7b 100644 --- a/deploy/bin/reset.sh +++ b/deploy/bin/reset.sh @@ -20,7 +20,8 @@ fi cd /home/ubuntu/deploy/litemall-db cat litemall_schema.sql > db.sql -cat litemall.sql >> db.sql +cat litemall_table.sql >> db.sql +cat litemall_data.sql >> db.sql mysql -h localhost -u $ROOT -p$PASSWORD < db.sql rm db.sql diff --git a/deploy/util/upload.sh b/deploy/util/upload.sh index e7daf1d5..7c2aa865 100644 --- a/deploy/util/upload.sh +++ b/deploy/util/upload.sh @@ -37,7 +37,8 @@ tar -zcvf ./deploy/litemall-admin/dist.tar -C ./litemall-admin/dist . # 复制数据库 cp -f ./litemall-db/sql/litemall_schema.sql ./deploy/litemall-db/litemall_schema.sql -cp -f ./litemall-db/sql/litemall.sql ./deploy/litemall-db/litemall.sql +cp -f ./litemall-db/sql/litemall_table.sql ./deploy/litemall-db/litemall_table.sql +cp -f ./litemall-db/sql/litemall_data.sql ./deploy/litemall-db/litemall_data.sql # 上传云主机 scp -i $ID_RSA -r ./deploy $REMOTE:/home/ubuntu/ diff --git a/doc/1.md b/doc/1.md index 6f02613e..011e85d8 100644 --- a/doc/1.md +++ b/doc/1.md @@ -9,27 +9,46 @@ litemall是一个简单的商场系统,基于现有的开源项目,重新实 项目的架构是三个系统和六个模块: -* 基础系统(core),由数据库、litemall-cre模块、litemall-db模块、litemall-os-api模块和litemall-all模块组成; -* 小商场系统(wxmall),由litemall-wx-api模块和litemall-wx模块组成; -* 后台管理系统(admin),由litemall-admin-api模块和litemall-admin模块组成。 -* 简单商城系统(mall),这里仅列出,目前没有开发计划。 +* 基础系统子系统(core) + + 由数据库、litemall-cre模块、litemall-db模块、litemall-os-api模块和litemall-all模块组成; + +* 小商场子系统(wxmall) + + 由litemall-wx-api模块和litemall-wx模块组成; + +* 后台管理子系统(admin) + + 由litemall-admin-api模块和litemall-admin模块组成。 + +* 简单商城系统(mall) + + 这里仅列出,目前没有开发计划。 而六个模块的开发设计到三种技术栈: -* Spring Boot技术栈,采用IDEA开发工具,开发litemall-core、litemall-db、litemall-all、 +* Spring Boot技术栈 + + 采用IDEA开发工具,开发litemall-core、litemall-db、litemall-all、 litemall-os-api、litemall-admin-api和litemall-wx-api共六个个模块; -* miniprogram(微信小程序)技术栈,采用微信小程序开发工具,开发litemall-wx模块; -* Vue技术栈,采用VSC开发工具,开发litemall-admin模块。 + +* miniprogram(微信小程序)技术栈 + + 采用微信小程序开发工具,开发litemall-wx模块; + +* Vue技术栈 + + 采用VSC开发工具,开发litemall-admin模块。 ## 1.2 系统功能 -从业务功能上,目前由五个业务模块(参考nideshop-mini-program和platform)组成: +从业务功能上,目前由五个业务模块组成: * 会员业务模块 * 商场业务模块 * 商品业务模块 * 推广业务模块 -* 系统业务模块(仅管理平台) +* 系统业务模块 ### 1.2.1 小程序端功能 @@ -55,21 +74,16 @@ litemall是一个简单的商场系统,基于现有的开源项目,重新实 * 地址添加 * 我的收藏 * 我的足迹 -* 支付页面(待定) -* 优惠券选择(待定) -* 我的优惠券(待定) +* 支付页面 ### 1.2.2 管理平台功能 * 会员管理 * 会员管理 - * 收货地址管理 + * 收货地址 * 会员收藏 * 会员足迹 * 搜索历史 - * 购物车 - * 会员等级(待定) - * 会员优惠劵(待定) * 商城管理 * 区域配置 * 品牌制造商 @@ -79,17 +93,13 @@ litemall是一个简单的商场系统,基于现有的开源项目,重新实 * 关键词 * 渠道管理(待定) * 商品管理 - * 商品管理 - * 商品参数 - * 商品规格 - * 货品管理 + * 商品列表 + * 商品上架 + * 商品编辑 * 用户评论 - * 团购设置(待定) - * 商品满减搭配(待定) * 推广管理 - * 广告列表 + * 广告管理 * 专题管理 - * 优惠劵管理(待定) * 系统管理 * 管理员 * 对象存储 @@ -171,7 +181,9 @@ litemall是一个简单的商场系统,基于现有的开源项目,重新实 最后,其实dep和prod不存在先后关系。例如,如果开发者已经存在域名和生产环境,可以直接 跳过dep阶段,而直接部署在线上环境中。因此有些时候,这里部署和上线是一个阶段。 + 当然,这里仍然建议先dep后prod,是因为对于第一次开发而言,先dep阶段可以避免对域名、https证书等非业务相关工作的干扰。 + 此外,有些业务功能(例如微信支付)必须是域名访问,那么开发和部署阶段可以先采用模拟或跳过的形式, 先不开发和测试这样业务功能,等其他功能开发完毕和部署测试成功以后,再来开发这些线上环境才能 运行的功能,此时会有一个好的基础。 @@ -211,19 +223,13 @@ litemall是一个简单的商场系统,基于现有的开源项目,重新实 ### 1.4.0 数据库 按照开发环境前,需要先在本地配置好数据库: -1. 本项目目前采用MySQL,请上网下载安装MySQL 5.7.x - 注意: - > 1. MySQL必须安装5.7.x, 如果是5.6或者最新的8.0都会存在错误。 - > 目前原因还没有找到(可能是采用JSON引起),希望开发者注意或者有好的解决方案。 - > 2. 如果采用IDE工具导入数据,建议采用MySQL Workbench。如果采用navicat可能有时导入失败。 - > 这里失败的原因应该是sql文件是MySQL Workbench导出的,因此可能存在不兼容的格式。 +1. 本项目目前采用MySQL + + 注意:建议采用命令行或者MySQL Workbench。如果采用navicat可能导入失败。 2. 数据库文件存放在litemall-db/sql文件夹中,其中litemall_schema.sql创建数据库和用户权限, - 而litemall.sql则是具体的表和数据。开发者可以采用命令或者软件导入数据。 - - 注意: - > 不要运行nideshop.sql文件,这只是所参考nideshop项目的原始数据库,仅用于对比。 + litemall_table.sql则创建表,litemall_data.sql则是测试数据。 ### 1.4.1 Spring Boot开发环境 @@ -339,11 +345,9 @@ litemall是一个简单的商场系统,基于现有的开源项目,重新实 > 原因是这里的appid是本人申请的,而开发者不是本人app的开发者或体验者, > 因此微信登录导致后台服务向微信服务器交互时会失败。 > 2. 有的开发者可能认为把litemall-wx模块的appid设置成开发者自己的appid就可以, -> 但是实际上,由于小商场的云主机后台服务的appid仍然是本人appid, -> 因此微信登录时仍然会失败。 +> 但是由于小商场的云主机后台服务的appid仍然是本人appid,因此微信登录时仍然会失败。 > 3. 开发者可以在litemall-wx和litemall-wx-api中设置自己的appid,以及部署自己的后台服务, -> 相关内容请阅读章节3。 -> 或者,开发者可以采用账号登录的方式登录后台服务,然后体验商品购买下单的方式。 +> 相关内容请阅读章节3。或者,开发者可以采用账号登录方式登录,然后体验商品购买下单的方式。 > 4. 由于没有设置商户支付信息,因此开发者在付款时会失败。相关内容阅读章节3。 ### 1.4.3 Vue开发环境 @@ -356,6 +360,7 @@ litemall是一个简单的商场系统,基于现有的开源项目,重新实 ``` cnpm install ``` + 5. 编译并运行 ``` @@ -363,6 +368,7 @@ litemall是一个简单的商场系统,基于现有的开源项目,重新实 ``` 然后,打开浏览器,输入`http://localhost:9527`。 如果出现管理后台登录页面,则表明管理后台的前端运行正常; + 6. 请确定litemall-admin-api模块已经运行,然后点击`登录`,如果能够成功登录,则表明管理后台的前端和后端对接成功,运行正常。 ## 1.5 部署方案 @@ -379,11 +385,6 @@ litemall是一个简单的商场系统,基于现有的开源项目,重新实 * litemall-wx模块部署在微信开发者工具中,此外数据API地址指向litemall-wx-api所在服务qi地址 * litemall-admin编译出的静态文件放在web服务器或者tomcat服务器,此外服务器地址设置指向3中litemall-admin-api所在地址 -注意 -> * 这里litemall-os-api、litemall-admin-api和litemall-wx-api也可以选择编译成jar模式的可执行文件(因为内嵌tomcat服务器),然后直接运行。 -> * litemall-wx部署时可以放在微信开发者工具中测试,但是上线时必须设置https开头的合法域名,因此litemall-wx-api所在的服务器需要配置合适的域名和SSL证书, -> 具体参见官方文档和本项目的1.6节。 - 最后,**如果项目部署,则根据开发者的部署环境在以下文件中或代码中修改相应的配置。** 1. MySQL数据库设置合适的用户名和密码等信息,同时在litemall-os-api、litemall-wx-api和litemall-admin-api模块 @@ -414,8 +415,10 @@ litemall是一个简单的商场系统,基于现有的开源项目,重新实 以下简单列举几种方案。 -### 1.5.3 基于ubuntu腾讯云的单机云部署方案 -单机云部署方案,面向的是服务器数据和应用部署在云主机单机中用于演示的场景。 +### 1.5.3 单机云部署方案 + +本节介绍基于腾讯云的单机云部署方案,面向的是服务器数据和应用部署在云主机单机中用于演示的场景。 +其他云应该也是可行的。 主要流程是:创建云主机,安装ubuntu操作系统,按照JDK和MySQL应用运行环境,部署三个Spring Boot微服务后台应用, 以及部署管理后台静态文件到tomcat。 @@ -449,6 +452,7 @@ litemall是一个简单的商场系统,基于现有的开源项目,重新实 #### 1.5.3.2 JDK8 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 ```bash @@ -464,7 +468,6 @@ sudo apt-get install oracle-java8-set-default #### 1.5.3.3 MySQL https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-16-04 -https://www.linuxidc.com/Linux/2017-01/139502.htm ``` sudo apt-get update @@ -504,31 +507,34 @@ sudo apt-get install mysql-client * 命令行导入 -1. 采用FileZilla把本项目的litemall.sql上传到云主机 -2. 使用PuTTY登录云主机 -3. 进入MySQL,输入上节所设置的MySQL的root密码 -4. 创建数据库、 创建用户并分配访问权限 -5. 导入数据 -6. 退出 + 1. 采用FileZilla把本项目的数据库文件上传到云主机 + 2. 使用PuTTY登录云主机 + 3. 进入MySQL,输入上节所设置的MySQL的root密码 + 4. 创建数据库、 创建用户并分配访问权限 + 5. 导入数据 + 6. 退出 * MySQL Workbench导入 -1. 先确认已经在1.4.3节中设置了root可以远程访问; -2. 创建一个新的连接,设置`Hostname` 、`Username` 和`Password`, -然后点击`Test Connection`测试是否能够连接到云主机; -如果测试成功,则进入; -![](pic1/1-5.png) -3. 开发者自行学习文档,完成`创建数据库`、`创建用户`和`分配权限`三个操作; -4. 利用Workbench的`Server`菜单下的`Data Import`完成数据导入。 + 1. 先确认已经在1.4.3节中设置了root可以远程访问; + 2. 创建一个新的连接,设置`Hostname` 、`Username` 和`Password`, + 然后点击`Test Connection`测试是否能够连接到云主机; + 如果测试成功,则进入; + ![](pic1/1-5.png) + 3. 开发者自行学习文档,完成`创建数据库`、`创建用户`和`分配权限`三个操作; + 4. 利用Workbench的`Server`菜单下的`Data Import`完成数据导入。 * 命令脚本 -```bash -cd deploy -mysql -h localhost -u root -p123456 -source ./litemall-db/litemall_schema.sql -source ./litemall-db/litemall.sql -``` -其中123456是开发者所设置的MySQL的管理员密码。 + + ```bash + cd deploy + mysql -h localhost -u root -p123456 + source ./litemall-db/litemall_schema.sql + source ./litemall-db/litemall_table.sql + source ./litemall-db/litemall_data.sql + ``` + + 其中123456是开发者所设置的MySQL的管理员密码。 #### 1.5.3.5 Tomcat/Nginx @@ -537,15 +543,15 @@ source ./litemall-db/litemall.sql 因此这里需要安装tomcat或者nginx(或者其他服务器)。 这里可能有点绕: + 1. 开发方案,无需tomcat/nginx,直接用VSC即可; -2. 部署方案,采用tomcat,这里是因为80端口可能需要云主机备案; +2. 部署方案,采用tomcat,这里是因为80端口可能需要云主机备案因此采用tomcat使用8080端口; 3. 上线方案. 采用nginx,通过反向代理访问后台服务。 因此目前这里采用tomcat部署静态文件,通过8080端口访问管理后台。 当然,这里没有严格限制,开发者也可以直接配置好nginx,在部署方案就使用nginx来 -部署静态文件服务,通过80web端口访问管理后台。 +部署静态文件服务,通过web端口80访问管理后台。 - * Tomcat部署静态文件 1. 安装tomcat @@ -581,10 +587,10 @@ source ./litemall-db/litemall.sql 1. 安装nginx - ```bash - sudo apt-get update - sudo apt-get install nginx - ``` + ```bash + sudo apt-get update + sudo apt-get install nginx + ``` 2. 配置nginx指向`delpoy/litemall-admin/dist`目录 @@ -597,36 +603,32 @@ source ./litemall-db/litemall.sql #### 1.5.3.6 项目打包 +这里项目打包括两种: + 1. Spring Boot打包 -通常项目打包方案存在两种: + 采用如下命令进行项目打包 -* 一是打包war格式,因此云主机需要专门安装tomcat -* 二是打包可执行jar格式,此时内部包含嵌入式tomcat + ``` + cd litemall + mvn clean + mvn package + ``` -这里仅采用第二种方案,简化tomcat的安装配置过程。 - -采用如下命令进行项目打包 - -``` -cd litemall -mvn clean -mvn package -``` -此时,在litemall-os-api项目的target文件夹中看到litemall-os-xxx.jar; -在litemall-wx-api项目的target文件夹中看到litemall-wx-xxx.jar; -在litemall-admin-api项目的target文件夹中看到litemall-admin-xxx.jar。 + 此时,在litemall-os-api项目的target文件夹中看到litemall-os-xxx.jar; + 在litemall-wx-api项目的target文件夹中看到litemall-wx-xxx.jar; + 在litemall-admin-api项目的target文件夹中看到litemall-admin-xxx.jar。 2. Vue项目打包 -采用如下命令进行项目打包 + 采用如下命令进行项目打包 -````bash -cd litemall/litemall-admin -cnpm run build:dep -```` + ````bash + cd litemall/litemall-admin + cnpm run build:dep + ```` -此时,litemall-admin模块的dist文件夹中就是最终部署时的代码,可以先压缩,上传到云主机,再解压缩。 + 此时,litemall-admin模块的dist文件夹中就是最终部署时的代码,可以先压缩,上传到云主机,再解压缩。 #### 1.5.3.7 项目部署运行 @@ -648,7 +650,8 @@ https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#dep cd deploy mysql -h localhost -u root -p123456 source ./litemall-db/litemall_schema.sql - source ./litemall-db/litemall.sql + source ./litemall-db/litemall_table.sql + source ./litemall-db/litemall_data.sql ``` 注意,123456是开发者所设置的MySQL管理员密码 警告: @@ -705,6 +708,7 @@ cd litemall ### 1.5.4 集群式云部署方案 由于本项目是面向微小型企业的小商城系统,因此预期的分布式部署方案是 + 1. 专门的云数据库部署数据 2. 专门的云存储方案 3. 专门的CDN分发管理后台的静态文件 @@ -718,19 +722,21 @@ cd litemall 至少每个功能模块应该是独立服务系统。此外,需要引入单点登录系统、集群、缓存 和消息队列等多种技术。因此如果开发者需要这种形式的分布式方案,请参考其他项目。 -### 1.5.5 单主机单服务war部署方案 +### 1.5.5 war部署方案 -这里介绍另外一种单主机单服务的方案,即四个服务打包成一个war格式的项目部署包。 +这里介绍另外一种单主机单服务的war部署方案,即四个服务打包成一个war格式的项目部署包。 ![](pic1/1-11.png) 和1.5.3节相比,采用这样方案的原因是: + 1. 安装方便,是传统的web项目安装方式,在tomcat里面部署一个war项目包,即可完成安装; 2. 内存消耗少。在1.5.3节中四个独立的java环境消耗的内存大概1.2G多,而这里部署以后 只需要一个java环境,因此消耗内存只有1.5.3节方案的四分之一,适合1G云主机部署。 3. 只存在一个域名和端口,没有多个服务之间依赖关系。 具体的打包部署方案是; + 1. litemall-admin首先需要先编译得到静态文件; 2. 采用`mvn package`命令,litemall-all模块自动生成war格式的安装包,里面 包含了三个后台服务和静态文件; @@ -746,6 +752,7 @@ cd litemall ## 1.6 上线方案 在1.5节部署方案中,我们介绍了多种部署的方案,但是实际上这些方案都不能立即用于正式环境: + 1. 正式环境需要域名和HTTPS证书 2. 小商场的小程序端对服务器域名存在接入要求。 @@ -917,7 +924,7 @@ http://www.example.com > 本人对nginx也不了解,仅仅依靠网络知识配置了简单的效果。 > 更多配置方法和功能,请开发者自行学习。 -### 1.6.3 小商场的小程序端上线 +### 1.6.3 小程序端上线 这里参考小程序官方文档,上线自己的小商城。 @@ -965,7 +972,7 @@ http://www.example.com ### 1.6.5 上线脚本 -本项目的deploy文件夹是用于 +本项目目前没有上线脚本,不过可以参考1.5.3节中的部署脚本。 ### 1.6.6 优化 @@ -976,10 +983,18 @@ http://www.example.com 如果部署方案中采用tomcat而8080端口访问后台,而这里配置nginx后, 可以直接采用80端口访问,因此tomcat可以卸载。 -#### 1.6.6.2 静态文件托管CDN +#### 1.6.6.2 管理后台前端文件启动优化 -在上节中,建议采用卸载tomcat,采用nginx托管管理后台的静态文件。 -这里可以进一步地,把静态文件托管到CDN,当然这里是需要收费。 +litemall-admin编译得到的前端文件在第一次加载时相当耗时,这里需要一些措施来优化启动速度 + +* 静态文件托管CDN + + 在上节中,建议采用卸载tomcat,采用nginx托管管理后台的静态文件。 + 这里可以进一步地,把静态文件托管到CDN,当然这里是需要收费。 + +* gzip压缩 + +* 动态加载 #### 1.6.6.3 后台服务内部访问 diff --git a/doc/2.md b/doc/2.md index 16e9d4b1..b960f882 100644 --- a/doc/2.md +++ b/doc/2.md @@ -1,13 +1,20 @@ -# 2 litemall基础平台 +# 2 litemall基础系统 -目前litemall基础系统主要由litemall数据库、litemall-core模块、litemall-db模块、 -litemall-os-api模块和litemall-all模块组成。 +目前litemall基础系统由以下部分组成: + +* litemall数据库 +* litemall-core模块 +* litemall-db模块、 +* litemall-os-api模块 +* litemall-all模块组成 + +实际上,litemall**真正的基础系统**是litemall-core模块和litemall-db模块。 -实际上,属于litemall**真正的基础系统**是litemall-core模块和litemall-db模块。 litemall-os-api模块只是为了减少开发中对第三方图片存储服务依赖而实现的简单图像存储服务, -建议开发者最终部署时切换到第三方图片存储服务。litemall-all模块只是一个包裹模块, -没有任何代码,其作用是融合三个spring boot模块和litemall-adminm模块静态文件到 -一个单独spring boot应用中,并最终打包成war格式的项目安装包。 +建议开发者最终部署时切换到第三方图片存储服务。 + +litemall-all模块则只是一个包裹模块,几乎没有任何代码。该模块的作用是融合三个spring boot模块 +和litemall-admin模块静态文件到一个单独spring boot应用中,并最终打包成war格式的项目安装包。 目前存在的问题: @@ -15,19 +22,43 @@ litemall-os-api模块只是为了减少开发中对第三方图片存储服务 * `改善` litemall-db的一些CRUD操作可以基于开源库重构 * `功能`可以参考一些云存储服务的API加强一些功能 -## 2.1 litemall.sql +## 2.1 litemall数据库 -litemall.sql数据库基于nideshop中的[nideshop.sql](https://github.com/tumobi/nideshop/blob/master/nideshop.sql)数据库,然后在实际开发过程中进行了调整和修改: +litemall数据库基于nideshop中的[nideshop.sql](https://github.com/tumobi/nideshop/blob/master/nideshop.sql)数据库,然后在实际开发过程中进行了调整和修改: * 删除了一些目前不必要的表; * 删除了表中一些目前不必要的字段; * 行政区域数据litemall_region没有采用原nideshop中的数据,而是采用了[Administrative-divisions-of-China](https://github.com/modood/Administrative-divisions-of-China); -* 表中的某些字段采用JSON; +* 表中的某些字段采用JSON存储; * 表中的日期或时间字段采用DATE、DATETIME; * 字段的数据类型粗粒度化,例如避免MEDIUMINT,而是INT; * 表的数据做了清理、调整和补充(假数据)。 -具体不同可以比较litemall-db模块下sql文件夹中nideshop.sql和litemall.sql。 +litemall数据库由三个sql文件组成,在litemall-db文件夹下面的sql文件夹中: + +1. litemall_schema.sql + + 作用是创建空数据库、创建用户、设置访问权限。 + + 开发者开发测试阶段可以使用,但是部署生产阶段一定要注意修改这里的默认用户名和密码。 + + 注意,这里的sql文件不一定需要运行,开发者可以自己手动或命令行或IDE进行对应的操作即可。 + +2. litemall_table.sql + + 作用是创建数据库表,但是没有创建任何数据。 + + 因此,开发者可以在部署生产阶段直接使用。 + +3. litemall_data.sql + + 作用是创建测试数据。 + + 这里的测试数据来自开源项目[nideshop-mini-program](https://github.com/tumobi/nideshop-mini-program) + + 开发者开发测试阶段可以使用,但是部署开发阶段应该使用自己的数据。 + +综上,这里litemall真正必须运行的sql文件是litemall_table.sql,其他两个sql文件开发者自行决定如何是否使用。 以下讨论一些关键性设计 @@ -36,7 +67,7 @@ litemall.sql数据库基于nideshop中的[nideshop.sql](https://github.com/tumob ### 2.1.1 商品和货品设计 -这里商品存在商品,商品属性,商品规格,货品四种表 +这里商品存在商品表(litemall_goods),商品属性表(litemall_goods_attribute),商品规格表(litemall_goods_specification),商品货品表(litemall_product)四种表 商品表是一种商品的基本信息,主要包括商品介绍,商品图片,商品所属类目,商品品牌商等; @@ -53,9 +84,10 @@ litemall.sql数据库基于nideshop中的[nideshop.sql](https://github.com/tumob * 多个规格和单一规格值,可以简化成第一种情况,或者采用第四种情况,通常实际情况下不常见; * 多个规格和多个规格值,通常是两种规格或者三种规格较为常见,而且对应的价格不完全相同。 -货品则是最终面向开发者购买的商品标识,存在多个规格值、数量和价格。 +商品货品则是最终实现商品库存管理、购买业务的实体对象,存在多个规格值、数量和价格。 +例如,同样的衣服品牌,可能因为不能尺寸和颜色而存在最终的货品,这里每个货品的价格可以一样,也可以不一样。 -因此这里一个商品表项,存在(至少0个)多个商品属性表项目,存在(至少一个)多个商品规格表项, +总结一下,一个普通商品,实际上在数据库中,存在一个商品表项,存在(至少0个)多个商品属性表项目,存在(至少一个)多个商品规格表项, 存在(至少一个)多个货品表项。 举例如下: @@ -64,7 +96,7 @@ litemall.sql数据库基于nideshop中的[nideshop.sql](https://github.com/tumob * 存在两个商品参数, * 属性名称“面向人群”,属性值“男士” * 属性名称“面料”,属性值“100%棉” -* 存在两种规格共八个商品规格项, +* 存在两种规格(分别五个规格值和三个规格值)共八个商品规格项, * 规格名称“尺寸”,规则值“S” * 规格名称“尺寸”,规则值“M” * 规格名称“尺寸”,规则值“L” @@ -102,8 +134,7 @@ litemall.sql数据库基于nideshop中的[nideshop.sql](https://github.com/tumob * 商品规格可以存在规格图片,效果是规格名称前放置规格图片 * 货品也可以存在货品图片,效果是所有规格选定以后对应的货品有货,则在货品价格前放置货品图片 * 如果商品是两种规格,分别是M个和N个规格值,那么通常应该是`M*N`个货品,但是有些货品可能天然不存在。 - 那么,此时数据库如何来设计,是允许少于`M*N`个项,还是必须等于`M*N`个,而不存在货品的数量设置为0? - * + 目前这里要求所有货品信息都应该存在,如果实际中货品不存在,也要设置商品数量为0. 注意: @@ -112,7 +143,9 @@ litemall.sql数据库基于nideshop中的[nideshop.sql](https://github.com/tumob ### 2.1.2 用户和微信用户设计 -目前准备支持用户普通账号登录和微信登录两种方式,两种登录方式仅仅采用一个litemall-user表可能不是很合适。此外,如果进一步支持其他多种第三方登录,那么这里需要重新设计。 +目前准备支持用户普通账号登录和微信登录两种方式,两种登录方式仅仅采用一个litemall-user表可能不是很合适。 + +外,如果进一步支持其他多种第三方登录,那么这里需要重新设计。 ### 2.1.3 行政区域设计 @@ -199,16 +232,16 @@ litemall.sql数据库基于nideshop中的[nideshop.sql](https://github.com/tumob 注意: > 1. 目前退款相关功能未完成。 > 2. 在上图中可以看到`101`到`101`的状态变化,这里只是小商场用户的操作,不会影响订单状态码。 -> * 用户点击付款时,后台服务会生成预支付会话id,但是不会影响订单状态。 -> * 而用户支付过程中,放弃支付,例如没有 +> 如果用户点击付款时,后台服务会生成预支付会话id,但是不会影响订单状态。 +> 如果而用户支付过程中,放弃支付,则也不会影响订单状态。 #### 2.1.4.2 状态变化所对应的流程 -* -> 101 +* 初始 -> 101 小商场用户在小商场点击`下单`按钮,此时小商城后台服务会生产商户订单。 -所对应的后台服务方法是litemall-wx-api模块的`WxOrderController.submit` +所对应的后台服务方法是litemall-wx-api模块的`WxOrderController.submit`方法。 * 101 -> 101 @@ -332,6 +365,7 @@ litemall.sql数据库基于nideshop中的[nideshop.sql](https://github.com/tumob 用户可以`确认收货` * 401 + 用户可以`退货`、`删除`、`去评价`、`再次购买` * 402 @@ -373,8 +407,7 @@ litemall.sql数据库基于nideshop中的[nideshop.sql](https://github.com/tumob ## 2.2 litemall-db -litemall-db模块是一个普通的Spring Boot应用,基于mybatis技术提供开发者 -访问数据库的功能。 +litemall-db模块是一个普通的Spring Boot应用,基于mybatis框架实现数据库访问操作,对外提供业务数据访问服务。 此外,litemall-db最终是作为一个类库被其他模块所依赖使用,因此并不对外 直接服务,没有使用Spring MVC技术。 @@ -392,13 +425,26 @@ litemall-db模块是一个普通的Spring Boot应用,基于mybatis技术提供 这里litemall-db模块可以分成以下几种代码: -* mybatis generator自动化代码 +* mybatis数据库访问代码 + * generator生成代码 + * 非generator手动代码 * 业务代码 * 安全代码 * JSON支持代码 * 配置代码 -### 2.2.1 自动化代码 +### 2.2.1 mybatis数据库访问代码 + +mybatis数据库访问代码是指dao接口代码、dao数据库XML文件和domain代码: +* dao接口代码,声明了数据库访问接口 +* dao数据库XML文件,实现了数据库访问操作 +* domain代码,则是保存数据库返回数据。 + +此外,这里的数据库访问代码又进一步分成 +* generator生成代码,即基于mybatis generator相关插件自动生成上述三种代码或文件; +* 非generator手动代码,则是需要开发者自己编写上述三种代码。 + +#### 2.2.1.1 generator生成代码 ![](./pic2/2-3.png) @@ -407,7 +453,7 @@ litemall-db模块是一个普通的Spring Boot应用,基于mybatis技术提供 1. 读取`mybatis-generator`文件夹下的`generatorConfig.xml`文件 2. 根据`jdbcConnection`访问数据库 3. 根据`table`, 自动生成三种代码: - * src文件夹`org.linlinjava.litemall.db.domain` 包内的Java代码 + * src文件夹`org.linlinjava.litemall.db.dao` 包内的Java代码 * src文件夹`org.linlinjava.litemall.db.domain` 包内的Java代码 * resources文件夹`org.linlinjava.litemall.db.domain.dao` 内的XML文件 @@ -460,9 +506,38 @@ litemall-db模块是一个普通的Spring Boot应用,基于mybatis技术提供 关于mybatis generator的用法,可以参考: https://blog.csdn.net/isea533/article/details/42102297 +#### 2.2.1.2 非generator手动代码 + +虽然generator可以自动生产代码,帮助开发者简化开发工作,但是在涉及到多表操作或特殊数据库操作时, +仍然需要开发者自己手动编写mybatis框架代码。 + +具体如何基于mybatis框架编写代码,请开发者自己查找资料。 + +这里,以统计功能举例说明: + +1. dao代码 + + 在src文件夹`org.linlinjava.litemall.db.domain` 包内的StatMapper.java代码定义了数据库访问的接口 + +2. domain代码 + + 如果希望数据库操作返回数据模型,可以在src文件夹`org.linlinjava.litemall.db.domain` 包内创建相应代码。 + 而这里统计功能是采用简化的`List`保存数据,没有定义domain代码。 + +3. XML文件 + + 在resources文件夹`org.linlinjava.litemall.db.domain.dao` 内的StatMapper.xml文件则是实现真正的数据库访问操作。 + + ### 2.2.2 业务代码 -基于2.2.1的代码,业务代码处理一些具体业务相关的操作,对其他模块提供具体的服务。 +虽然2.2.1节所述代码已经能够提供数据库访问操作,但是这里仍然会抽象出业务访问层代码,即基于2.2.1所述代码和实际业务需求 +实现一些具体业务相关的操作,对其他模块提供便捷业务数据服务。 + +需要指出的是,这里的业务代码往往是单表相关的业务代码,而涉及到多表操作的java代码通常是在其他高层模块中实现。 +这里的业务分层并不是绝对的。例如,开发者可以取消这里的业务代码,而在其他模块中直接调用2.2.1所述代码。 + +通常业务层代码在src文件夹`org.linlinjava.litemall.db.service` 包中。 ### 2.2.3 安全代码 @@ -562,12 +637,16 @@ https://blog.csdn.net/isea533/article/details/42102297 开发者可以自行修改代码进行真正的物理删除。 -### 2.2.8 乐观锁 +### 2.2.8 并发访问 由于服务是多线程并发的,因此这带来了多线程同时操作数据库中同一数据的问题。 由于数据极少删除或者是逻辑删除,因此操作数据,可以简化成更新数据。 也就是说,需要解决多线程更新数据库同一数据的并发问题。 +* https://blog.csdn.net/qq315737546/article/details/76850173 +* http://baijiahao.baidu.com/s?id=1571172971189129 +* https://blog.csdn.net/speedme/article/details/48525119 + 例如,下单操作中,用户A购买商品G的数量是1个,而用户B同一时间也购买商品G的 数量也是1个,那么如果没有很好地并发控制,有可能商品G的数量仅仅是减1,而不是 设想的2。 @@ -583,14 +662,6 @@ https://blog.csdn.net/isea533/article/details/42102297 * 如果相同,说明数据没有改变则可以更新,数据更新同时version调整一个新值; * 如果不相同,则说明数据改变了则更新失败,不能修改数据。 -当然,这里好像也存在一个漏洞,3中所比较的数据库当前version字段的值有可能修改过 -但是恰巧没有变。例如version加1再减1,那么查询时该值并没有变化。当然,如果version -是单调自增,则应该不存在这个问题。 - -具体技术细节如下: - -1. - 当然,由于采用乐观锁,这里也会带来另外一个问题: 数据库有可能更新失败,那么如何处理更新失败的情况? @@ -605,7 +676,25 @@ https://blog.csdn.net/isea533/article/details/42102297 开发者需要注意这个问题,可能需要采用其他技术来解决或避免。 -### 2.2.9 mybatis增强框架 +### 2.2.9 事务管理 + +litemall-db模块中不涉及到事务管理,而是在其他后台服务模块中涉及。 +但是其他后台服务模块因为依赖litemall-db模块,因此这里列出。 + +事务管理的问题出现在多个表的修改操作中。 + +例如用户A修改表1,再修改表2,而如果修改表2的时候出现错误推出, +此时如果没有引入事务管理,那么这里会存在表1数据已跟新,表2数据 +未更新的问题。 + +解决的方案是采用spring自带的事务管理机制。 +当事务管理中的任何SQL操作出现错误而抛出异常时,则回滚之前的操作。 + +注意: +> 并发访问是多个用户同时操作单个表时可能出现的问题; +> 而事务管理是单个用户操作多个表时可能出现的问题。 + +### 2.2.10 mybatis增强框架 通过mybatis-generator已经自动生成了很多代码,而且具有一定的功能, 但是开发者仍然需要基于生成的代码写一些固定的CRUD代码。 @@ -757,5 +846,4 @@ bcypt代码本质上是spring里面的代码。 注意,这里只是简单的复制。因此开发者需要保证litemall-all打包前,litemall-admin 模块内dist目录下静态文件已经生成。 -最终打包以后则是一个war格式的项目包,包含了三个后台服务和静态文件。 - +最终打包以后则是一个war格式的项目包,包含了三个后台服务和静态文件。 \ No newline at end of file diff --git a/doc/3.md b/doc/3.md index 3107d049..c8e5ad7b 100644 --- a/doc/3.md +++ b/doc/3.md @@ -94,6 +94,8 @@ wx.app-secret=开发者申请的app-secret ## 3.1 litemall-wx-api +本节介绍小商场的后台服务模块。 + ### 3.1.1 授权服务 #### 3.1.1.1 账号登录 @@ -266,7 +268,7 @@ wx.app-secret=开发者申请的app-secret ### 3.1.16 安全 -#### 3.1.161 Token +#### 3.1.16.1 Token 用户登录成功以后,后端会返回`token`,之后用户的请求都会携带token。 @@ -389,30 +391,36 @@ var WxApiRoot = 'http://localhost:8082/wx/'; ### 3.2.2 页面 -#### 3.2.2.1 首页 - -#### 3.2.2. - -#### 3.2.2. - -#### 3.2.2. - -#### 3.2.2. - -#### 3.2.2. - -#### 3.2.2. - -#### 3.2.2. -#### 3.2.2. - -#### 3.2.2. -#### 3.2.2. -#### 3.2.2. -#### 3.2.2. -#### 3.2.2. -#### 3.2.2. - +* 首页 +* 专题页 +* 专题详情页 +* 专题评论页 +* 专题评论添加页 +* 品牌页 +* 品牌详情页 +* 人气推荐页 +* 新品首发页 +* 分类页 +* 分类详情页 +* 查找页 +* 商品详情页 +* 商品评论页 +* 购物车页 +* 下单页 +* 下单地址页 +* 下单地址添加页 +* 支付结果页 +* 个人页 +* 订单列表页 +* 订单详情页 +* 优惠券页 +* 收藏页 +* 足迹页 +* 收货地址页 +* 收货地址添加页 +* 登录页 +* 注册页 +* 找回密码页 ### 3.2.3 登录设计 @@ -489,16 +497,6 @@ var WxApiRoot = 'http://localhost:8082/wx/'; }); ``` -#### 3.2.2.4 登录拒绝授权 - -还存在一个问题,当用户登录时,会出现"微信授权"的对话框。 -如果用户选择”拒绝“,那么之后用户的登录操作总是失败的。 - -目前的做法是,用户拒绝授权后,如果再次登录,则: -1. 弹出对话框,请求用户授权 -2. 如果用户仍然拒绝,则返回 -3. 如果用户接受授权,则弹出系统权限配置页面,等待用户给与授权。 - ### 3.2.4 立即购买和放入购物车 ### 3.2.5 storage使用 diff --git a/doc/4.md b/doc/4.md index 4eb720e6..7c0d5b45 100644 --- a/doc/4.md +++ b/doc/4.md @@ -1,7 +1,5 @@ # 4 litemall管理后台 -这里的后台管理业务参考了[platform](https://gitee.com/fuyang_lipengjun/platform). - 项目技术架构: * 后台管理前端,即litemall-admin模块 @@ -46,16 +44,61 @@ ## 4.1 litemall-admin-api -### 4.1.1 +本节介绍管理后台的后台服务模块。 + +### 4.1.1 授权服务 -### 4.1.2 安全配置 -### 4.1.3 CROS配置 +### 4.1.16 安全 + +#### 4.1.16.1 Token + +用户登录成功以后,后端会返回`token`,之后用户的请求都会携带token。 + +目前token的失效和跟新机制没有涉及。 + +#### 4.1.16.2 CROS + +如果litemall-admin-api不配置CROS,则Spring Boot会失败。 + +#### 4.1.16.3 账号密码加盐 + +如果是微信登录,那么无需账号和密码。 + +而如果用户采用了账号和密码的形式登录,那么后端需要把用户密码加盐。 + +#### 4.1.16.4 限制登录 + +如果采用账号密码登录,那么登录失败一定次数,应该限制登录。 + +进一步地,如果项目启用了短信功能,应该短信提醒用户,防止他人登录。 + +目前这里没有实现,仅列出。 + +### 4.1.17 定时任务 + +### 4.1.18 并发控制 + +参考`2.2.8 乐观锁` + +当乐观锁更新失败时采用多次尝试方案。 + +### 4.1.19 事务管理 + +### 4.1.20 开发技巧 + +当小商城后台服务开发中因为测试或者debug可能需要经常性重启应用,此时 +一旦重启,将导致小商场的小程序段的token失效,因此要求用户再次登录。 +这里,介绍一个小技巧: +开发时, + ## 4.2 litemall-admin +本节介绍管理后台的前端模块。 + litemall-admin模块的代码基于[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) ### 4.2.1 diff --git a/doc/7.md b/doc/7.md index aed61221..e01dd361 100644 --- a/doc/7.md +++ b/doc/7.md @@ -2,7 +2,7 @@ ## 7.1 小商城 -### 7.1.1 为什么小程序微信登录失败 +### 7.1.1 小程序微信登录失败 原因: @@ -13,11 +13,25 @@ 1. 如果只是体验商品购买流程,开发者可以采用账号注册登录方式。 2. 开发者在litemall-wx和litemall-wx-api模块的appid等信息设置成自己申请的信息。 -#### 7.1.2 为什么开发者工具运行正常,但是手机真机测试不正常? +#### 7.1.1.1 appid已经修改,微信登陆仍然失败 现象: -手机真机扫描加载小商场以后,只有页面结构,没有数据和图片。 +微信开发者工具修改了开发者自己申请的appid,后台也更新了相应信息,但是微信登录仍然报错。 + +原因: + +这里可能是缓存问题,虽然修改了appid,但是微信开发者工具未及时跟新。 + +解决方案: + +微信开发者工具中修改appid以后,请关闭litemall-wx项目或者微信开发者工具,重新启动导入litemall-wx。 + +### 7.1.2 手机真机测试不正常 + +现象: + +微信开发者工具打开正常,但是手机真机扫描加载小商场以后,只有页面结构,没有数据和图片。 原因: @@ -31,7 +45,11 @@ * 小商场的后台服务未启动 2. 手机小商场的**调试功能**开启 -#### 7.1.3 为什么本人手机测试正常,而第三者手机测试不正常? +### 7.1.3 第三方手机测试不正常 + +现象: + +本人手机测试正常,而第三者手机测试不正常。 解决方案: @@ -39,7 +57,11 @@ 2. 第三者手机小商场的**调试功能**开启 3. 在微信小程序平台设置第三者的微信账号是**体验者** -#### 7.1.4 小商场为什么不能支付,或者为什么点击支付总是报错? +### 7.1.4 微信支付失败 + +现象: + +小商场不能支付,或者点击支付总是报错 原因: @@ -54,9 +76,9 @@ wx.notify-url= 解决方案: -参考`3.0 小商场环境`,设置好相应支付配置信息 +参考`3.0 小商场环境`,设置相应支付配置信息 -#### 7.1.5 为什么小商城支付成功,但是返回以后商品订单? +### 7.1.5 支付成功,但商品仍未付款 现象: @@ -79,4 +101,26 @@ wx.notify-url= ## 7.2 管理后台 -## 7.3 基础平台 \ No newline at end of file +### 7.2.1 Invalid bound statement + +现象: + +有时(特别是采用mybatis generator重新生成代码)后台服务报错 + +``` +org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): org.linlinjava.litemall.db.dao.XXXX +``` + +原因: + +应该是自动生成的新的XML文件没有及时跟新到编译文件夹target中,造成了target中mybatis的Java代码和XML文件不对应。 + +解决方案: + +采用maven命令或者插件先清理项目再重新编译打包,例如 +```bash +mvn clean +mvn package +``` + +## 7.3 基础系统 \ No newline at end of file diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 00000000..e8d86464 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,12 @@ +# Table of Contents + +* [介绍](../README.md) +* [更新日志](../CHANGELOG.md) +* [贡献指南](../CONTRIBUTE.md) +* [1. 系统架构](./1.md) +* [2. 基础系统](./2.md) +* [3. 小商场](./3.md) +* [4. 管理后台](./4.md) +* [5. 商场](./5.md) +* [6. 下一步计划](./6.md) +* [7. FAQ](./7.md) \ No newline at end of file diff --git a/litemall-admin-api/pom.xml b/litemall-admin-api/pom.xml index c7f580f1..e9510a83 100644 --- a/litemall-admin-api/pom.xml +++ b/litemall-admin-api/pom.xml @@ -25,6 +25,22 @@ + + + + src/main/resources + + + + src/main/java + + **/*.properties + **/*.xml + + false + + + org.springframework.boot diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/dao/GoodsAllinone.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/dao/GoodsAllinone.java new file mode 100644 index 00000000..b085f144 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/dao/GoodsAllinone.java @@ -0,0 +1,47 @@ +package org.linlinjava.litemall.admin.dao; + +import org.linlinjava.litemall.db.domain.LitemallGoods; +import org.linlinjava.litemall.db.domain.LitemallGoodsAttribute; +import org.linlinjava.litemall.db.domain.LitemallGoodsSpecification; +import org.linlinjava.litemall.db.domain.LitemallProduct; + +public class GoodsAllinone { + LitemallGoods goods; + LitemallGoodsSpecification[] specifications; + LitemallGoodsAttribute[] attributes; + // 这里采用 Product 再转换到 LitemallProduct + LitemallProduct[] products; + + public LitemallGoods getGoods() { + return goods; + } + + public LitemallProduct[] getProducts() { + return products; + } + + public void setProducts(LitemallProduct[] products) { + this.products = products; + } + + public void setGoods(LitemallGoods goods) { + this.goods = goods; + } + + public LitemallGoodsSpecification[] getSpecifications() { + return specifications; + } + + public void setSpecifications(LitemallGoodsSpecification[] specifications) { + this.specifications = specifications; + } + + public LitemallGoodsAttribute[] getAttributes() { + return attributes; + } + + public void setAttributes(LitemallGoodsAttribute[] attributes) { + this.attributes = attributes; + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/dao/Product.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/dao/Product.java new file mode 100644 index 00000000..b2636a10 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/dao/Product.java @@ -0,0 +1,42 @@ +package org.linlinjava.litemall.admin.dao; + +import java.math.BigDecimal; + +public class Product { + String[] specifications; + BigDecimal price; + Integer number; + String url; + + public String[] getSpecifications() { + return specifications; + } + + public void setSpecifications(String[] specifications) { + this.specifications = specifications; + } + + public BigDecimal getPrice() { + return price; + } + + public void setPrice(BigDecimal price) { + this.price = price; + } + + public Integer getNumber() { + return number; + } + + public void setNumber(Integer number) { + this.number = number; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/util/CatVo.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/util/CatVo.java new file mode 100644 index 00000000..033bb944 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/util/CatVo.java @@ -0,0 +1,33 @@ +package org.linlinjava.litemall.admin.util; + +import java.util.List; + +public class CatVo { + private Integer value = null; + private String label = null; + private List children = null; + + public Integer getValue() { + return value; + } + + public void setValue(Integer value) { + this.value = value; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/util/StatVo.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/util/StatVo.java new file mode 100644 index 00000000..a19b97f2 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/util/StatVo.java @@ -0,0 +1,31 @@ +package org.linlinjava.litemall.admin.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class StatVo { + private String[] columns = new String[0]; + private List rows = new ArrayList<>(); + + public String[] getColumns() { + return columns; + } + + public void setColumns(String[] columns) { + this.columns = columns; + } + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public void add(Map ... r) { + rows.addAll(Arrays.asList(r)); + } +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAdController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAdController.java index d57b1398..03015713 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAdController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAdController.java @@ -9,6 +9,7 @@ import org.linlinjava.litemall.core.util.ResponseUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import java.time.LocalDateTime; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -45,6 +46,7 @@ public class AdminAdController { if(adminId == null){ return ResponseUtil.unlogin(); } + ad.setAddTime(LocalDateTime.now()); adService.add(ad); return ResponseUtil.ok(ad); } diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAddressController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAddressController.java index a71befc9..4b947c44 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAddressController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAddressController.java @@ -11,6 +11,7 @@ import org.linlinjava.litemall.core.util.ResponseUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -53,7 +54,7 @@ public class AdminAddressController { @RequestParam(value = "limit", defaultValue = "10") Integer limit, String sort, String order){ if(adminId == null){ - return ResponseUtil.fail401(); + return ResponseUtil.unlogin(); } List addressList = addressService.querySelective(userId, name, page, limit, sort, order); @@ -75,7 +76,7 @@ public class AdminAddressController { @PostMapping("/create") public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallAddress address){ if(adminId == null){ - return ResponseUtil.fail401(); + return ResponseUtil.unlogin(); } String mobile = address.getMobile(); @@ -83,6 +84,7 @@ public class AdminAddressController { return ResponseUtil.fail(403, "手机号格式不正确"); } + address.setAddTime(LocalDateTime.now()); addressService.add(address); Map addressVo = toVo(address); @@ -92,7 +94,7 @@ public class AdminAddressController { @GetMapping("/read") public Object read(@LoginAdmin Integer adminId, Integer addressId){ if(adminId == null){ - return ResponseUtil.fail401(); + return ResponseUtil.unlogin(); } LitemallAddress address = addressService.findById(addressId); @@ -103,7 +105,7 @@ public class AdminAddressController { @PostMapping("/update") public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallAddress address){ if(adminId == null){ - return ResponseUtil.fail401(); + return ResponseUtil.unlogin(); } addressService.updateById(address); Map addressVo = toVo(address); @@ -113,7 +115,7 @@ public class AdminAddressController { @PostMapping("/delete") public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallAddress address){ if(adminId == null){ - return ResponseUtil.fail401(); + return ResponseUtil.unlogin(); } addressService.delete(address.getId()); return ResponseUtil.ok(); diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAdminController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAdminController.java index 507eec81..543381ef 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAdminController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAdminController.java @@ -9,6 +9,7 @@ import org.linlinjava.litemall.db.service.LitemallAdminService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -72,6 +73,7 @@ public class AdminAdminController { BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String encodedPassword = encoder.encode(rawPassword); admin.setPassword(encodedPassword); + admin.setAddTime(LocalDateTime.now()); adminService.add(admin); return ResponseUtil.ok(admin); diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminBrandController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminBrandController.java index 26cd179f..ee1e6d36 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminBrandController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminBrandController.java @@ -9,6 +9,7 @@ import org.linlinjava.litemall.core.util.ResponseUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import java.time.LocalDateTime; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -45,6 +46,7 @@ public class AdminBrandController { if(adminId == null){ return ResponseUtil.unlogin(); } + brand.setAddTime(LocalDateTime.now()); brandService.add(brand); return ResponseUtil.ok(brand); } diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCartController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCartController.java deleted file mode 100644 index dd1a7aa1..00000000 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCartController.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.linlinjava.litemall.admin.web; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.linlinjava.litemall.admin.annotation.LoginAdmin; -import org.linlinjava.litemall.db.domain.LitemallCart; -import org.linlinjava.litemall.db.service.LitemallCartService; -import org.linlinjava.litemall.db.service.LitemallGoodsService; -import org.linlinjava.litemall.db.service.LitemallProductService; -import org.linlinjava.litemall.db.service.LitemallUserService; -import org.linlinjava.litemall.core.util.ResponseUtil; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@RestController -@RequestMapping("/admin/cart") -public class AdminCartController { - private final Log logger = LogFactory.getLog(AdminCartController.class); - - @Autowired - private LitemallCartService cartService; - @Autowired - private LitemallUserService userService; - @Autowired - private LitemallGoodsService goodsService; - @Autowired - private LitemallProductService productService; - - @GetMapping("/list") - public Object list(@LoginAdmin Integer adminId, - Integer userId, Integer goodsId, - @RequestParam(value = "page", defaultValue = "1") Integer page, - @RequestParam(value = "limit", defaultValue = "10") Integer limit, - String sort, String order){ - if(adminId == null){ - return ResponseUtil.fail401(); - } - List cartList = cartService.querySelective(userId, goodsId, page, limit, sort, order); - int total = cartService.countSelective(userId, goodsId, page, limit, sort, order); - - Map data = new HashMap<>(); - data.put("total", total); - data.put("items", cartList); - - return ResponseUtil.ok(data); - } - - /* - * 目前的逻辑不支持管理员创建 - */ - @PostMapping("/create") - public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallCart cart){ - if(adminId == null){ - return ResponseUtil.fail401(); - } - - return ResponseUtil.fail501(); - } - - @GetMapping("/read") - public Object read(@LoginAdmin Integer adminId, Integer id){ - if(adminId == null){ - return ResponseUtil.fail401(); - } - - LitemallCart cart = cartService.findById(id); - return ResponseUtil.ok(cart); - } - - /* - * 目前的逻辑不支持管理员创建 - */ - @PostMapping("/update") - public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallCart cart){ - if(adminId == null){ - return ResponseUtil.fail401(); - } - return ResponseUtil.fail501(); - } - - @PostMapping("/delete") - public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallCart cart){ - if(adminId == null){ - return ResponseUtil.fail401(); - } - cartService.deleteById(cart.getId()); - return ResponseUtil.ok(); - } - -} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCategoryController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCategoryController.java index 5eef9988..b2a350af 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCategoryController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCategoryController.java @@ -3,12 +3,15 @@ package org.linlinjava.litemall.admin.web; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.admin.util.CatVo; import org.linlinjava.litemall.db.domain.LitemallCategory; import org.linlinjava.litemall.db.service.LitemallCategoryService; import org.linlinjava.litemall.core.util.ResponseUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -45,6 +48,7 @@ public class AdminCategoryController { if(adminId == null){ return ResponseUtil.unlogin(); } + category.setAddTime(LocalDateTime.now()); categoryService.add(category); return ResponseUtil.ok(); } @@ -89,11 +93,13 @@ public class AdminCategoryController { // 所有一级分类目录 List l1CatList = categoryService.queryL1(); - HashMap data = new HashMap<>(l1CatList.size()); + List> data = new ArrayList<>(l1CatList.size()); for(LitemallCategory category : l1CatList){ - data.put(category.getId(), category.getName()); + Map d = new HashMap<>(2); + d.put("value", category.getId()); + d.put("label", category.getName()); + data.add(d); } return ResponseUtil.ok(data); } - } diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCommentController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCommentController.java index 5b43ad21..c0156711 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCommentController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminCommentController.java @@ -9,6 +9,7 @@ import org.linlinjava.litemall.core.util.ResponseUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import java.time.LocalDateTime; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -45,6 +46,7 @@ public class AdminCommentController { if(adminId == null){ return ResponseUtil.unlogin(); } + comment.setAddTime(LocalDateTime.now()); commentService.add(comment); return ResponseUtil.ok(comment); } diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGoodsAttributeController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGoodsAttributeController.java deleted file mode 100644 index 6a5745c6..00000000 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGoodsAttributeController.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.linlinjava.litemall.admin.web; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.linlinjava.litemall.admin.annotation.LoginAdmin; -import org.linlinjava.litemall.db.domain.LitemallGoodsAttribute; -import org.linlinjava.litemall.db.service.LitemallGoodsAttributeService; -import org.linlinjava.litemall.core.util.ResponseUtil; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@RestController -@RequestMapping("/admin/goods-attribute") -public class AdminGoodsAttributeController { - private final Log logger = LogFactory.getLog(AdminGoodsAttributeController.class); - - @Autowired - private LitemallGoodsAttributeService goodsAttributeService; - - @GetMapping("/list") - public Object list(@LoginAdmin Integer adminId, - Integer goodsId, - @RequestParam(value = "page", defaultValue = "1") Integer page, - @RequestParam(value = "limit", defaultValue = "10") Integer limit, - String sort, String order){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - - List goodsAttributeList = goodsAttributeService.querySelective(goodsId, page, limit, sort, order); - int total = goodsAttributeService.countSelective(goodsId, page, limit, sort, order); - Map data = new HashMap<>(); - data.put("total", total); - data.put("items", goodsAttributeList); - - return ResponseUtil.ok(data); - } - - @PostMapping("/create") - public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsAttribute goodsAttribute){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - goodsAttributeService.add(goodsAttribute); - return ResponseUtil.ok(goodsAttribute); - } - - @GetMapping("/read") - public Object read(@LoginAdmin Integer adminId, Integer id){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - - if(id == null){ - return ResponseUtil.badArgument(); - } - - LitemallGoodsAttribute goodsAttribute = goodsAttributeService.findById(id); - return ResponseUtil.ok(goodsAttribute); - } - - @PostMapping("/update") - public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsAttribute goodsAttribute){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - goodsAttributeService.updateById(goodsAttribute); - return ResponseUtil.ok(goodsAttribute); - } - - @PostMapping("/delete") - public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsAttribute goodsAttribute){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - goodsAttributeService.deleteById(goodsAttribute.getId()); - return ResponseUtil.ok(); - } - -} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGoodsController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGoodsController.java index a24dbe23..f58806a1 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGoodsController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGoodsController.java @@ -1,25 +1,46 @@ package org.linlinjava.litemall.admin.web; +import io.swagger.models.auth.In; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.linlinjava.litemall.admin.annotation.LoginAdmin; -import org.linlinjava.litemall.db.domain.LitemallGoods; -import org.linlinjava.litemall.db.service.LitemallGoodsService; +import org.linlinjava.litemall.admin.dao.GoodsAllinone; +import org.linlinjava.litemall.admin.dao.Product; +import org.linlinjava.litemall.admin.util.CatVo; +import org.linlinjava.litemall.db.domain.*; +import org.linlinjava.litemall.db.service.*; import org.linlinjava.litemall.core.util.ResponseUtil; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.web.bind.annotation.*; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.*; @RestController @RequestMapping("/admin/goods") public class AdminGoodsController { private final Log logger = LogFactory.getLog(AdminGoodsController.class); + @Autowired + private PlatformTransactionManager txManager; + @Autowired private LitemallGoodsService goodsService; + @Autowired + private LitemallGoodsSpecificationService specificationService; + @Autowired + private LitemallGoodsAttributeService attributeService; + @Autowired + private LitemallProductService productService; + @Autowired + private LitemallCategoryService categoryService; + @Autowired + private LitemallBrandService brandService; @GetMapping("/list") public Object list(@LoginAdmin Integer adminId, @@ -40,36 +61,71 @@ public class AdminGoodsController { return ResponseUtil.ok(data); } - @PostMapping("/create") - public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallGoods goods){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - goodsService.add(goods); - return ResponseUtil.ok(goods); - } - - @GetMapping("/read") - public Object read(@LoginAdmin Integer adminId, Integer id){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - - if(id == null){ - return ResponseUtil.badArgument(); - } - - LitemallGoods goods = goodsService.findById(id); - return ResponseUtil.ok(goods); - } - + /* + * TODO + * 目前商品修改的逻辑是 + * 1. 更新litemall_goods表 + * 2. 逻辑删除litemall_goods_specification、litemall_goods_attribute、litemall_product + * 3. 添加litemall_goods_specification、litemall_goods_attribute、litemall_product + * + * 这里商品三个表的数据采用删除再跟新的策略是因为 + * 商品编辑页面,管理员可以添加删除商品规格、添加删除商品属性,因此这里仅仅更新表是不可能的, + * 因此这里只能删除所有旧的数据,然后添加新的数据 + */ @PostMapping("/update") - public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallGoods goods){ + public Object update(@LoginAdmin Integer adminId, @RequestBody GoodsAllinone goodsAllinone){ if(adminId == null){ return ResponseUtil.unlogin(); } - goodsService.updateById(goods); - return ResponseUtil.ok(goods); + + LitemallGoods goods = goodsAllinone.getGoods(); + LitemallGoodsAttribute[] attributes = goodsAllinone.getAttributes(); + LitemallGoodsSpecification[] specifications = goodsAllinone.getSpecifications(); + LitemallProduct[] products = goodsAllinone.getProducts(); + + // 开启事务管理 + DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + TransactionStatus status = txManager.getTransaction(def); + try { + + // 商品基本信息表litemall_goods + goodsService.updateById(goods); + + Integer gid = goods.getId(); + specificationService.deleteByGid(gid); + attributeService.deleteByGid(gid); + productService.deleteByGid(gid); + + // 商品规格表litemall_goods_specification + Map specIds = new HashMap<>(); + for(LitemallGoodsSpecification specification : specifications){ + specification.setGoodsId(goods.getId()); + specification.setAddTime(LocalDateTime.now()); + specificationService.add(specification); + specIds.put(specification.getValue(), specification.getId()); + } + + // 商品参数表litemall_goods_attribute + for(LitemallGoodsAttribute attribute : attributes){ + attribute.setGoodsId(goods.getId()); + attribute.setAddTime(LocalDateTime.now()); + attributeService.add(attribute); + } + + // 商品货品表litemall_product + for(LitemallProduct product : products){ + product.setGoodsId(goods.getId()); + product.setAddTime(LocalDateTime.now()); + productService.add(product); + } + } catch (Exception ex) { + txManager.rollback(status); + logger.error("系统内部错误", ex); + } + txManager.commit(status); + + return ResponseUtil.ok(); } @PostMapping("/delete") @@ -77,8 +133,162 @@ public class AdminGoodsController { if(adminId == null){ return ResponseUtil.unlogin(); } - goodsService.deleteById(goods.getId()); + + // 开启事务管理 + DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + TransactionStatus status = txManager.getTransaction(def); + try { + + Integer gid = goods.getId(); + goodsService.deleteById(gid); + specificationService.deleteByGid(gid); + attributeService.deleteByGid(gid); + productService.deleteByGid(gid); + } catch (Exception ex) { + txManager.rollback(status); + logger.error("系统内部错误", ex); + } + txManager.commit(status); return ResponseUtil.ok(); } + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody GoodsAllinone goodsAllinone){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + LitemallGoods goods = goodsAllinone.getGoods(); + LitemallGoodsAttribute[] attributes = goodsAllinone.getAttributes(); + LitemallGoodsSpecification[] specifications = goodsAllinone.getSpecifications(); + LitemallProduct[] products = goodsAllinone.getProducts(); + + String name = goods.getName(); + if(goodsService.checkExistByName(name)){ + return ResponseUtil.fail(403, "商品名已经存在"); + } + + // 开启事务管理 + DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + TransactionStatus status = txManager.getTransaction(def); + try { + + // 商品基本信息表litemall_goods + goods.setAddTime(LocalDateTime.now()); + goodsService.add(goods); + + // 商品规格表litemall_goods_specification + Map specIds = new HashMap<>(); + for(LitemallGoodsSpecification specification : specifications){ + specification.setGoodsId(goods.getId()); + specification.setAddTime(LocalDateTime.now()); + specificationService.add(specification); + specIds.put(specification.getValue(), specification.getId()); + } + + // 商品参数表litemall_goods_attribute + for(LitemallGoodsAttribute attribute : attributes){ + attribute.setGoodsId(goods.getId()); + attribute.setAddTime(LocalDateTime.now()); + attributeService.add(attribute); + } + + // 商品货品表litemall_product + for(LitemallProduct product : products){ + product.setGoodsId(goods.getId()); + product.setAddTime(LocalDateTime.now()); + productService.add(product); + } + } catch (Exception ex) { + txManager.rollback(status); + logger.error("系统内部错误", ex); + } + txManager.commit(status); + + return ResponseUtil.ok(); + } + + + + @GetMapping("/catAndBrand") + public Object list2(@LoginAdmin Integer adminId) { + if (adminId == null) { + return ResponseUtil.unlogin(); + } + + // http://element-cn.eleme.io/#/zh-CN/component/cascader + // 管理员设置“所属分类” + List l1CatList = categoryService.queryL1(); + List categoryList = new ArrayList<>(l1CatList.size()); + + for(LitemallCategory l1 : l1CatList){ + CatVo l1CatVo = new CatVo(); + l1CatVo.setValue(l1.getId()); + l1CatVo.setLabel(l1.getName()); + + List l2CatList = categoryService.queryByPid(l1.getId()); + List children = new ArrayList<>(l2CatList.size()); + for(LitemallCategory l2 : l2CatList) { + CatVo l2CatVo = new CatVo(); + l2CatVo.setValue(l2.getId()); + l2CatVo.setLabel(l2.getName()); + children.add(l2CatVo); + } + l1CatVo.setChildren(children); + + categoryList.add(l1CatVo); + } + + // http://element-cn.eleme.io/#/zh-CN/component/select + // 管理员设置“所属品牌商” + List list = brandService.all(); + List> brandList = new ArrayList<>(l1CatList.size()); + for(LitemallBrand brand : list){ + Map b = new HashMap<>(2); + b.put("value", brand.getId()); + b.put("label", brand.getName()); + brandList.add(b); + } + + Map data = new HashMap<>(); + data.put("categoryList" ,categoryList); + data.put("brandList", brandList); + return ResponseUtil.ok(data); + } + + @GetMapping("/detail") + public Object detail(@LoginAdmin Integer adminId, Integer id) { + if (adminId == null) { + return ResponseUtil.unlogin(); + } + + if (id == null) { + return ResponseUtil.badArgument(); + } + + LitemallGoods goods = goodsService.findById(id); + List products = productService.queryByGid(id); + List specifications = specificationService.queryByGid(id); + List attributes = attributeService.queryByGid(id); + + Integer categoryId = goods.getCategoryId(); + LitemallCategory category = categoryService.findById(categoryId); + Integer[] categoryIds = new Integer[]{}; + if (category != null) { + Integer parentCategoryId = category.getPid(); + categoryIds = new Integer[] {parentCategoryId, categoryId}; + } + + Map data = new HashMap<>(); + data.put("goods" ,goods); + data.put("specifications", specifications); + data.put("products", products); + data.put("attributes", attributes); + data.put("categoryIds", categoryIds); + + return ResponseUtil.ok(data); + } + } diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGoodsSpecificationController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGoodsSpecificationController.java deleted file mode 100644 index 4de24f5b..00000000 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminGoodsSpecificationController.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.linlinjava.litemall.admin.web; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.linlinjava.litemall.admin.annotation.LoginAdmin; -import org.linlinjava.litemall.db.domain.LitemallGoodsSpecification; -import org.linlinjava.litemall.db.service.LitemallGoodsSpecificationService; -import org.linlinjava.litemall.core.util.ResponseUtil; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@RestController -@RequestMapping("/admin/goods-specification") -public class AdminGoodsSpecificationController { - private final Log logger = LogFactory.getLog(AdminGoodsSpecificationController.class); - - @Autowired - private LitemallGoodsSpecificationService goodsSpecificationService; - - @GetMapping("/list") - public Object list(@LoginAdmin Integer adminId, - Integer goodsId, - @RequestParam(value = "page", defaultValue = "1") Integer page, - @RequestParam(value = "limit", defaultValue = "10") Integer limit, - String sort, String order){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - - List goodsSpecificationList = goodsSpecificationService.querySelective(goodsId, page, limit, sort, order); - int total = goodsSpecificationService.countSelective(goodsId, page, limit, sort, order); - Map data = new HashMap<>(); - data.put("total", total); - data.put("items", goodsSpecificationList); - - return ResponseUtil.ok(data); - } - - @PostMapping("/create") - public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsSpecification goodsSpecification){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - goodsSpecificationService.add(goodsSpecification); - return ResponseUtil.ok(goodsSpecification); - } - - @GetMapping("/read") - public Object read(@LoginAdmin Integer adminId, Integer id){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - - if(id == null){ - return ResponseUtil.badArgument(); - } - - LitemallGoodsSpecification goodsSpecification = goodsSpecificationService.findById(id); - return ResponseUtil.ok(goodsSpecification); - } - - @PostMapping("/update") - public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsSpecification goodsSpecification){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - goodsSpecificationService.updateById(goodsSpecification); - return ResponseUtil.ok(goodsSpecification); - } - - @PostMapping("/delete") - public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsSpecification goodsSpecification){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - goodsSpecificationService.deleteById(goodsSpecification.getId()); - return ResponseUtil.ok(); - } - - @GetMapping("/volist") - public Object volist(@LoginAdmin Integer adminId, Integer id){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - - if(id == null){ - return ResponseUtil.badArgument(); - } - - Object goodsSpecificationVoList = goodsSpecificationService.getSpecificationVoList(id); - return ResponseUtil.ok(goodsSpecificationVoList); - } - -} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminHistoryController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminHistoryController.java index 93580a75..03521d57 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminHistoryController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminHistoryController.java @@ -43,9 +43,9 @@ public class AdminHistoryController { @PostMapping("/create") public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallSearchHistory history){ if(adminId == null){ - return ResponseUtil.fail401(); + return ResponseUtil.unlogin(); } - return ResponseUtil.fail501(); + return ResponseUtil.unsupport(); } @GetMapping("/read") diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminIssueController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminIssueController.java index 0d889bda..7ce96e7e 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminIssueController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminIssueController.java @@ -9,6 +9,7 @@ import org.linlinjava.litemall.core.util.ResponseUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import java.time.LocalDateTime; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -41,12 +42,13 @@ public class AdminIssueController { } @PostMapping("/create") - public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallIssue brand){ + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallIssue issue){ if(adminId == null){ return ResponseUtil.unlogin(); } - issueService.add(brand); - return ResponseUtil.ok(brand); + issue.setAddTime(LocalDateTime.now()); + issueService.add(issue); + return ResponseUtil.ok(issue); } @GetMapping("/read") @@ -59,25 +61,25 @@ public class AdminIssueController { return ResponseUtil.badArgument(); } - LitemallIssue brand = issueService.findById(id); - return ResponseUtil.ok(brand); + LitemallIssue issue = issueService.findById(id); + return ResponseUtil.ok(issue); } @PostMapping("/update") - public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallIssue brand){ + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallIssue issue){ if(adminId == null){ return ResponseUtil.unlogin(); } - issueService.updateById(brand); - return ResponseUtil.ok(brand); + issueService.updateById(issue); + return ResponseUtil.ok(issue); } @PostMapping("/delete") - public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallIssue brand){ + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallIssue issue){ if(adminId == null){ return ResponseUtil.unlogin(); } - issueService.deleteById(brand.getId()); + issueService.deleteById(issue.getId()); return ResponseUtil.ok(); } diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminKeywordController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminKeywordController.java index c1d417a6..ee9d051b 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminKeywordController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminKeywordController.java @@ -9,6 +9,7 @@ import org.linlinjava.litemall.core.util.ResponseUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import java.time.LocalDateTime; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -45,6 +46,7 @@ public class AdminKeywordController { if(adminId == null){ return ResponseUtil.unlogin(); } + keywords.setAddTime(LocalDateTime.now()); keywordService.add(keywords); return ResponseUtil.ok(keywords); } diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminOrderController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminOrderController.java index dd3475ee..f58c167b 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminOrderController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminOrderController.java @@ -21,6 +21,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.web.bind.annotation.*; +import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.HashMap; import java.util.List; @@ -48,7 +49,7 @@ public class AdminOrderController { @RequestParam(value = "limit", defaultValue = "10") Integer limit, String sort, String order){ if(adminId == null){ - return ResponseUtil.fail401(); + return ResponseUtil.unlogin(); } List orderList = orderService.querySelective(userId, orderSn, page, limit, sort, order); int total = orderService.countSelective(userId, orderSn, page, limit, sort, order); @@ -60,73 +61,6 @@ public class AdminOrderController { return ResponseUtil.ok(data); } - /* - * 目前的逻辑不支持管理员创建 - */ - @PostMapping("/create") - public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallOrder order){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - return ResponseUtil.unsupport(); - } - - @GetMapping("/read") - public Object read(@LoginAdmin Integer adminId, Integer id){ - if(adminId == null){ - return ResponseUtil.fail401(); - } - - LitemallOrder order = orderService.findById(id); - return ResponseUtil.ok(order); - } - - /* - * 目前仅仅支持管理员设置发货相关的信息 - */ - @PostMapping("/update") - public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallOrder order){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - - Integer orderId = order.getId(); - if(orderId == null){ - return ResponseUtil.badArgument(); - } - - LitemallOrder litemallOrder = orderService.findById(orderId); - if(litemallOrder == null){ - return ResponseUtil.badArgumentValue(); - } - - if(OrderUtil.isPayStatus(litemallOrder) || OrderUtil.isShipStatus(litemallOrder)){ - LitemallOrder newOrder = new LitemallOrder(); - newOrder.setId(orderId); - newOrder.setShipChannel(order.getShipChannel()); - newOrder.setShipSn(order.getOrderSn()); - newOrder.setShipStartTime(order.getShipStartTime()); - newOrder.setShipEndTime(order.getShipEndTime()); - newOrder.setOrderStatus(OrderUtil.STATUS_SHIP); - orderService.update(newOrder); - } - else { - return ResponseUtil.badArgumentValue(); - } - - litemallOrder = orderService.findById(orderId); - return ResponseUtil.ok(litemallOrder); - } - - @PostMapping("/delete") - public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallOrder order){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - return ResponseUtil.unsupport(); - } - - /** * 订单退款确认 * 1. 检测当前订单是否能够退款确认 @@ -139,12 +73,13 @@ public class AdminOrderController { * 成功则 { errno: 0, errmsg: '成功' } * 失败则 { errno: XXX, errmsg: XXX } */ - @PostMapping("refundConfirm") - public Object refundConfirm(@LoginAdmin Integer adminId, @RequestBody String body) { + @PostMapping("refund") + public Object refund(@LoginAdmin Integer adminId, @RequestBody String body) { if (adminId == null) { return ResponseUtil.unlogin(); } Integer orderId = JacksonUtil.parseInteger(body, "orderId"); + Integer refundMoney = JacksonUtil.parseInteger(body, "refundMoney"); if (orderId == null) { return ResponseUtil.badArgument(); } @@ -153,13 +88,14 @@ public class AdminOrderController { if (order == null) { return ResponseUtil.badArgument(); } - if (!order.getUserId().equals(adminId)) { + + if(order.getActualPrice().compareTo(new BigDecimal(refundMoney)) != 0){ return ResponseUtil.badArgumentValue(); } - OrderHandleOption handleOption = OrderUtil.build(order); - if (!handleOption.isRefund()) { - return ResponseUtil.fail(403, "订单不能取消"); + // 如果订单不是退款状态,则不能退款 + if (!order.getOrderStatus().equals(OrderUtil.STATUS_REFUND)) { + return ResponseUtil.fail(403, "订单不能确认收货"); } // 开启事务管理 @@ -176,8 +112,8 @@ public class AdminOrderController { for (LitemallOrderGoods orderGoods : orderGoodsList) { Integer productId = orderGoods.getProductId(); LitemallProduct product = productService.findById(productId); - Integer number = product.getGoodsNumber() + orderGoods.getNumber(); - product.setGoodsNumber(number); + Integer number = product.getNumber() + orderGoods.getNumber(); + product.setNumber(number); productService.updateById(product); } } catch (Exception ex) { @@ -217,9 +153,6 @@ public class AdminOrderController { if (order == null) { return ResponseUtil.badArgument(); } - if (!order.getUserId().equals(adminId)) { - return ResponseUtil.badArgumentValue(); - } // 如果订单不是已付款状态,则不能发货 if (!order.getOrderStatus().equals(OrderUtil.STATUS_PAY)) { @@ -229,7 +162,7 @@ public class AdminOrderController { order.setOrderStatus(OrderUtil.STATUS_SHIP); order.setShipSn(shipSn); order.setShipChannel(shipChannel); - order.setShipStartTime(LocalDateTime.now()); + order.setShipTime(LocalDateTime.now()); orderService.update(order); return ResponseUtil.ok(); @@ -274,8 +207,8 @@ public class AdminOrderController { for (LitemallOrderGoods orderGoods : orderGoodsList) { Integer productId = orderGoods.getProductId(); LitemallProduct product = productService.findById(productId); - Integer number = product.getGoodsNumber() + orderGoods.getNumber(); - product.setGoodsNumber(number); + Integer number = product.getNumber() + orderGoods.getNumber(); + product.setNumber(number); productService.updateById(product); } } catch (Exception ex) { @@ -298,7 +231,7 @@ public class AdminOrderController { * 早点清理未付款情况,这里八天再确认是可以的。 * * TODO - * 目前自动确认是基于管理后台管理员所设置的商品快递到达时间,见orderService.queryUnconfirm。 + * 目前自动确认是基于管理后台管理员所设置的商品快递时间,见orderService.queryUnconfirm。 * 那么在实际业务上有可能存在商品寄出以后商品因为一些原因快递最终没有到达, * 也就是商品快递失败而shipEndTime一直是空的情况,因此这里业务可能需要扩展,以防止订单一直 * 处于发货状态。 @@ -309,9 +242,9 @@ public class AdminOrderController { List orderList = orderService.queryUnconfirm(); for(LitemallOrder order : orderList){ - LocalDateTime shipEnd = order.getShipEndTime(); + LocalDateTime ship = order.getShipTime(); LocalDateTime now = LocalDateTime.now(); - LocalDateTime expired = shipEnd.plusDays(7); + LocalDateTime expired = ship.plusDays(7); if(expired.isAfter(now)){ continue; } diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminProductController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminProductController.java deleted file mode 100644 index cceaedfa..00000000 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminProductController.java +++ /dev/null @@ -1,125 +0,0 @@ -package org.linlinjava.litemall.admin.web; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.linlinjava.litemall.admin.annotation.LoginAdmin; -import org.linlinjava.litemall.db.domain.LitemallGoods; -import org.linlinjava.litemall.db.domain.LitemallProduct; -import org.linlinjava.litemall.db.service.LitemallGoodsService; -import org.linlinjava.litemall.db.service.LitemallGoodsSpecificationService; -import org.linlinjava.litemall.db.service.LitemallProductService; -import org.linlinjava.litemall.core.util.ResponseUtil; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -import java.math.BigDecimal; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@RestController -@RequestMapping("/admin/product") -public class AdminProductController { - private final Log logger = LogFactory.getLog(AdminProductController.class); - - @Autowired - private LitemallProductService productService; - @Autowired - private LitemallGoodsService goodsService; - @Autowired - private LitemallGoodsSpecificationService goodsSpecificationService; - - @GetMapping("/list") - public Object list(@LoginAdmin Integer adminId, - Integer goodsId, - @RequestParam(value = "page", defaultValue = "1") Integer page, - @RequestParam(value = "limit", defaultValue = "10") Integer limit, - String sort, String order){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - - List productList = productService.querySelective(goodsId, page, limit, sort, order); - int total = productService.countSelective(goodsId, page, limit, sort, order); - Map data = new HashMap<>(); - data.put("total", total); - data.put("items", productList); - - return ResponseUtil.ok(data); - } - - /** - * - * @param adminId - * @param litemallProduct - * @return - */ - @PostMapping("/create") - public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallProduct litemallProduct){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - - Integer goodsId = litemallProduct.getGoodsId(); - if(goodsId == null){ - return ResponseUtil.badArgument(); - } - - LitemallGoods goods = goodsService.findById(goodsId); - if(goods == null){ - return ResponseUtil.badArgumentValue(); - } - - List productList = productService.queryByGid(goodsId); - if(productList.size() != 0){ - return ResponseUtil.badArgumentValue(); - } - - Integer[] goodsSpecificationIds = goodsSpecificationService.queryIdsByGid(goodsId); - if(goodsSpecificationIds.length == 0) { - return ResponseUtil.serious(); - } - - LitemallProduct product = new LitemallProduct(); - product.setGoodsId(goodsId); - product.setGoodsNumber(0); - product.setRetailPrice(new BigDecimal(0.00)); - product.setGoodsSpecificationIds(goodsSpecificationIds); - productService.add(product); - - return ResponseUtil.ok(); - } - - @GetMapping("/read") - public Object read(@LoginAdmin Integer adminId, Integer id){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - - if(id == null){ - return ResponseUtil.badArgument(); - } - - LitemallProduct product = productService.findById(id); - return ResponseUtil.ok(product); - } - - @PostMapping("/update") - public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallProduct product){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - productService.updateById(product); - return ResponseUtil.ok(product); - } - - @PostMapping("/delete") - public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallProduct product){ - if(adminId == null){ - return ResponseUtil.unlogin(); - } - productService.deleteById(product.getId()); - return ResponseUtil.ok(); - } - -} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminStatController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminStatController.java new file mode 100644 index 00000000..72a89c2c --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminStatController.java @@ -0,0 +1,72 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.admin.util.StatVo; +import org.linlinjava.litemall.core.util.ResponseUtil; +import org.linlinjava.litemall.db.dao.StatMapper; +import org.linlinjava.litemall.db.service.LitemallOrderService; +import org.linlinjava.litemall.db.service.StatService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/stat") +public class AdminStatController { + private final Log logger = LogFactory.getLog(AdminStatController.class); + + @Autowired + private StatService statService; + + @GetMapping("/user") + public Object statUser(@LoginAdmin Integer adminId){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List rows = statService.statUser(); + String[] columns = new String[]{"day", "users"}; + StatVo statVo = new StatVo(); + statVo.setColumns(columns); + statVo.setRows(rows); + + return ResponseUtil.ok(statVo); + } + + @GetMapping("/order") + public Object statOrder(@LoginAdmin Integer adminId){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List rows = statService.statOrder(); + String[] columns = new String[]{"day", "orders", "customers", "amount", "pcr"}; + StatVo statVo = new StatVo(); + statVo.setColumns(columns); + statVo.setRows(rows); + + return ResponseUtil.ok(statVo); + } + + @GetMapping("/goods") + public Object statGoods(@LoginAdmin Integer adminId){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List rows = statService.statGoods(); + String[] columns = new String[]{"day", "orders", "products", "amount"}; + StatVo statVo = new StatVo(); + statVo.setColumns(columns); + statVo.setRows(rows); + + + return ResponseUtil.ok(statVo); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminTopicController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminTopicController.java index 455b2ad4..fb79166d 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminTopicController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminTopicController.java @@ -9,6 +9,7 @@ import org.linlinjava.litemall.core.util.ResponseUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import java.time.LocalDateTime; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -45,6 +46,7 @@ public class AdminTopicController { if(adminId == null){ return ResponseUtil.unlogin(); } + topic.setAddTime(LocalDateTime.now()); topicService.add(topic); return ResponseUtil.ok(topic); } diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminUserController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminUserController.java index a172dbd9..a67577ad 100644 --- a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminUserController.java +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminUserController.java @@ -10,6 +10,7 @@ import org.linlinjava.litemall.core.util.ResponseUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import java.time.LocalDateTime; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -29,7 +30,7 @@ public class AdminUserController { @RequestParam(value = "limit", defaultValue = "10") Integer limit, String sort, String order){ if(adminId == null){ - return ResponseUtil.fail401(); + return ResponseUtil.unlogin(); } List userList = userService.querySelective(username, mobile, page, limit, sort, order); int total = userService.countSeletive(username, mobile, page, limit, sort, order); @@ -43,7 +44,7 @@ public class AdminUserController { @GetMapping("/username") public Object username(String username){ if(StringUtil.isEmpty(username)){ - return ResponseUtil.fail402(); + return ResponseUtil.badArgument(); } int total = userService.countSeletive(username, null, null, null, null, null); @@ -57,7 +58,7 @@ public class AdminUserController { @PostMapping("/create") public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallUser user){ logger.debug(user); - + user.setAddTime(LocalDateTime.now()); userService.add(user); return ResponseUtil.ok(user); } diff --git a/litemall-admin-api/src/main/resources/application-dep.properties b/litemall-admin-api/src/main/resources/application-dep.properties index f9a6f906..47db7700 100644 --- a/litemall-admin-api/src/main/resources/application-dep.properties +++ b/litemall-admin-api/src/main/resources/application-dep.properties @@ -7,9 +7,9 @@ spring.datasource.druid.url=jdbc:mysql://localhost:3306/litemall?useUnicode=true spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver spring.datasource.druid.username=litemall spring.datasource.druid.password=litemall123456 -spring.datasource.druid.initial-size=50 -spring.datasource.druid.max-active=100 -spring.datasource.druid.min-idle=20 +spring.datasource.druid.initial-size=10 +spring.datasource.druid.max-active=50 +spring.datasource.druid.min-idle=10 spring.datasource.druid.max-wait=60000 spring.datasource.druid.pool-prepared-statements=true spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20 diff --git a/litemall-admin-api/src/main/resources/application-dev.properties b/litemall-admin-api/src/main/resources/application-dev.properties index f9a6f906..47db7700 100644 --- a/litemall-admin-api/src/main/resources/application-dev.properties +++ b/litemall-admin-api/src/main/resources/application-dev.properties @@ -7,9 +7,9 @@ spring.datasource.druid.url=jdbc:mysql://localhost:3306/litemall?useUnicode=true spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver spring.datasource.druid.username=litemall spring.datasource.druid.password=litemall123456 -spring.datasource.druid.initial-size=50 -spring.datasource.druid.max-active=100 -spring.datasource.druid.min-idle=20 +spring.datasource.druid.initial-size=10 +spring.datasource.druid.max-active=50 +spring.datasource.druid.min-idle=10 spring.datasource.druid.max-wait=60000 spring.datasource.druid.pool-prepared-statements=true spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20 diff --git a/litemall-admin-api/src/main/resources/application-prod.properties b/litemall-admin-api/src/main/resources/application-prod.properties index 0a09fe15..dd9dcb59 100644 --- a/litemall-admin-api/src/main/resources/application-prod.properties +++ b/litemall-admin-api/src/main/resources/application-prod.properties @@ -7,9 +7,9 @@ spring.datasource.druid.url=jdbc:mysql://localhost:3306/litemall?useUnicode=true spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver spring.datasource.druid.username=litemall spring.datasource.druid.password=litemall123456 -spring.datasource.druid.initial-size=50 -spring.datasource.druid.max-active=100 -spring.datasource.druid.min-idle=20 +spring.datasource.druid.initial-size=10 +spring.datasource.druid.max-active=50 +spring.datasource.druid.min-idle=10 spring.datasource.druid.max-wait=60000 spring.datasource.druid.pool-prepared-statements=true spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20 diff --git a/litemall-admin/package.json b/litemall-admin/package.json index 049ffeb3..1ad6a5fb 100644 --- a/litemall-admin/package.json +++ b/litemall-admin/package.json @@ -13,9 +13,10 @@ "test": "npm run lint" }, "dependencies": { + "@tinymce/tinymce-vue": "^1.0.8", "axios": "0.17.1", "clipboard": "1.7.1", - "echarts": "3.8.5", + "echarts": "^4.1.0", "element-ui": "2.0.8", "file-saver": "1.3.3", "font-awesome": "4.7.0", @@ -24,13 +25,13 @@ "normalize.css": "7.0.0", "nprogress": "0.2.0", "screenfull": "3.3.2", + "v-charts": "^1.16.19", "vue": "2.5.10", "vue-count-to": "1.0.13", "vue-router": "3.0.1", "vue-splitpane": "1.0.2", "vuex": "3.0.1", - "xlsx": "^0.11.16", - "@tinymce/tinymce-vue": "1.0.8" + "xlsx": "^0.11.16" }, "devDependencies": { "autoprefixer": "7.2.3", diff --git a/litemall-admin/src/api/cart.js b/litemall-admin/src/api/cart.js deleted file mode 100644 index 7825d527..00000000 --- a/litemall-admin/src/api/cart.js +++ /dev/null @@ -1,41 +0,0 @@ -import request from '@/utils/request' - -export function listCart(query) { - return request({ - url: '/cart/list', - method: 'get', - params: query - }) -} - -export function createCart(data) { - return request({ - url: '/cart/create', - method: 'post', - data - }) -} - -export function readCart(data) { - return request({ - url: '/cart/read', - method: 'get', - data - }) -} - -export function updateCart(data) { - return request({ - url: '/cart/update', - method: 'post', - data - }) -} - -export function deleteCart(data) { - return request({ - url: '/cart/delete', - method: 'post', - data - }) -} diff --git a/litemall-admin/src/api/goods-attribute.js b/litemall-admin/src/api/goods-attribute.js deleted file mode 100644 index e8500604..00000000 --- a/litemall-admin/src/api/goods-attribute.js +++ /dev/null @@ -1,41 +0,0 @@ -import request from '@/utils/request' - -export function listGoodsAttribute(query) { - return request({ - url: '/goods-attribute/list', - method: 'get', - params: query - }) -} - -export function createGoodsAttribute(data) { - return request({ - url: '/goods-attribute/create', - method: 'post', - data - }) -} - -export function readGoodsAttribute(data) { - return request({ - url: '/goods-attribute/read', - method: 'get', - data - }) -} - -export function updateGoodsAttribute(data) { - return request({ - url: '/goods-attribute/update', - method: 'post', - data - }) -} - -export function deleteGoodsAttribute(data) { - return request({ - url: '/goods-attribute/delete', - method: 'post', - data - }) -} diff --git a/litemall-admin/src/api/goods-specification.js b/litemall-admin/src/api/goods-specification.js deleted file mode 100644 index 69b6eb82..00000000 --- a/litemall-admin/src/api/goods-specification.js +++ /dev/null @@ -1,49 +0,0 @@ -import request from '@/utils/request' - -export function listGoodsSpecification(query) { - return request({ - url: '/goods-specification/list', - method: 'get', - params: query - }) -} - -export function createGoodsSpecification(data) { - return request({ - url: '/goods-specification/create', - method: 'post', - data - }) -} - -export function readGoodsSpecification(data) { - return request({ - url: '/goods-specification/read', - method: 'get', - data - }) -} - -export function updateGoodsSpecification(data) { - return request({ - url: '/goods-specification/update', - method: 'post', - data - }) -} - -export function deleteGoodsSpecification(data) { - return request({ - url: '/goods-specification/delete', - method: 'post', - data - }) -} - -export function listGoodsSpecificationVo(query) { - return request({ - url: '/goods-specification/volist', - method: 'get', - params: query - }) -} diff --git a/litemall-admin/src/api/goods.js b/litemall-admin/src/api/goods.js index fc45aa04..0ef2834c 100644 --- a/litemall-admin/src/api/goods.js +++ b/litemall-admin/src/api/goods.js @@ -8,30 +8,6 @@ export function listGoods(query) { }) } -export function createGoods(data) { - return request({ - url: '/goods/create', - method: 'post', - data - }) -} - -export function readGoods(data) { - return request({ - url: '/goods/read', - method: 'get', - data - }) -} - -export function updateGoods(data) { - return request({ - url: '/goods/update', - method: 'post', - data - }) -} - export function deleteGoods(data) { return request({ url: '/goods/delete', @@ -39,3 +15,34 @@ export function deleteGoods(data) { data }) } + +export function publishGoods(data) { + return request({ + url: '/goods/create', + method: 'post', + data + }) +} + +export function detailGoods(id) { + return request({ + url: '/goods/detail', + method: 'get', + params: { id } + }) +} + +export function editGoods(data) { + return request({ + url: '/goods/update', + method: 'post', + data + }) +} + +export function listCatAndBrand() { + return request({ + url: '/goods/catAndBrand', + method: 'get' + }) +} diff --git a/litemall-admin/src/api/order.js b/litemall-admin/src/api/order.js index de6e814e..58d76cb2 100644 --- a/litemall-admin/src/api/order.js +++ b/litemall-admin/src/api/order.js @@ -8,33 +8,17 @@ export function listOrder(query) { }) } -export function createOrder(data) { +export function shipOrder(data) { return request({ - url: '/order/create', + url: '/order/ship', method: 'post', data }) } -export function readOrder(data) { +export function refundOrder(data) { return request({ - url: '/order/read', - method: 'get', - data - }) -} - -export function updateOrder(data) { - return request({ - url: '/order/update', - method: 'post', - data - }) -} - -export function deleteOrder(data) { - return request({ - url: '/order/delete', + url: '/order/refund', method: 'post', data }) diff --git a/litemall-admin/src/api/product.js b/litemall-admin/src/api/product.js deleted file mode 100644 index 5e0bf765..00000000 --- a/litemall-admin/src/api/product.js +++ /dev/null @@ -1,41 +0,0 @@ -import request from '@/utils/request' - -export function listProduct(query) { - return request({ - url: '/product/list', - method: 'get', - params: query - }) -} - -export function createProduct(data) { - return request({ - url: '/product/create', - method: 'post', - data - }) -} - -export function readProduct(data) { - return request({ - url: '/product/read', - method: 'get', - data - }) -} - -export function updateProduct(data) { - return request({ - url: '/product/update', - method: 'post', - data - }) -} - -export function deleteProduct(data) { - return request({ - url: '/product/delete', - method: 'post', - data - }) -} diff --git a/litemall-admin/src/api/stat.js b/litemall-admin/src/api/stat.js new file mode 100644 index 00000000..0aa70587 --- /dev/null +++ b/litemall-admin/src/api/stat.js @@ -0,0 +1,25 @@ +import request from '@/utils/request' + +export function statUser(query) { + return request({ + url: '/stat/user', + method: 'get', + params: query + }) +} + +export function statOrder(query) { + return request({ + url: '/stat/order', + method: 'get', + params: query + }) +} + +export function statGoods(query) { + return request({ + url: '/stat/goods', + method: 'get', + params: query + }) +} diff --git a/litemall-admin/src/api/storage.js b/litemall-admin/src/api/storage.js index 467f7c04..07547fc8 100644 --- a/litemall-admin/src/api/storage.js +++ b/litemall-admin/src/api/storage.js @@ -60,3 +60,6 @@ export function deleteStorage(data) { data }) } + +const uploadPath = process.env.OS_API + '/storage/create' +export { uploadPath } diff --git a/litemall-admin/src/directive/sticky.js b/litemall-admin/src/directive/sticky.js deleted file mode 100644 index bc234660..00000000 --- a/litemall-admin/src/directive/sticky.js +++ /dev/null @@ -1,91 +0,0 @@ -const vueSticky = {} -let listenAction -vueSticky.install = Vue => { - Vue.directive('sticky', { - inserted(el, binding) { - const params = binding.value || {} - const stickyTop = params.stickyTop || 0 - const zIndex = params.zIndex || 1000 - const elStyle = el.style - - elStyle.position = '-webkit-sticky' - elStyle.position = 'sticky' - // if the browser support css sticky(Currently Safari, Firefox and Chrome Canary) - // if (~elStyle.position.indexOf('sticky')) { - // elStyle.top = `${stickyTop}px`; - // elStyle.zIndex = zIndex; - // return - // } - const elHeight = el.getBoundingClientRect().height - const elWidth = el.getBoundingClientRect().width - elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}` - - const parentElm = el.parentNode || document.documentElement - const placeholder = document.createElement('div') - placeholder.style.display = 'none' - placeholder.style.width = `${elWidth}px` - placeholder.style.height = `${elHeight}px` - parentElm.insertBefore(placeholder, el) - - let active = false - - const getScroll = (target, top) => { - const prop = top ? 'pageYOffset' : 'pageXOffset' - const method = top ? 'scrollTop' : 'scrollLeft' - let ret = target[prop] - if (typeof ret !== 'number') { - ret = window.document.documentElement[method] - } - return ret - } - - const sticky = () => { - if (active) { - return - } - if (!elStyle.height) { - elStyle.height = `${el.offsetHeight}px` - } - - elStyle.position = 'fixed' - elStyle.width = `${elWidth}px` - placeholder.style.display = 'inline-block' - active = true - } - - const reset = () => { - if (!active) { - return - } - - elStyle.position = '' - placeholder.style.display = 'none' - active = false - } - - const check = () => { - const scrollTop = getScroll(window, true) - const offsetTop = el.getBoundingClientRect().top - if (offsetTop < stickyTop) { - sticky() - } else { - if (scrollTop < elHeight + stickyTop) { - reset() - } - } - } - listenAction = () => { - check() - } - - window.addEventListener('scroll', listenAction) - }, - - unbind() { - window.removeEventListener('scroll', listenAction) - } - }) -} - -export default vueSticky - diff --git a/litemall-admin/src/directive/waves/index.js b/litemall-admin/src/directive/waves/index.js deleted file mode 100644 index 65f9b308..00000000 --- a/litemall-admin/src/directive/waves/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import waves from './waves' - -const install = function(Vue) { - Vue.directive('waves', waves) -} - -if (window.Vue) { - window.waves = waves - Vue.use(install); // eslint-disable-line -} - -waves.install = install -export default waves diff --git a/litemall-admin/src/directive/waves/waves.css b/litemall-admin/src/directive/waves/waves.css deleted file mode 100644 index af7a7efd..00000000 --- a/litemall-admin/src/directive/waves/waves.css +++ /dev/null @@ -1,26 +0,0 @@ -.waves-ripple { - position: absolute; - border-radius: 100%; - background-color: rgba(0, 0, 0, 0.15); - background-clip: padding-box; - pointer-events: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - -webkit-transform: scale(0); - -ms-transform: scale(0); - transform: scale(0); - opacity: 1; -} - -.waves-ripple.z-active { - opacity: 0; - -webkit-transform: scale(2); - -ms-transform: scale(2); - transform: scale(2); - -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; - transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; - transition: opacity 1.2s ease-out, transform 0.6s ease-out; - transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out; -} \ No newline at end of file diff --git a/litemall-admin/src/directive/waves/waves.js b/litemall-admin/src/directive/waves/waves.js deleted file mode 100644 index ac1d8611..00000000 --- a/litemall-admin/src/directive/waves/waves.js +++ /dev/null @@ -1,42 +0,0 @@ -import './waves.css' - -export default{ - bind(el, binding) { - el.addEventListener('click', e => { - const customOpts = Object.assign({}, binding.value) - const opts = Object.assign({ - ele: el, // 波纹作用元素 - type: 'hit', // hit点击位置扩散center中心点扩展 - color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 - }, customOpts) - const target = opts.ele - if (target) { - target.style.position = 'relative' - target.style.overflow = 'hidden' - const rect = target.getBoundingClientRect() - let ripple = target.querySelector('.waves-ripple') - if (!ripple) { - ripple = document.createElement('span') - ripple.className = 'waves-ripple' - ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px' - target.appendChild(ripple) - } else { - ripple.className = 'waves-ripple' - } - switch (opts.type) { - case 'center': - ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px' - ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px' - break - default: - ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px' - ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px' - } - ripple.style.backgroundColor = opts.color - ripple.className = 'waves-ripple z-active' - return false - } - }, false) - } -} - diff --git a/litemall-admin/src/permission.js b/litemall-admin/src/permission.js index 73ec8946..b6c6bf9e 100644 --- a/litemall-admin/src/permission.js +++ b/litemall-admin/src/permission.js @@ -14,7 +14,7 @@ function hasPermission(roles, permissionRoles) { return roles.some(role => permissionRoles.indexOf(role) >= 0) } -const whiteList = ['/login', '/authredirect']// no redirect whitelist +const whiteList = ['/login']// no redirect whitelist router.beforeEach((to, from, next) => { NProgress.start() // start progress bar @@ -33,7 +33,7 @@ router.beforeEach((to, from, next) => { }) }).catch(() => { store.dispatch('FedLogOut').then(() => { - Message.error('Verification failed, please login again') + Message.error('验证失败,请输入正确的用户名和密码') next({ path: '/login' }) }) }) diff --git a/litemall-admin/src/router/index.js b/litemall-admin/src/router/index.js index 3f96052b..af5fd599 100644 --- a/litemall-admin/src/router/index.js +++ b/litemall-admin/src/router/index.js @@ -29,7 +29,6 @@ import Layout from '../views/layout/Layout' **/ export const constantRouterMap = [ { path: '/login', component: _import('login/index'), hidden: true }, - { path: '/authredirect', component: _import('login/authredirect'), hidden: true }, { path: '/404', component: _import('error/404'), hidden: true }, { path: '/401', component: _import('error/401'), hidden: true }, { @@ -66,8 +65,7 @@ export const asyncRouterMap = [ { path: 'address', component: _import('user/address'), name: 'address', meta: { title: '收货地址', noCache: true }}, { path: 'collect', component: _import('user/collect'), name: 'collect', meta: { title: '会员收藏', noCache: true }}, { path: 'footprint', component: _import('user/footprint'), name: 'footprint', meta: { title: '会员足迹', noCache: true }}, - { path: 'history', component: _import('user/history'), name: 'history', meta: { title: '搜索历史', noCache: true }}, - { path: 'cart', component: _import('user/cart'), name: 'cart', meta: { title: '购物车', noCache: true }} + { path: 'history', component: _import('user/history'), name: 'history', meta: { title: '搜索历史', noCache: true }} ] }, @@ -100,13 +98,13 @@ export const asyncRouterMap = [ icon: 'chart' }, children: [ - { path: 'goods', component: _import('goods/goods'), name: 'goods', meta: { title: '商品管理', noCache: true }}, - { path: 'attribute', component: _import('goods/attribute'), name: 'attribute', meta: { title: '商品参数', noCache: true }}, - { path: 'specification', component: _import('goods/specification'), name: 'specification', meta: { title: '商品规格', noCache: true }}, - { path: 'product', component: _import('goods/product'), name: 'product', meta: { title: '货品管理', noCache: true }}, - { path: 'comment', component: _import('goods/comment'), name: 'comment', meta: { title: '用户评论', noCache: true }} + { path: 'list', component: _import('goods/list'), name: 'goodsList', meta: { title: '商品列表', noCache: true }}, + { path: 'create', component: _import('goods/create'), name: 'goodsCreate', meta: { title: '商品上架', noCache: true }}, + { path: 'edit', component: _import('goods/edit'), name: 'goodsEdit', meta: { title: '商品编辑', noCache: true }, hidden: true }, + { path: 'comment', component: _import('goods/comment'), name: 'goodsComment', meta: { title: '商品评论', noCache: true }} ] }, + { path: '/promotion', component: Layout, @@ -121,6 +119,7 @@ export const asyncRouterMap = [ { path: 'topic', component: _import('promotion/topic'), name: 'topic', meta: { title: '专题管理', noCache: true }} ] }, + { path: '/sys', component: Layout, @@ -136,5 +135,21 @@ export const asyncRouterMap = [ ] }, + { + path: '/stat', + component: Layout, + redirect: 'noredirect', + name: 'statManage', + meta: { + title: '统计', + icon: 'chart' + }, + children: [ + { path: 'user', component: _import('stat/user'), name: 'statUser', meta: { title: '用户统计', noCache: true }}, + { path: 'order', component: _import('stat/order'), name: 'statOrder', meta: { title: '订单统计', noCache: true }}, + { path: 'goods', component: _import('stat/goods'), name: 'statGoods', meta: { title: '商品统计', noCache: true }} + ] + }, + { path: '*', redirect: '/404', hidden: true } ] diff --git a/litemall-admin/src/utils/request.js b/litemall-admin/src/utils/request.js index 750123de..5da715bf 100644 --- a/litemall-admin/src/utils/request.js +++ b/litemall-admin/src/utils/request.js @@ -26,22 +26,32 @@ service.interceptors.request.use(config => { service.interceptors.response.use( response => { const res = response.data - if (res.errno === 502) { - MessageBox.alert('系统内部错误,请联系管理员维护', '错误', { + + if (res.errno === 501) { + MessageBox.alert('系统未登录,请重新登录', '未登录', { confirmButtonText: '确定', type: 'error' - }) - return Promise.reject('error') - } else if (res.errno !== 0) { - MessageBox.alert('超时自动退出系统,请重新登录', '已退出', { - confirmButtonText: '重新登录', - type: 'error' }).then(() => { store.dispatch('FedLogOut').then(() => { location.reload() }) }) return Promise.reject('error') + } else if (res.errno === 502) { + MessageBox.alert('系统内部错误,请联系管理员维护', '错误', { + confirmButtonText: '确定', + type: 'error' + }) + return Promise.reject('error') + } else if (res.errno === 503) { + MessageBox.alert('请求业务目前未支持', '警告', { + confirmButtonText: '确定', + type: 'error' + }) + return Promise.reject('error') + } else if (res.errno !== 0) { + // 非5xx的错误属于业务错误,留给具体页面处理 + return Promise.reject(response) } else { return response } diff --git a/litemall-admin/src/views/goods/attribute.vue b/litemall-admin/src/views/goods/attribute.vue deleted file mode 100644 index 1544c1c5..00000000 --- a/litemall-admin/src/views/goods/attribute.vue +++ /dev/null @@ -1,218 +0,0 @@ - - - diff --git a/litemall-admin/src/views/goods/comment.vue b/litemall-admin/src/views/goods/comment.vue index d37950a7..136653e4 100644 --- a/litemall-admin/src/views/goods/comment.vue +++ b/litemall-admin/src/views/goods/comment.vue @@ -7,33 +7,33 @@ - 查找 + 查找 添加 - 导出 + 导出 - + - + - + - + + - + - - - - + @@ -210,12 +172,6 @@ $light_gray:#eee; text-align: center; font-weight: bold; } - .set-language { - color: #fff; - position: absolute; - top: 5px; - right: 0px; - } } .show-pwd { position: absolute; @@ -226,10 +182,5 @@ $light_gray:#eee; cursor: pointer; user-select: none; } - .thirdparty-button { - position: absolute; - right: 35px; - bottom: 28px; - } } diff --git a/litemall-admin/src/views/login/socialsignin.vue b/litemall-admin/src/views/login/socialsignin.vue deleted file mode 100644 index bdd11120..00000000 --- a/litemall-admin/src/views/login/socialsignin.vue +++ /dev/null @@ -1,68 +0,0 @@ - - - - - diff --git a/litemall-admin/src/views/mall/brand.vue b/litemall-admin/src/views/mall/brand.vue index a3a89bc8..72ece5af 100644 --- a/litemall-admin/src/views/mall/brand.vue +++ b/litemall-admin/src/views/mall/brand.vue @@ -7,44 +7,33 @@ - 查找 + 查找 添加 - 导出 + 导出 - - + - + - - - - - - - - - - + - - - + + - + + + + \ No newline at end of file diff --git a/litemall-admin/src/views/stat/order.vue b/litemall-admin/src/views/stat/order.vue new file mode 100644 index 00000000..d348aea6 --- /dev/null +++ b/litemall-admin/src/views/stat/order.vue @@ -0,0 +1,37 @@ + + + \ No newline at end of file diff --git a/litemall-admin/src/views/stat/user.vue b/litemall-admin/src/views/stat/user.vue new file mode 100644 index 00000000..a89073ab --- /dev/null +++ b/litemall-admin/src/views/stat/user.vue @@ -0,0 +1,38 @@ + + + \ No newline at end of file diff --git a/litemall-admin/src/views/sys/admin.vue b/litemall-admin/src/views/sys/admin.vue index e20a7fb3..cb3dead1 100644 --- a/litemall-admin/src/views/sys/admin.vue +++ b/litemall-admin/src/views/sys/admin.vue @@ -5,23 +5,26 @@
- 查找 + 查找 添加 - 导出 + 导出
- + - + - + + - + + + \ No newline at end of file diff --git a/litemall-admin/src/views/user/collect.vue b/litemall-admin/src/views/user/collect.vue index fd247999..c7302021 100644 --- a/litemall-admin/src/views/user/collect.vue +++ b/litemall-admin/src/views/user/collect.vue @@ -7,9 +7,9 @@ - 查找 + 查找 添加 - 导出 + 导出 @@ -67,13 +67,9 @@