Compare commits

...

178 Commits
qjh ... main

Author SHA1 Message Date
pvtfxms7o 95c1948187 Update YamiSysUser.java
2 months ago
pvtfxms7o 1ecd33e7fb Update ResourceServerAdapter.java
2 months ago
pvtfxms7o b0d0a43957 Update SecurityUtils.java
2 months ago
pvtfxms7o a58bcd8e2a Update AdminConfig.java
2 months ago
pvtfxms7o ae91627af9 Merge pull request '1.7' (#8) from cyj into main
2 months ago
pbvfus8to ecf3994182 Update SysConfigServiceImpl.java
2 months ago
pbvfus8to 3abacbdd5b Update SysConfigServiceImpl.java
2 months ago
pbvfus8to 6d6b6450fd Update SysLog.java
2 months ago
pbvfus8to abc2605f0c Update SysLog.java
2 months ago
pbvfus8to 7b0717f04b Update SysConfig.java
2 months ago
pbvfus8to ad31f30786 Update SysConfig.java
2 months ago
pbvfus8to 3e3adcc125 Update UpdatePasswordDto.java
2 months ago
pbvfus8to 0a60dc2958 Update UpdatePasswordDto.java
2 months ago
pbvfus8to f9febb2f29 Update SysConfigMapper.java
2 months ago
pbvfus8to dca6e9a515 Update SysConfigMapper.java
2 months ago
pbvfus8to 0446a811b8 Update coupon.wxss
2 months ago
pbvfus8to a117a038d8 Update coupon.wxss
2 months ago
pmsyffau9 29bcc76acb Update .eslintrc-auto-import.json
2 months ago
pbvfus8to 7b54503699 Update coupon.wxss
2 months ago
pbvfus8to 8ffce3e441 Update SysLogController.java
2 months ago
pbvfus8to 42121c5c99 Update SysConfigController.java
2 months ago
pmsyffau9 3413479c0e Update index.html
2 months ago
pbvfus8to a59045d1aa Update MenuType.java
2 months ago
pbvfus8to 37db7da768 Update Constant.java
2 months ago
pbvfus8to 6e4e19e236 Update SysLogAspect.java
2 months ago
pmsyffau9 de666306dd Update package.json
2 months ago
pmsyffau9 ed6e1642c3 Update tsconfig.json
2 months ago
pbvfus8to 8fb5e770ae Update CategoryBrandMapper.java
2 months ago
pmsyffau9 16ffc23bc8 Update nginx.conf
2 months ago
pbvfus8to 4c484e70d5 Update BrandMapper.java
2 months ago
pbvfus8to ea4a20188a Update BasketMapper.java
2 months ago
pbvfus8to 4e1ad2d508 Update AttachFileMapper.java
2 months ago
pbvfus8to ea0535752f Update AreaMapper.java
2 months ago
p6927cwoe 6287041b03 Update index.js
2 months ago
p6927cwoe 2fae343ef0 Update index.js
2 months ago
pmsyffau9 b7df850555 Update index.js
2 months ago
pmsyffau9 e004193ae7 Update index.js
2 months ago
pmsyffau9 06b13af655 Update README.md
2 months ago
pmsyffau9 ad109447d5 Update README.md
2 months ago
pmsyffau9 ce6ba3af07 Update index.vue
2 months ago
pmsyffau9 92d90dda84 Update README.md
2 months ago
pbvfus8to a6b50a827d Update YamiSysUser.java
2 months ago
pbvfus8to 5ee2f7c619 Update YamiSysUser.java
2 months ago
pbvfus8to 35e6b393d7 Update CaptchaAuthenticationDTO.java
2 months ago
pbvfus8to 2304ea3028 Update CaptchaAuthenticationDTO.java
2 months ago
pbvfus8to 98fe1342cb Update ResourceServerAdapter.java
2 months ago
pbvfus8to 3fad55b6fe Update ResourceServerAdapter.java
2 months ago
pbvfus8to 7c93318895 Update Brand.java
2 months ago
pbvfus8to a120b62b7b Update Brand.java
2 months ago
pbvfus8to bc6b6c5f25 Update Basket.java
2 months ago
pbvfus8to 1181fc82cd Update Basket.java
2 months ago
pbvfus8to 165d71c01d Update AttachFile.java
2 months ago
pbvfus8to 5a68bd72ed Update AttachFile.java
2 months ago
pbvfus8to 96a2759add Update Area.java
2 months ago
pbvfus8to 771fc3bfff Update Area.java
2 months ago
pbvfus8to d25ff779e5 Update XssWrapper.java
2 months ago
pbvfus8to 5561d06618 Update ServerResponseEntity.java
2 months ago
pbvfus8to cfb66f6779 Update XssUtil.java
2 months ago
pbvfus8to 692bacb0cd Update ImgJsonSerializer.java
2 months ago
pbvfus8to 86f9b70d3b Update ServerResponse.java
2 months ago
pbvfus8to d677da6a1a Update ResponseEnum.java
2 months ago
pbvfus8to 3c61f739d1 Update ResponseCode.java
2 months ago
pbvfus8to bce5cbda95 Update HttpHandler.java
2 months ago
pbvfus8to e77de28600 Update XssFilter.java
2 months ago
pbvfus8to 80f2713f57 Update YamiShopBindException.java
2 months ago
pbvfus8to 88efae9389 Update QiniuZone.java
2 months ago
pbvfus8to afddade8e5 Update OauthCacheNames.java
2 months ago
pbvfus8to 28162b1f61 Update Constant.java
2 months ago
pbvfus8to 31d9765113 Update RedisCacheConfig.java
2 months ago
pbvfus8to a9f95e2bc9 Update RedisCacheConfig.java
2 months ago
pbvfus8to ca1bca2152 Update ShopBeanConfig.java
2 months ago
pbvfus8to 8afa5647f3 Update RedisCacheConfig.java
2 months ago
pbvfus8to fcace1c8d7 Update ShopBasicConfig.java
2 months ago
pbvfus8to 246e6d4ea9 Update ResourceConfigAdapter.java
2 months ago
pbvfus8to 66d3a34972 Update MybatisPlusConfig.java
2 months ago
pbvfus8to 03ae0a4315 Update FileUploadConfig.java
2 months ago
pbvfus8to 1f0d48978e Update DefaultExceptionHandlerConfig.java
2 months ago
pbvfus8to e6482b8aad Update Qiniu.java
2 months ago
pbvfus8to 4e07d88d63 Update ImgUpload.java
2 months ago
pbvfus8to 3c8ebc76c7 Update AliDaYu.java
2 months ago
pbvfus8to fc835290dc Update RedisLockAspect.java
2 months ago
pbvfus8to 3b04b4f8aa Update SysLog.java
2 months ago
pbvfus8to ecc63969ca Update RedisLock.java
2 months ago
pbvfus8to 5033a73603 Update SubmitOrderOrder.java
2 months ago
pbvfus8to ad2f800748 Update ShopCartEventOrder.java
2 months ago
pbvfus8to c42f5bad6f Update ConfirmOrderOrder.java
2 months ago
pbvfus8to 667014ca80 Update ShopDetailParam.java
2 months ago
pbvfus8to d81c5bdead Update UserRegisterParam.java
2 months ago
pbvfus8to 547a5f0712 Update ProductParam.java
2 months ago
pbvfus8to 7d640bebaf Update OrderParam.java
2 months ago
pbvfus8to faf78b4efa Update DeliveryOrderParam.java
2 months ago
pbvfus8to f96966b9e1 Update PayInfoDto.java
2 months ago
pbvfus8to f827177c86 Update SysUserVO.java
2 months ago
pbvfus8to cca9fcd331 Update SmsInfoContext.java
2 months ago
pbvfus8to 7739ba3d4f Update UserVO.java
2 months ago
pbvfus8to fe68f8adb7 Update UserRegisterController.java
2 months ago
pbvfus8to 004421575a Update UserController.java
2 months ago
pbvfus8to b383062b10 Update UserCollectionController.java
2 months ago
pbvfus8to a6fdb601be Update SmsController.java
2 months ago
pbvfus8to b49569532d Update ShopCartController.java
2 months ago
pbvfus8to ced98f2db7 Update SkuController.java
2 months ago
pbvfus8to b026dfa3a6 Update SearchController.java
2 months ago
pbvfus8to 47d2a40753 Update ProdTagController.java
2 months ago
pbvfus8to f320a67c5e Update ProdController.java
2 months ago
pbvfus8to ef4d91a5d9 Update ProdCommController.java
2 months ago
pbvfus8to 9f19c0617c Update PayNoticeController.java
2 months ago
pbvfus8to adf1f7a27e Update PayController.java
2 months ago
pbvfus8to 5638c04655 Update OrderController.java
2 months ago
pbvfus8to 63a37bde2c Update MyOrderController.java
2 months ago
pbvfus8to 05438d025a Update NoticeController.java
2 months ago
pbvfus8to 9f4ca94cbb Update IndexImgController.java
2 months ago
pbvfus8to ce684669e0 Update DeliveryController.java
2 months ago
pbvfus8to 156a360b34 Update DeliveryController.java
2 months ago
pbvfus8to 5eb9290e68 Update CategoryController.java
2 months ago
pbvfus8to ee7fa822a5 Update CategoryController.java
2 months ago
pbvfus8to afddfede8d Update AreaController.java
2 months ago
pbvfus8to 8195e74911 Update AreaController.java
2 months ago
pbvfus8to d994e91883 Update AddrController.java
2 months ago
pbvfus8to fe3a23381f Update SubmitOrderListener.java
2 months ago
pbvfus8to 9ec893deb7 Update ShopCartListener.java
2 months ago
pbvfus8to cbe72dfc20 Update ConfirmOrderListener.java
2 months ago
pbvfus8to a22c1decd0 Update ApiApplication.java
2 months ago
pbvfus8to 7b2a7d82e7 Update SwaggerConfiguration.java
2 months ago
pbvfus8to fa64b38ee4 Update ApiConfig.java
2 months ago
pbvfus8to 6a226492d3 Update ApiBeanConfig.java
2 months ago
pbvfus8to 0aa1cfb6ad Update OrderTask.java
2 months ago
pbvfus8to 59e24c8b62 Update WebApplication.java
2 months ago
pbvfus8to 5b0601dd39 Update UserController.java
2 months ago
pbvfus8to ef01acb7f9 Update UserAddrController.java
2 months ago
pbvfus8to ddce0e28cb Update UserAddrController.java
2 months ago
pbvfus8to 13a16e3ccb Update TransportController.java
2 months ago
pbvfus8to c7b45e47d3 Update SpecController.java
2 months ago
pbvfus8to adff03961d Update ShopDetailController.java
2 months ago
pbvfus8to 74e503e1c9 Update ProductController.java
2 months ago
pbvfus8to 9c418de0e1 Update ProdTagReferenceController.java
2 months ago
pbvfus8to 6c7a3c4244 Update ProdTagController.java
2 months ago
pbvfus8to c533d9b3eb Update ProdCommController.java
2 months ago
pbvfus8to 552df56b8b Update PickAddrController.java
2 months ago
pbvfus8to 89d78e4a12 Update OrderController.java
2 months ago
pbvfus8to 5e9728171e Update NoticeController.java
2 months ago
pbvfus8to 3d7ac2bd0d Update MessageController.java
2 months ago
pbvfus8to 641c9fe7a6 Update IndexImgController.java
2 months ago
pbvfus8to 30892c41db Update HotSearchController.java
2 months ago
pbvfus8to 961a3fa388 Update FileController.java
2 months ago
pbvfus8to 5bcc756a64 Update DeliveryController.java
2 months ago
pbvfus8to 1789270f80 Update CategoryController.java
2 months ago
pbvfus8to b2c499d142 Update BrandController.java
2 months ago
pbvfus8to 3662750764 Update AttributeController.java
2 months ago
pbvfus8to 65fa627568 Update AreaController.java
2 months ago
pbvfus8to 3a7a370525 Update AdminLoginController.java
2 months ago
pbvfus8to 040f52e09b Update XxlJobConfig.java
2 months ago
pbvfus8to ffea9fd8bb Update AdminConfig.java
2 months ago
pbvfus8to 58d385bd3a Update SwaggerConfiguration.java
2 months ago
pbvfus8to f5021b34cd Update AdminBeanConfig.java
2 months ago
pvtfxms7o 68140563f8 Update 2. 订单设计-确认订单.md
2 months ago
cyj be39ff3d65 Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
cyj 3e66f40a2e Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
cyj 5432dcd8cb Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
k4243kian 145dcb8036 Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
k4243kian 90b2f14cd5 Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
pvtfxms7o 6e44d41b04 Merge pull request '1.6' (#7) from cyj into main
2 months ago
pvtfxms7o 6840647be8 Merge pull request '1.6' (#7) from cyj into main
2 months ago
cyj 754a372973 Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
cyj 4e5fc1ce0a Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
pvtfxms7o 9ca59a9a18 Merge pull request '1.5' (#6) from yq into main
2 months ago
cyj be920706da Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
cyj 31a33f7658 Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
k4243kian b3dd2a26fe Merge branch 'yq' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
k4243kian 7d22a20f13 Merge branch 'yq' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
pvtfxms7o ec7c1379c2 Merge pull request '1.4' (#5) from qjh into main
2 months ago
cyj a9f191bc8d Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
cyj aa4c6b5cd8 Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
cyj ee441b193e Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
cyj cd215158b6 Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
cyj d866f136af Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
cyj 974920d7d0 Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
cyj c31bd558a5 Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago
k4243kian 370d840dc5 Merge branch 'cyj' of https://bdgit.educoder.net/pvtfxms7o/yudao
2 months ago

@ -1,2 +1,138 @@
# mall4 # 编写的原因
写在前面,很多加入我们群里的人,都会问我们源码在哪里,现在仔细回答一下
1. 我们已经声明了这是一个前后端分离的商城而且分离的很彻底java的后台管理系统不是jsp使用vue写的所以商城的后台管理在vue这个项目 https://gitee.com/gz-yami/mall4v ,启动完java不要访问java的端口启动vue访问vue的端口页面就能看到后台管理就能上商品了
2. 和上面一样的,很多人问,前端的浏览商品的页面在哪里,其实就在这里 https://gitee.com/gz-yami/mall4m
3. 有人会问你是不是将上面两个链接藏起来了上面两个项目的链接都在readme里面写着可是很多人都不读。
4. swagger文档怎么访问不了其实路径是/doc.html
5. 我们开源的刚上线直接申请通过了推荐项目第一天就有100个星星一个月就有1k的星星大家都不是傻的这代码是能用的拜托...后来没怎么维护(整个流程都是好的,整个功能都是好的,都不知道维护啥),现在又开始维护了,星星也没了(如果你悄悄拿去做外包项目,觉得这个项目对你有用,就给个星星呗)
## 先确定我们下载的项目有几个项目
- mall4jj代表javajava项目这里面包含了小程序/后台vue连接需要的接口。
- mall4vv代表vue项目是后台管理员界面使用的前端项目因为前后端分离的
- mall4mm代表mini小程序项目这里的项目是小程序的项目
- mall4uniuni代表uniappH5项目这里的项目是H5的项目
- jvmjava虚拟机啦~
## 1.java开发环境安装
### 1.1开发环境
以下版本是最低要求的!!! 提问问题前请注意开发环境!!
| 工具 | 版本 |
|---------|-------|
| jdk | 17 |
| mysql | 5.7+ |
| redis | 4.0+ |
| nodejs | 14-16 |
| xxl-job | 2.4.0 |
### 1.2 安装jdk + mysql + redis + maven
如果不了解怎么安装jdk的可以参考 [菜鸟教程的java相关](https://www.runoob.com/java/java-environment-setup.html)
- 教程展示的是oracle需要自行搜索openjdk的下载链接下载jdk17版本
如果不了解怎么安装mysql的可以参考 [菜鸟教程的mysql相关](https://www.runoob.com/mysql/mysql-install.html)
如果不了解怎么安装maven的可以参考 [菜鸟教程的maven相关]( https://www.runoob.com/maven/maven-setup.html )
如果对于redis的安装并不了解的可以参考 [菜鸟教程的redis相关](https://www.runoob.com/redis/redis-install.html)
安装相对简单网上也有很多教程这里就不多讲述。安装完按需对redis进行配置后启动redis服务即可。
### 2.启动
- 推荐使用idea安装lombok插件后使用idea导入maven项目
- 将yami_shop.sql导入到mysql中修改`application-dev.yml`更改 datasource.url、user、password
- 通过修改`shop.properties` 修改七牛云、阿里大鱼等信息
- 修改`api.properties` 修改当前接口所在域名,用于支付回调
- 启动redis端口6379
- 通过`WebApplication`启动项目后台接口,`ApiApplication` 启动项目前端接口
- xxl-job定时任务通过github或者gitee下载xxl-job的已经打包好的源码把`XxlJobConfig.class`这个文件的代码注释打开配置yml文件中相关xxl-job配置即可使用
## 3.vue开发环境安装
这是一套正常的vue启动流程。如果你无法理解可能要先学习一下vue...
#### 3.1 安装nodejs
[NodeJS](https://nodejs.org) 项目要求最低 18.12.0,推荐 20.9.0
如果不了解怎么安装nodejs的可以参考 [菜鸟教程的nodejs相关](https://www.runoob.com/nodejs/nodejs-install-setup.html)
#### 3.2 安装依赖启动项目
项目要求使用 [pnpm](https://www.pnpm.cn/) 包管理工具
使用编辑器打开项目,在根目录执行以下命令安装依赖
```bash
pnpm i
```
如果不想使用 pnpm请删除 `package.json` 文件中 `preinstall` 脚本后再进行安装
```json
{
"scripts" : {
"preinstall": "npx only-allow pnpm" // 使用其他包管理工具npm、yarn、cnpm等请删除此命令
}
}
```
H5端和平台端修改文件`.env.production`(生产环境)/ `.env.development`(开发环境)
里面的`VITE_APP_BASE_API`为api接口请求地址 `VITE_APP_RESOURCES_URL`为静态资源文件url
```json
// api接口请求地址
VITE_APP_BASE_API = 'http://127.0.0.1:8085'
// 静态资源文件url
VITE_APP_RESOURCES_URL = 'https://img.mall4j.com/'
```
mall4m小程序端修改文件`utils\config.js`,里面的`domain`为api接口请求地址
注意如果启动uni项目或者小程序默认后台api服务端口号为8086
如果启动后台项目默认后台admin服务端口号为8085请对照仔细填写后再启动如遇401状态码仔细检查端口号是否配置正确
如果后台启动后图形验证码显示“接口验证失败数过多请稍后再试”请F12打开network确定连接的admin服务端口号是否正确ip或域名是否正确
如果有配置nginx还要确认下项目访问路径是否正确可以通过地址+/doc.html来访问接口文档确定是否正确访问到admin服务
运行dev环境
```bash
npm run dev
```
运行dev环境(H5)
```bash
npm run dev:h5
```
## 4.文档
这代码有没有文档呀?
当然有啦你已经下载了在doc这个文件夹上实在不知道我就给链接出来咯
### [https://gitee.com/gz-yami/mall4j/tree/master/doc](https://gitee.com/gz-yami/mall4j/tree/master/doc)

@ -1,4 +1,4 @@
# 编写原因 # 编写原因
写在前面,很多加入我们群里的人,都会问我们源码在哪里,现在仔细回答一下 写在前面,很多加入我们群里的人,都会问我们源码在哪里,现在仔细回答一下

@ -1,6 +1,6 @@
## 商品分组 ## 商品分组
#### 商城应用 ####商城应用
在mall4j精选商城首页中可以看到有`每日上新`、`商城热卖`、`更多商品`等标签栏,在每一栏位中用来展示特定的商品列表,如下图:。 在mall4j精选商城首页中可以看到有`每日上新`、`商城热卖`、`更多商品`等标签栏,在每一栏位中用来展示特定的商品列表,如下图:。

@ -2,7 +2,7 @@
在看具体的数据库实体设计之前,我们先一起了解下**电商的名词定义** 在看具体的数据库实体设计之前,我们先一起了解下**电商的名词定义**
## 1.1 名词定义 ##1.1 名词定义
参考 [《产品 SKU 是什么意思?与之相关的还有哪些?》](https://www.zhihu.com/question/19841574) 整理。 参考 [《产品 SKU 是什么意思?与之相关的还有哪些?》](https://www.zhihu.com/question/19841574) 整理。

@ -2,7 +2,7 @@
## PageAdapter ##PageAdapter
使用分页时,前端传入的数据统一格式为`current`当前页,`size`每页大小。而我们在数据库中要将这两个数据变更为从第几行到第几行,所以我们需要简单的适配一下: 使用分页时,前端传入的数据统一格式为`current`当前页,`size`每页大小。而我们在数据库中要将这两个数据变更为从第几行到第几行,所以我们需要简单的适配一下:

@ -1,6 +1,6 @@
在小程序登陆的时候,在`MiniAppAuthenticationProvider`中我们看到这样一行代码 在小程序登陆的时候,在`MiniAppAuthenticationProvider`中我们看到这样一行代码
```java ``java
yamiUserDetailsService.insertUserIfNecessary(appConnect); yamiUserDetailsService.insertUserIfNecessary(appConnect);
``` ```

@ -2,7 +2,7 @@
网上有很多说解决xss攻击的方法有很多都是和前端有关而实际上在后台这最后一个防御当中是最为重要的。 网上有很多说解决xss攻击的方法有很多都是和前端有关而实际上在后台这最后一个防御当中是最为重要的。
在mall4j这个项目里面,使用了一个过滤器 `XssFilter` 在mall4这个项目里面使用了一个过滤器 `XssFilter`
``` ```
public class XssFilter implements Filter { public class XssFilter implements Filter {

@ -7,7 +7,7 @@
- 多图片上传:`src\components\mul-pic-upload` - 多图片上传:`src\components\mul-pic-upload`
- 文件上传:`src\components\file-upload` - 文件上传:`src\components\file-upload`
上述这些文件上传,都是基于`el-upload`进封装 上述这些文件上传,都是基于`el-upload`进封装

@ -1,6 +1,6 @@
## 权限控制 ## 权限控制
#### 前端权限控制 ### 前端权限控制
在商城运营时,我们可能是多个人员共同操作我们的系统,但是每个操作人员所具备的权限应该不同,权限的不同主要表现在两个部分,即导航菜单的查看权限和页面增删改操作按钮的操作权限。我们的把页面导航菜单查看权限和页面操作按钮统一存储在菜单数据库表中,菜单类型页面资源的类型。类型包括目录 、菜单 、按钮。 在商城运营时,我们可能是多个人员共同操作我们的系统,但是每个操作人员所具备的权限应该不同,权限的不同主要表现在两个部分,即导航菜单的查看权限和页面增删改操作按钮的操作权限。我们的把页面导航菜单查看权限和页面操作按钮统一存储在菜单数据库表中,菜单类型页面资源的类型。类型包括目录 、菜单 、按钮。

@ -1,4 +1,4 @@
## 后台异常处理 # 后台异常处理
在开发过程中,不可避免的是需要处理各种异常,异常处理方法随处可见,所以代码中就会出现大量的`try {...} catch {...} finally {...}` 代码块,不仅会造成大量的冗余代码,而且还影响代码的可读性,所以对异常统一处理非常有必要。为此,我们定义了一个统一的异常类`YamiShopBindException` 与异常管理类 `DefaultExceptionHandlerConfig` 在开发过程中,不可避免的是需要处理各种异常,异常处理方法随处可见,所以代码中就会出现大量的`try {...} catch {...} finally {...}` 代码块,不仅会造成大量的冗余代码,而且还影响代码的可读性,所以对异常统一处理非常有必要。为此,我们定义了一个统一的异常类`YamiShopBindException` 与异常管理类 `DefaultExceptionHandlerConfig`

@ -2,7 +2,7 @@
利用`spring`框架中`aop`,我们可以实现业务代码与系统级服务进行解耦,例如日志记录、事务及其他安全业务等,可以使得我们的工程更加容易维护、优雅。如何在系统中添加相应的日志呢? 利用`spring`框架中`aop`,我们可以实现业务代码与系统级服务进行解耦,例如日志记录、事务及其他安全业务等,可以使得我们的工程更加容易维护、优雅。如何在系统中添加相应的日志呢?
##### 添加依赖 #### 添加依赖
``` ```
<dependency> <dependency>

@ -1,4 +1,4 @@
我们后使用`spring` 为我们提供好的统一校验的工具`spring-boot-starter-validation`对请求进行校验。 我们后使用`spring` 为我们提供好的统一校验的工具`spring-boot-starter-validation`对请求进行校验。
```xml ```xml
<dependency> <dependency>

@ -1,4 +1,4 @@
## 通用分页表格实现 # 通用分页表格实现
前端基于VUE的轻量级表格插件 `avue` 前端基于VUE的轻量级表格插件 `avue`
后端分页组件使用Mybatis分页插件 `MybatisPlus` 后端分页组件使用Mybatis分页插件 `MybatisPlus`

@ -2,7 +2,7 @@
~~~ ~~~
yami-shops yami-shops
├── mall4m -- 小程序代码 ├──mall4m -- 小程序代码
├── mall4v -- 后台vue代码 ├── mall4v -- 后台vue代码
├── yami-shop-admin -- 后台vue接口工程[8085] ├── yami-shop-admin -- 后台vue接口工程[8085]
├── yami-shop-api -- 前端(小程序)接口工程[8086] ├── yami-shop-api -- 前端(小程序)接口工程[8086]

@ -1,4 +1,4 @@
里整理了一些经常会被问到的问题: 里整理了一些经常会被问到的问题:
1. 为什么vue打包之后或者修改url之后无法登录 1. 为什么vue打包之后或者修改url之后无法登录
你用chrome按f12看看console提示的信息如`Access-Control-Allow-Origin` 那就是跨域了再看看network的请求方法是不是`options`但是返回不是200这也是跨域了。 你用chrome按f12看看console提示的信息如`Access-Control-Allow-Origin` 那就是跨域了再看看network的请求方法是不是`options`但是返回不是200这也是跨域了。

@ -13,7 +13,7 @@
我们先来看下是如何获取商品信息的 我们先来看下是如何获取商品信息的
```java ``java
@PostMapping("/info") @PostMapping("/info")
@Operation(summary = "获取用户购物车信息" , description = "获取用户购物车信息,参数为用户选中的活动项数组,以购物车id为key") @Operation(summary = "获取用户购物车信息" , description = "获取用户购物车信息,参数为用户选中的活动项数组,以购物车id为key")
public ServerResponseEntity<List<ShopCartDto>> info(@RequestBody Map<Long, ShopCartParam> basketIdShopCartParamMap) { public ServerResponseEntity<List<ShopCartDto>> info(@RequestBody Map<Long, ShopCartParam> basketIdShopCartParamMap) {

@ -1,4 +1,4 @@
下单简单的分成几个步骤 下单简单的分成几个步骤
1. 用户点击“立即购买”或“购物车-结算”进入到“确认订单”页面 1. 用户点击“立即购买”或“购物车-结算”进入到“确认订单”页面
2. 在“确认订单”页面选择收货地址,优惠券等,重新计算运费、订单价格 2. 在“确认订单”页面选择收货地址,优惠券等,重新计算运费、订单价格
@ -7,7 +7,7 @@
## 第一步: # 第一步:
1. 用户点击“立即购买”或“购物车-结算”进入到“确认订单”页面相关url`/p/order/confirm` 1. 用户点击“立即购买”或“购物车-结算”进入到“确认订单”页面相关url`/p/order/confirm`

@ -8,7 +8,7 @@
我们返回确认订单的接口,看到这样一行代码: 我们返回确认订单的接口,看到这样一行代码:
```java ``java
@Operation(summary = "结算,生成订单信息" , description = "传入下单所需要的参数进行下单") @Operation(summary = "结算,生成订单信息" , description = "传入下单所需要的参数进行下单")
public ServerResponseEntity<ShopCartOrderMergerDto> confirm(@Valid @RequestBody OrderParam orderParam) { public ServerResponseEntity<ShopCartOrderMergerDto> confirm(@Valid @RequestBody OrderParam orderParam) {
orderService.putConfirmOrderCache(userId,shopCartOrderMergerDto); orderService.putConfirmOrderCache(userId,shopCartOrderMergerDto);

@ -1,6 +1,6 @@
> 我们的支付时不允许在订单的支付接口传订单金额的,所以我们采用了订单号进行支付的形式 > 我们的支付时不允许在订单的支付接口传订单金额的,所以我们采用了订单号进行支付的形式
## 支付 # 支付
我们来到`PayController` ,这里就是统一支付的接口,当然这里的统一支付采用的是模拟支付。 我们来到`PayController` ,这里就是统一支付的接口,当然这里的统一支付采用的是模拟支付。

@ -1,3 +1,3 @@
这里只有几点说明: 这里只有几点说明:
1. 这里写的是接口设计如果你整个接口的接口文档只需要启动api这个项目然后访问 http://localhost:8086/doc.html 1 这里写的是接口设计如果你整个接口的接口文档只需要启动api这个项目然后访问 http://localhost:8086/doc.html

@ -3,6 +3,6 @@
安装JDK,如果没有java-17-openjdk-devel就没有javac命令 安装JDK,如果没有java-17-openjdk-devel就没有javac命令
```bash ```bash
yum install java-17-openjdk java-17-openjdk-devel yu install java-17-openjdk java-17-openjdk-devel
``` ```

@ -14,7 +14,7 @@ docker-compose version 1.17.1, build 6d101fb
Linux 系统请使用以下介绍的方法安装。 Linux 系统请使用以下介绍的方法安装。
## 安装方法一:二进制包 # 安装方法一:二进制包
在 Linux 上的也安装十分简单,从 [官方 GitHub Release](https://github.com/docker/compose/releases) 处直接下载编译好的二进制文件即可。 在 Linux 上的也安装十分简单,从 [官方 GitHub Release](https://github.com/docker/compose/releases) 处直接下载编译好的二进制文件即可。

@ -1,4 +1,4 @@
## 安装 Docker # 安装 Docker
从 2017 年 3 月开始 docker 在原来的基础上分为两个分支版本: Docker CE 和 Docker EE。 从 2017 年 3 月开始 docker 在原来的基础上分为两个分支版本: Docker CE 和 Docker EE。
Docker CE 即社区免费版Docker EE 即企业版,强调安全,但需付费使用。 Docker CE 即社区免费版Docker EE 即企业版,强调安全,但需付费使用。

@ -9,7 +9,7 @@ docker pull [OPTIONS] NAME[:TAG|@DIGEST]
``` ```
体的选项可以通过 docker pull --help 命令看到,这里我们说一下镜像名称的格式。 体的选项可以通过 docker pull --help 命令看到,这里我们说一下镜像名称的格式。
- Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub。 - Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub。
- 仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub如果不给出用户名则默认为 library也就是官方镜像。 - 仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub如果不给出用户名则默认为 library也就是官方镜像。

@ -4,7 +4,7 @@
从 Docker 镜像仓库获取镜像的命令是 `docker pull`。其命令格式为: 从 Docker 镜像仓库获取镜像的命令是 `docker pull`。其命令格式为:
``` ```
# docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签] docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
docker pull [OPTIONS] NAME[:TAG|@DIGEST] docker pull [OPTIONS] NAME[:TAG|@DIGEST]
``` ```

@ -2,7 +2,7 @@
**如果无法理解我们所编写的 `Dockerfile`强烈的不推荐使用docker进行生产环境部署** *如果无法理解我们所编写的 `Dockerfile`强烈的不推荐使用docker进行生产环境部署**
0. 将整个项目上传到centos中进入到项目根目录 0. 将整个项目上传到centos中进入到项目根目录
1. 安装 `docker` (参考《docker centos 安装》) 1. 安装 `docker` (参考《docker centos 安装》)

@ -1,6 +1,6 @@
安装maven的前提是安装jdk参考《linux jdk安装》 安装maven的前提是安装jdk参考《linux jdk安装》
```bash ``bash
// 使用配置工具配置第三方epel源仓库 // 使用配置工具配置第三方epel源仓库
yum-config-manager --add-repo http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo yum-config-manager --add-repo http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo
yum-config-manager --enable epel-apache-maven yum-config-manager --enable epel-apache-maven

@ -2,7 +2,7 @@
Nginx官方提供了Yum源 Nginx官方提供了Yum源
## 1、安装nginx # 1、安装nginx
```shell ```shell
yum install -y nginx yum install -y nginx

@ -1,6 +1,6 @@
本文为大家介绍了*CentOS* 7 64位 安装 *MySQL5.7* 的详细步骤 本文为大家介绍了*CentOS* 7 64位 安装 *MySQL5.7* 的详细步骤
## 1、配置YUM源 # 1、配置YUM源
在[MySQL]官网中下载YUM源rpm安装包http://dev.mysql.com/downloads/repo/yum/ 在[MySQL]官网中下载YUM源rpm安装包http://dev.mysql.com/downloads/repo/yum/

@ -1,7 +1,7 @@
## 安装redis ## 安装redis
``` ```
#安装tcl redis需要 安装tcl redis需要
wget http://downloads.sourceforge.net/tcl/tcl8.6.8-src.tar.gz wget http://downloads.sourceforge.net/tcl/tcl8.6.8-src.tar.gz
tar xzvf tcl8.6.8-src.tar.gz -C /usr/local/ tar xzvf tcl8.6.8-src.tar.gz -C /usr/local/
cd /usr/local/tcl8.6.8/unix/ cd /usr/local/tcl8.6.8/unix/

@ -1,6 +1,6 @@
## 安装jdk ## 安装jdk
JDK,如果没有java-17-openjdk-devel就没有javac命令 安JDK,如果没有java-17-openjdk-devel就没有javac命令
```bash ```bash
yum install java-17-openjdk java-17-openjdk-devel yum install java-17-openjdk java-17-openjdk-devel

@ -1,242 +1,345 @@
module.exports = (function() { // 模块导出的自执行函数,用于定义模块的导出内容以及处理模块的依赖加载等逻辑
var __MODS__ = {}; module.exports = (function () {
var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; var __MODS__ = {};
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; }; // 定义模块的函数,用于创建一个模块的相关信息对象,包括模块状态、执行函数、依赖等
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; var __DEFINE__ = function (modId, func, req) {
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; var m = { exports: {}, _tempexports: {} };
__DEFINE__(1648886413910, function(require, module, exports) { __MODS__[modId] = { status: 0, func: func, req: req, m: m };
;(function (root, factory, undef) { };
if (typeof exports === "object") { // 用于加载模块的函数如果模块未加载则通过原生的require加载若已定义但未执行则执行模块函数并返回其导出内容
// CommonJS var __REQUIRE__ = function (modId, source) {
module.exports = exports = factory(require("./core"), require("./x64-core"), require("./lib-typedarrays"), require("./enc-utf16"), require("./enc-base64"), require("./enc-base64url"), require("./md5"), require("./sha1"), require("./sha256"), require("./sha224"), require("./sha512"), require("./sha384"), require("./sha3"), require("./ripemd160"), require("./hmac"), require("./pbkdf2"), require("./evpkdf"), require("./cipher-core"), require("./mode-cfb"), require("./mode-ctr"), require("./mode-ctr-gladman"), require("./mode-ofb"), require("./mode-ecb"), require("./pad-ansix923"), require("./pad-iso10126"), require("./pad-iso97971"), require("./pad-zeropadding"), require("./pad-nopadding"), require("./format-hex"), require("./aes"), require("./tripledes"), require("./rc4"), require("./rabbit"), require("./rabbit-legacy")); if (!__MODS__[modId]) return require(source);
} if (!__MODS__[modId].status) {
else if (typeof define === "function" && define.amd) { var m = __MODS__[modId].m;
// AMD m._exports = m._tempexports;
define(["./core", "./x64-core", "./lib-typedarrays", "./enc-utf16", "./enc-base64", "./enc-base64url", "./md5", "./sha1", "./sha256", "./sha224", "./sha512", "./sha384", "./sha3", "./ripemd160", "./hmac", "./pbkdf2", "./evpkdf", "./cipher-core", "./mode-cfb", "./mode-ctr", "./mode-ctr-gladman", "./mode-ofb", "./mode-ecb", "./pad-ansix923", "./pad-iso10126", "./pad-iso97971", "./pad-zeropadding", "./pad-nopadding", "./format-hex", "./aes", "./tripledes", "./rc4", "./rabbit", "./rabbit-legacy"], factory); var desp = Object.getOwnPropertyDescriptor(m, "exports");
} if (desp && desp.configurable)
else { Object.defineProperty(m, "exports", {
// Global (browser) set: function (val) {
root.CryptoJS = factory(root.CryptoJS); if (typeof val === "object" && val!== m._exports) {
} m._exports.__proto__ = val.__proto__;
}(this, function (CryptoJS) { Object.keys(val).forEach(function (k) {
m._exports[k] = val[k];
return CryptoJS; });
}
})); m._tempexports = val;
}, function(modId) {var map = {"./core":1648886413911,"./x64-core":1648886413912,"./lib-typedarrays":1648886413913,"./enc-utf16":1648886413914,"./enc-base64":1648886413915,"./enc-base64url":1648886413916,"./md5":1648886413917,"./sha1":1648886413918,"./sha256":1648886413919,"./sha224":1648886413920,"./sha512":1648886413921,"./sha384":1648886413922,"./sha3":1648886413923,"./ripemd160":1648886413924,"./hmac":1648886413925,"./pbkdf2":1648886413926,"./evpkdf":1648886413927,"./cipher-core":1648886413928,"./mode-cfb":1648886413929,"./mode-ctr":1648886413930,"./mode-ctr-gladman":1648886413931,"./mode-ofb":1648886413932,"./mode-ecb":1648886413933,"./pad-ansix923":1648886413934,"./pad-iso10126":1648886413935,"./pad-iso97971":1648886413936,"./pad-zeropadding":1648886413937,"./pad-nopadding":1648886413938,"./format-hex":1648886413939,"./aes":1648886413940,"./tripledes":1648886413941,"./rc4":1648886413942,"./rabbit":1648886413943,"./rabbit-legacy":1648886413944}; return __REQUIRE__(map[modId], modId); }) },
__DEFINE__(1648886413911, function(require, module, exports) { get: function () {
;(function (root, factory) { return m._tempexports;
if (typeof exports === "object") { }
// CommonJS });
module.exports = exports = factory(); __MODS__[modId].status = 1;
} __MODS__[modId].func(__MODS__[modId].req, m, m.exports);
else if (typeof define === "function" && define.amd) { }
// AMD return __MODS__[modId].m.exports;
define([], factory); };
} // 处理通配符导入的模块如果模块是ES模块则直接返回否则创建一个新对象并将原对象的属性复制过去同时添加默认属性指向原对象
else { var __REQUIRE_WILDCARD__ = function (obj) {
// Global (browser) if (obj && obj.__esModule) {
root.CryptoJS = factory(); return obj;
} } else {
}(this, function () { var newObj = {};
if (obj!= null) {
/*globals window, global, require*/ for (var k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k];
/** }
* CryptoJS core components. }
*/ newObj.default = obj;
var CryptoJS = CryptoJS || (function (Math, undefined) { return newObj;
}
var crypto; };
// 获取模块默认导出的函数如果模块是ES模块则返回其default属性否则直接返回模块对象
// Native crypto from window (Browser) var __REQUIRE_DEFAULT__ = function (obj) {
if (typeof window !== 'undefined' && window.crypto) { return obj && obj.__esModule? obj.default : obj;
crypto = window.crypto; };
}
// Native crypto in web worker (Browser)
if (typeof self !== 'undefined' && self.crypto) {
crypto = self.crypto;
}
// Native crypto from worker
if (typeof globalThis !== 'undefined' && globalThis.crypto) {
crypto = globalThis.crypto;
}
// Native (experimental IE 11) crypto from window (Browser)
if (!crypto && typeof window !== 'undefined' && window.msCrypto) {
crypto = window.msCrypto;
}
// Native crypto from global (NodeJS)
if (!crypto && typeof global !== 'undefined' && global.crypto) {
crypto = global.crypto;
}
// Native crypto import via require (NodeJS)
if (!crypto && typeof require === 'function') {
try {
crypto = require('crypto');
} catch (err) {}
}
/*
* Cryptographically secure pseudorandom number generator
*
* As Math.random() is cryptographically not safe to use
*/
var cryptoSecureRandomInt = function () {
if (crypto) {
// Use getRandomValues method (Browser)
if (typeof crypto.getRandomValues === 'function') {
try {
return crypto.getRandomValues(new Uint32Array(1))[0];
} catch (err) {}
}
// Use randomBytes method (NodeJS)
if (typeof crypto.randomBytes === 'function') {
try {
return crypto.randomBytes(4).readInt32LE();
} catch (err) {}
}
}
throw new Error('Native crypto module could not be used to get secure random number.');
};
/*
* Local polyfill of Object.create
*/
var create = Object.create || (function () {
function F() {}
return function (obj) {
var subtype;
F.prototype = obj;
subtype = new F();
F.prototype = null;
return subtype;
};
}());
/**
* CryptoJS namespace.
*/
var C = {};
/**
* Library namespace.
*/
var C_lib = C.lib = {};
/**
* Base object for prototypal inheritance.
*/
var Base = C_lib.Base = (function () {
return {
/**
* Creates a new object that inherits from this object.
*
* @param {Object} overrides Properties to copy into the new object.
*
* @return {Object} The new object.
*
* @static
*
* @example
*
* var MyType = CryptoJS.lib.Base.extend({
* field: 'value',
*
* method: function () {
* }
* });
*/
extend: function (overrides) {
// Spawn
var subtype = create(this);
// Augment
if (overrides) {
subtype.mixIn(overrides);
}
// Create default initializer
if (!subtype.hasOwnProperty('init') || this.init === subtype.init) {
subtype.init = function () {
subtype.$super.init.apply(this, arguments);
};
}
// Initializer's prototype is the subtype object
subtype.init.prototype = subtype;
// Reference supertype
subtype.$super = this;
return subtype;
},
/**
* Extends this object and runs the init method.
* Arguments to create() will be passed to init().
*
* @return {Object} The new object.
*
* @static
*
* @example
*
* var instance = MyType.create();
*/
create: function () {
var instance = this.extend();
instance.init.apply(instance, arguments);
return instance;
},
/** // 定义模块ID为1648886413910的模块它主要处理不同环境下CommonJS、AMD、全局浏览器环境CryptoJS的导出逻辑
* Initializes a newly created object. __DEFINE__(1648886413910, function (require, module, exports) {
* Override this method to add some logic when your objects are created. // 立即执行函数根据不同的模块环境CommonJS、AMD、全局浏览器环境来设置CryptoJS的导出方式
* (function (root, factory, undef) {
* @example if (typeof exports === "object") {
* // CommonJS环境下将factory函数执行的结果赋值给module.exports和exportsfactory函数需要传入多个依赖模块
* var MyType = CryptoJS.lib.Base.extend({ module.exports = exports = factory(
* init: function () { require("./core"),
* // ... require("./x64-core"),
* } require("./lib-typedarrays"),
* }); require("./enc-utf16"),
*/ require("./enc-base64"),
init: function () { require("./enc-base64url"),
}, require("./md5"),
require("./sha1"),
require("./sha256"),
require("./sha224"),
require("./sha512"),
require("./sha384"),
require("./sha3"),
require("./ripemd160"),
require("./hmac"),
require("./pbkdf2"),
require("./evpkdf"),
require("./cipher-core"),
require("./mode-cfb"),
require("./mode-ctr"),
require("./mode-ctr-gladman"),
require("./mode-ofb"),
require("./mode-ecb"),
require("./pad-ansix923"),
require("./pad-iso10126"),
require("./pad-iso97971"),
require("./pad-zeropadding"),
require("./pad-nopadding"),
require("./format-hex"),
require("./aes"),
require("./tripledes"),
require("./rc4"),
require("./rabbit"),
require("./rabbit-legacy")
);
} else if (typeof define === "function" && define.amd) {
// AMD环境下使用define函数定义模块传入依赖数组和factory函数
define([
"./core",
"./x64-core",
"./lib-typedarrays",
"./enc-utf16",
"./enc-base64",
"./enc-base64url",
"./md5",
"./sha1",
"./sha256",
"./sha224",
"./sha512",
"./sha384",
"./sha3",
"./ripemd160",
"./hmac",
"./pbkdf2",
"./evpkdf",
"./cipher-core",
"./mode-cfb",
"./mode-ctr",
"./mode-ctr-gladman",
"./mode-ofb",
"./mode-ecb",
"./pad-ansix923",
"./pad-iso10126",
"./pad-iso97971",
"./pad-zeropadding",
"./pad-nopadding",
"./format-hex",
"./aes",
"./tripledes",
"./rc4",
"./rabbit",
"./rabbit-legacy"
], factory);
} else {
// 全局浏览器环境下将factory函数传入全局的CryptoJS如果存在执行并将结果赋值给全局的CryptoJS
root.CryptoJS = factory(root.CryptoJS);
}
}(this, function (CryptoJS) {
return CryptoJS;
}));
}, function (modId) {
var map = {
"./core": 1648886413911,
"./x64-core": 1648886413912,
"./lib-typedarrays": 1648886413913,
"./enc-utf16": 1648886413914,
"./enc-base64": 1648886413915,
"./enc-base64url": 1648886413916,
"./md5": 1648886413917,
"./sha1": 1648886413918,
"./sha256": 1648886413919,
"./sha224": 1648886413920,
"./sha512": 1648886413921,
"./sha384": 1648886413922,
"./sha3": 1648886413923,
"./ripemd160": 1648886413924,
"./hmac": 1648886413925,
"./pbkdf2": 1648886413926,
"./evpkdf": 1648886413927,
"./cipher-core": 1648886413928,
"./mode-cfb": 1648886413929,
"./mode-ctr": 1648886413930,
"./mode-ctr-gladman": 1648886413931,
"./mode-ofb": 1648886413932,
"./mode-ecb": 1648886413933,
"./pad-ansix923": 1648886413934,
"./pad-iso10126": 1648886413935,
"./pad-iso97971": 1648886413936,
"./pad-zeropadding": 1648886413937,
"./pad-nopadding": 1648886413938,
"./format-hex": 1648886413939,
"./aes": 1648886413940,
"./tripledes": 1648886413941,
"./rc4": 1648886413942,
"./rabbit": 1648886413943,
"./rabbit-legacy": 1648886413944
};
return __REQUIRE__(map[modId], modId);
});
// 定义模块ID为1648886413911的模块主要处理CryptoJS核心部分在不同环境下的导出逻辑
__DEFINE__(1648886413911, function (require, module, exports) {
// 立即执行函数根据不同的模块环境CommonJS、AMD、全局浏览器环境来设置CryptoJS核心部分的导出方式
(function (root, factory) {
if (typeof exports === "object") {
// CommonJS环境下将factory函数执行的结果赋值给module.exports和exports
module.exports = exports = factory();
} else if (typeof define === "function" && define.amd) {
// AMD环境下使用define函数定义模块传入空的依赖数组和factory函数
define([], factory);
} else {
// 全局浏览器环境下将factory函数传入全局的CryptoJS如果存在执行并将结果赋值给全局的CryptoJS
root.CryptoJS = factory();
}
}(this, function () {
/*globals window, global, require*/
/**
* CryptoJS核心组件相关代码
*/
var CryptoJS = CryptoJS || (function (Math, undefined) {
var crypto;
// 尝试从浏览器的window对象获取crypto属性作为加密相关的原生对象适用于普通浏览器环境
if (typeof window!== 'undefined' && window.crypto) {
crypto = window.crypto;
}
// 尝试从浏览器的web worker的self对象获取crypto属性作为加密相关的原生对象适用于web worker环境
if (typeof self!== 'undefined' && self.crypto) {
crypto = self.crypto;
}
// 尝试从全局的globalThis对象获取crypto属性作为加密相关的原生对象适用于新的全局环境规范
if (typeof globalThis!== 'undefined' && globalThis.crypto) {
crypto = globalThis.crypto;
}
// 尝试从浏览器的window对象获取msCrypto属性作为加密相关的原生对象适用于IE 11等旧浏览器的实验性支持
if (!crypto && typeof window!== 'undefined' && window.msCrypto) {
crypto = window.msCrypto;
}
// 尝试从NodeJS的global对象获取crypto属性作为加密相关的原生对象适用于NodeJS环境
if (!crypto && typeof global!== 'undefined' && global.crypto) {
crypto = global.crypto;
}
// 尝试通过NodeJS的require函数导入'crypto'模块作为加密相关的原生对象适用于NodeJS环境
if (!crypto && typeof require === 'function') {
try {
crypto = require('crypto');
} catch (err) {}
}
/** /*
* Copies properties into this object. * 生成密码学安全的伪随机数的函数
* * 由于Math.random()在密码学上不安全所以需要使用原生加密模块提供的方法来生成
* @param {Object} properties The properties to mix in. */
* var cryptoSecureRandomInt = function () {
* @example if (crypto) {
* // 如果存在crypto对象且有getRandomValues方法适用于浏览器环境尝试使用该方法生成随机数
* MyType.mixIn({ if (typeof crypto.getRandomValues === 'function') {
* field: 'value' try {
* }); return crypto.getRandomValues(new Uint32Array(1))[0];
*/ } catch (err) {}
mixIn: function (properties) { }
for (var propertyName in properties) { // 如果存在crypto对象且有randomBytes方法适用于NodeJS环境尝试使用该方法生成随机数
if (properties.hasOwnProperty(propertyName)) { if (typeof crypto.randomBytes === 'function') {
this[propertyName] = properties[propertyName]; try {
} return crypto.randomBytes(4).readInt32LE();
} } catch (err) {}
}
}
// 如果无法通过上述方式获取安全的随机数,则抛出错误
throw new Error('Native crypto module could not be used to get secure random number.');
};
// 本地对Object.create的 polyfill实现如果原生不存在Object.create方法则使用以下函数替代
var create = Object.create || (function () {
function F() {}
return function (obj) {
var subtype;
F.prototype = obj;
subtype = new F();
F.prototype = null;
return subtype;
};
}());
/**
* CryptoJS的命名空间对象
*/
var C = {};
/**
* 库的命名空间对象用于存放库相关的属性和方法等
*/
var C_lib = C.lib = {};
/**
* 作为原型继承的基础对象提供了一系列用于创建扩展初始化混合属性以及克隆对象等方法
*/
var Base = C_lib.Base = (function () {
return {
/**
* 创建一个继承自当前对象的新对象并可传入要覆盖或添加的属性对象
* @param {Object} overrides 要复制到新对象的属性对象
* @return {Object} 新创建的继承了当前对象的对象
* @example
* var MyType = CryptoJS.lib.Base.extend({
* field: 'value',
* method: function () {}
* });
*/
extend: function (overrides) {
// 创建一个继承自当前对象的新对象通过Object.create或者polyfill的方式
var subtype = create(this);
// 如果有要覆盖或添加的属性,将其混入新对象
if (overrides) {
subtype.mixIn(overrides);
}
// 如果新对象没有自定义的init方法或者其init方法与父对象的init方法相同则创建一个默认的init方法用于调用父对象的init方法
if (!subtype.hasOwnProperty('init') || this.init === subtype.init) {
subtype.init = function () {
subtype.$super.init.apply(this, arguments);
};
}
// 设置新对象的init方法的原型为新对象本身以便在init方法中可以正确访问到新对象的属性等
subtype.init.prototype = subtype;
// 保存对父对象的引用,方便在新对象中调用父对象的方法等
subtype.$super = this;
return subtype;
},
/**
* 扩展当前对象并执行init方法传入的参数会传递给init方法返回新创建的对象
* @return {Object} 新创建并初始化后的对象
* @example
* var instance = MyType.create();
*/
create: function () {
var instance = this.extend();
instance.init.apply(instance, arguments);
return instance;
},
/**
* 初始化新创建的对象可在子类中重写此方法来添加对象创建时的逻辑
* @example
* var MyType = CryptoJS.lib.Base.extend({
* init: function () {
* //...
* }
* });
*/
init: function () { },
/**
* 将传入的属性对象的属性复制到当前对象中
* @param {Object} properties 要混入的属性对象
* @example
* MyType.mixIn({
* field: 'value'
* });
*/
mixIn: function (properties) {
for (var propertyName in properties) {
if (properties.hasOwnProperty(propertyName)) {
this[propertyName] = properties[propertyName];
}
}
// IE won't copy toString using the loop above // IE won't copy toString using the loop above
if (properties.hasOwnProperty('toString')) { if (properties.hasOwnProperty('toString')) {

@ -1,39 +1,63 @@
// 这个函数用于将给定的日期对象格式化为特定的字符串格式,格式为 "年/月/日 时:分:秒"
const formatTime = date => { const formatTime = date => {
const year = date.getFullYear() // 获取日期对象中的年份信息
const month = date.getMonth() + 1 const year = date.getFullYear();
const day = date.getDate() // 获取日期对象中的月份信息需要注意的是JavaScript中月份是从0开始计数的所以这里要加1得到实际的月份值
const hour = date.getHours() const month = date.getMonth() + 1;
const minute = date.getMinutes() // 获取日期对象中的日信息
const second = date.getSeconds() const day = date.getDate();
// 获取日期对象中的小时信息
const hour = date.getHours();
// 获取日期对象中的分钟信息
const minute = date.getMinutes();
// 获取日期对象中的秒信息
const second = date.getSeconds();
return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':') // 先将年、月、日组成的数组中的每个元素通过formatNumber函数进行格式化处理然后使用"/"将它们连接起来;
// 再将小时、分钟、秒组成的数组中的每个元素通过formatNumber函数进行格式化处理然后使用":"将它们连接起来;
// 最后将这两部分用空格连接起来,形成最终的格式化后的时间字符串并返回
return [year, month, day].map(formatNumber).join('/') +'' + [hour, minute, second].map(formatNumber).join(':');
} }
// 这个函数用于将数字格式化为固定长度的字符串格式,如果数字是个位数,则在前面添加"0"
const formatNumber = n => { const formatNumber = n => {
n = n.toString() // 将传入的参数转换为字符串类型
return n[1] ? n : '0' + n n = n.toString();
// 如果字符串长度大于1说明不是个位数直接返回原字符串否则在字符串前面添加"0"后返回
return n[1]? n : '0' + n;
} }
// 这个函数用于对HTML内容字符串进行样式相关的格式化处理主要是调整图片和表格单元格的样式
const formatHtml = content => { const formatHtml = content => {
content = content.replace(/\<img/gi, '<img style="width:100% !important;height:auto !important;margin:0;display:flex;" '); // 查找HTML内容中所有的<img>标签并给它们添加特定的内联样式设置宽度为100%、高度自适应、外边距为0以及以flex布局显示
content = content.replace(/\<img/gi, '<img style="width:100%!important;height:auto!important;margin:0;display:flex;" ');
// 查找HTML内容中所有的<td>标签,并给它们添加特定的内联样式,设置单元格间距、内边距、边框等样式属性,使其更符合特定的布局需求
content = content.replace(/\<td/gi, '<td cellspacing="0" cellpadding="0" border="0" style="display:block;vertical-align:top;margin: 0px; padding: 0px; border: 0px;outline-width:0px;" '); content = content.replace(/\<td/gi, '<td cellspacing="0" cellpadding="0" border="0" style="display:block;vertical-align:top;margin: 0px; padding: 0px; border: 0px;outline-width:0px;" ');
// 将HTML内容中所有的width=属性替换为sss=,这里可能是为了暂时去除原有的宽度设置,后续再统一处理或者有其他特殊用途
content = content.replace(/width=/gi, 'sss='); content = content.replace(/width=/gi, 'sss=');
// 将HTML内容中所有的height=属性替换为sss=作用类似于对width属性的处理可能是为了后续统一管理高度相关样式
content = content.replace(/height=/gi, 'sss='); content = content.replace(/height=/gi, 'sss=');
content = content.replace(/ \/\>/gi, ' style="max-width:100% !important;height:auto !important;margin:0;display:block;" \/\>'); // 查找HTML内容中所有的结束标签 />并给对应的标签添加特定的内联样式设置最大宽度为100%、高度自适应、外边距为0以及以块级元素显示然后替换原内容中的结束标签部分
content = content.replace(/ \/\>/gi, ' style="max-width:100%!important;height:auto!important;margin:0;display:block;" \/\>');
// 返回处理后的HTML内容字符串
return content; return content;
} }
// 这个函数的作用是移除购物车Tabbar上显示的数字可能是未读消息数量等提示信息通过调用微信小程序的API来实现
// 这里假设使用的是微信小程序的开发环境wx是小程序的全局对象removeTabBarBadge是其提供的用于移除Tabbar角标数字提示的方法
/** /**
* 移除购物车Tabbar的数字 * 移除购物车Tabbar的数字
*/ */
const removeTabBadge = () => { const removeTabBadge = () => {
wx.removeTabBarBadge({ wx.removeTabBarBadge({
index: 2 // 指定要移除角标的Tabbar选项卡的索引这里的2表示购物车对应的选项卡索引具体索引值可能根据小程序的实际布局而定
}) index: 2
})
} }
// 将上述定义的几个函数作为模块的属性进行导出,方便其他模块引入并使用这些功能函数
module.exports = { module.exports = {
formatTime: formatTime, formatTime: formatTime,
formatHtml: formatHtml, formatHtml: formatHtml,
removeTabBadge: removeTabBadge removeTabBadge: removeTabBadge
} }

@ -1,5 +1,10 @@
// 从指定路径 '../mixins/basic' 导入名为 'basic' 的模块(这里假设是一个混入对象,用于扩展组件的功能等)
import { basic } from '../mixins/basic'; import { basic } from '../mixins/basic';
// 从 '../mixins/observer/index' 路径导入名为 'observe' 的模块(可能用于实现数据观察等相关功能)
import { observe } from '../mixins/observer/index'; import { observe } from '../mixins/observer/index';
// 这个函数用于将源对象source中的某些属性按照给定的映射关系map复制到目标对象target
// 具体来说遍历映射关系中的每个键key如果源对象中存在对应的属性则将该属性值复制到目标对象的对应映射键名下
function mapKeys(source, target, map) { function mapKeys(source, target, map) {
Object.keys(map).forEach(key => { Object.keys(map).forEach(key => {
if (source[key]) { if (source[key]) {
@ -7,8 +12,14 @@ function mapKeys(source, target, map) {
} }
}); });
} }
// 定义名为 'VantComponent' 的函数,用于创建一个 Vant 风格的组件(可能是基于微信小程序等框架),接收一个配置对象 'vantOptions'(有默认值为空对象)
function VantComponent(vantOptions = {}) { function VantComponent(vantOptions = {}) {
// 创建一个空的对象 'options',用于存储经过处理后的组件配置选项,后续会逐步往里面添加和调整各项配置
const options = {}; const options = {};
// 使用'mapKeys' 函数将 'vantOptions' 中的部分属性按照特定的映射规则复制到 'options' 对象中
// 例如,将 'data' 属性映射为 'properties'(可能是适配不同框架对于数据定义的要求)等,实现属性名称的转换和整理
mapKeys(vantOptions, options, { mapKeys(vantOptions, options, {
data: 'data', data: 'data',
props: 'properties', props: 'properties',
@ -21,28 +32,45 @@ function VantComponent(vantOptions = {}) {
destroyed: 'detached', destroyed: 'detached',
classes: 'externalClasses' classes: 'externalClasses'
}); });
// 从 'vantOptions' 中获取'relation' 属性(可能用于定义组件间的关系等)
const { relation } = vantOptions; const { relation } = vantOptions;
if (relation) { if (relation) {
// 如果存在'relation' 属性,则将其合并到 'options.relations' 中,构建组件关系配置
// 这里将组件关系配置以特定的格式(路径和关系对象的形式)添加到'relations' 属性中,用于后续处理组件间的关联逻辑
options.relations = Object.assign(options.relations || {}, { options.relations = Object.assign(options.relations || {}, {
[`../${relation.name}/index`]: relation [`../${relation.name}/index`]: relation
}); });
} }
// add default externalClasses
// 如果 'options.externalClasses' 不存在(为假值),则初始化为一个空数组,用于存储组件外部可传入的类名相关配置
options.externalClasses = options.externalClasses || []; options.externalClasses = options.externalClasses || [];
// 向 'options.externalClasses' 数组中添加一个默认的类名 'custom-class',方便外部对组件样式进行定制
options.externalClasses.push('custom-class'); options.externalClasses.push('custom-class');
// add default behaviors
// 如果 'options.behaviors' 不存在(为假值),则初始化为一个空数组,'behaviors' 通常用于添加混入的功能模块等
options.behaviors = options.behaviors || []; options.behaviors = options.behaviors || [];
// 将之前导入的 'basic' 混入对象添加到 'options.behaviors' 数组中,为组件添加基础的通用功能扩展
options.behaviors.push(basic); options.behaviors.push(basic);
// map field to form-field behavior
// 如果 'vantOptions' 中存在 'field' 属性(可能表示组件与表单字段相关的某种标识)
if (vantOptions.field) { if (vantOptions.field) {
// 则向 'options.behaviors' 数组中添加 'wx://form-field'(可能是指向一个表单字段相关的行为模块,用于实现表单相关功能)
options.behaviors.push('wx://form-field'); options.behaviors.push('wx://form-field');
} }
// add default options
// 为组件配置添加默认的通用选项配置
options.options = { options.options = {
multipleSlots: true, multipleSlots: true, // 允许组件使用多个插槽,方便更灵活的内容布局和定制
addGlobalClass: true addGlobalClass: true // 允许添加全局类名,增强样式定制的灵活性
}; };
// 使用导入的 'observe' 模块(函数),根据 'vantOptions' 来对 'options' 进行一些数据观察等相关的处理(具体功能由 'observe' 函数实现)
observe(vantOptions, options); observe(vantOptions, options);
// 调用 'Component' 函数(这里假设是所在框架提供的用于注册组件的函数,比如微信小程序的组件注册机制),传入处理好的 'options' 配置对象来注册组件
Component(options); Component(options);
} }
// 将 'VantComponent' 函数作为模块的导出项,以便其他模块可以引入并使用该函数来创建 Vant 风格的组件
export { VantComponent }; export { VantComponent };

@ -1,14 +1,29 @@
// 函数 isDef 用于判断传入的值是否为已定义(即不是 undefined 也不是 null
// 返回一个布尔值,如果值既不是 undefined 也不是 null则返回 true否则返回 false
function isDef(value) { function isDef(value) {
return value !== undefined && value !== null; return value!== undefined && value!== null;
} }
// 函数 isObj 用于判断传入的参数是否为对象类型(包括普通对象、函数等符合 JavaScript 中对象定义的情况,但要排除 null
// 首先获取参数的类型(通过 typeof 操作符),然后判断参数不为 null 且类型是 'object' 或者 'function' 时,返回 true否则返回 false
function isObj(x) { function isObj(x) {
const type = typeof x; const type = typeof x;
return x !== null && (type === 'object' || type === 'function'); return x!== null && (type === 'object' || type === 'function');
} }
// 函数 isNumber 用于简单判断传入的字符串是否表示一个纯数字(只包含数字字符)
// 通过正则表达式 /^\d+$/ 来测试传入的字符串,如果匹配成功,表示字符串只由数字组成,返回 true否则返回 false
// 注意:该函数对于如 "12.3" 这样的浮点数字符串会判断为 false有一定的局限性仅适用于判断正整数形式的字符串
function isNumber(value) { function isNumber(value) {
return /^\d+$/.test(value); return /^\d+$/.test(value);
} }
// 函数 range 用于将给定的数字 num 限定在指定的最小值 min 和最大值 max 范围内
// 先通过 Math.max 取 num 和 min 中的较大值,确保返回值不会小于 min再通过 Math.min 取上述较大值与 max 中的较小值,确保返回值不会大于 max
// 最终返回限定在 [min, max] 范围内的值
function range(num, min, max) { function range(num, min, max) {
return Math.min(Math.max(num, min), max); return Math.min(Math.max(num, min), max);
} }
export { isObj, isDef, isNumber, range };
// 将 isObj、isDef、isNumber、range 这四个函数作为模块的导出项,以便其他模块可以引入并使用这些函数
export { isObj, isDef, isNumber, range };

@ -1,19 +1,33 @@
// 从相对路径 '../common/component' 导入名为 'VantComponent' 的模块,这里假设 'VantComponent' 是一个用于创建特定组件的函数(可能遵循某种组件化规范)
import { VantComponent } from '../common/component'; import { VantComponent } from '../common/component';
// 调用 'VantComponent' 函数来创建一个组件,传入一个配置对象,用于定义组件的各种属性、方法等相关信息
VantComponent({ VantComponent({
// 'props' 属性用于定义组件可接收的外部属性(类似 React 中的 props 概念),是一个对象,对象的每个键值对表示一个属性的定义
props: { props: {
// 'info' 属性,初始值设置为 null类型未明确限定可接收各种类型的值传入
info: null, info: null,
// 'name' 属性限定接收的值类型为字符串类型String
name: String, name: String,
//'size' 属性限定接收的值类型为字符串类型String
size: String, size: String,
// 'color' 属性限定接收的值类型为字符串类型String
color: String, color: String,
// 'customStyle' 属性限定接收的值类型为字符串类型String
customStyle: String, customStyle: String,
// 'classPrefix' 属性详细定义了其属性类型为字符串类型type: String并且设置了默认值为 'van-icon'
classPrefix: { classPrefix: {
type: String, type: String,
value: 'van-icon' value: 'van-icon'
} }
}, },
//'methods' 属性用于定义组件内部的方法,是一个对象,对象的每个键对应一个方法名,值为对应的方法函数
methods: { methods: {
// 定义名为 'onClick' 的方法,当组件被点击时可能会触发该方法
onClick() { onClick() {
// 通过 '$emit' 方法(这里假设是基于某种组件框架提供的事件触发机制,比如 Vue 中的自定义事件触发方式)向外触发一个名为 'click' 的自定义事件
// 这样外部使用该组件的地方可以监听这个 'click' 事件并执行相应的逻辑
this.$emit('click'); this.$emit('click');
} }
} }
}); });

@ -1,7 +1,13 @@
// 从相对路径 '../common/component' 导入名为 'VantComponent' 的模块。通常情况下,'VantComponent' 应该是一个用于创建特定组件的函数,遵循着特定的组件构建规则或框架规范
import { VantComponent } from '../common/component'; import { VantComponent } from '../common/component';
// 调用 'VantComponent' 函数来创建一个组件,向其传入一个配置对象,该配置对象用于定义组件的相关属性、行为等信息。
VantComponent({ VantComponent({
// 'props' 是配置对象中的一个属性,用于定义组件可接收的外部属性(类似于其他框架中的 props 概念),这里它是一个对象,对象内的每个键值对代表一个具体的外部属性定义。
props: { props: {
// 'info' 属性的定义,其初始值被设置为 null这意味着它可以接收任意类型的值外部使用组件时可以传入各种类型的数据给这个属性且若不传值则默认为 null。
info: null, info: null,
// 'customStyle' 属性的定义指定其接收的值类型为字符串类型String意味着外部向组件传入该属性时必须传入符合字符串格式的数据用于可能的样式定制等功能。
customStyle: String customStyle: String
} }
}); });

@ -1,22 +1,35 @@
// 使用 `export` 关键字将名为 `basic` 的变量作为模块的导出项,以便其他模块可以引入并使用它。
// `basic` 被赋值为通过 `Behavior` 函数创建的一个行为对象(在微信小程序等框架中,行为可以被组件引入来复用其中的属性、方法等功能)。
export const basic = Behavior({ export const basic = Behavior({
// `methods` 属性用于定义在该行为对象中包含的一系列方法,这些方法可以被使用了这个行为的组件所调用。
methods: { methods: {
// 定义名为 `$emit` 的方法,其功能是调用 `this.triggerEvent` 方法,并将接收到的所有参数原封不动地传递给 `triggerEvent` 方法。
// 这里的 `$emit` 可能是仿照一些框架(如 Vue 等)中触发自定义事件的方式来命名,用于在组件内部触发自定义事件,方便组件间通信等操作。
$emit() { $emit() {
this.triggerEvent.apply(this, arguments); this.triggerEvent.apply(this, arguments);
}, },
// 定义名为 `getRect` 的方法,该方法用于获取页面中指定选择器对应的元素的布局信息(边界矩形信息等),返回一个 `Promise` 对象。
getRect(selector, all) { getRect(selector, all) {
return new Promise(resolve => { return new Promise(resolve => {
// 使用微信小程序提供的 `wx.createSelectorQuery` 方法创建一个选择器查询对象,用于在页面中查找元素。
wx.createSelectorQuery() wx.createSelectorQuery()
.in(this)[all ? 'selectAll' : 'select'](selector) // 通过 `.in(this)` 将选择器查询的作用范围限定在当前组件实例内(如果有组件层级等情况时很有用)。
// 根据 `all` 参数的值来决定调用 `selectAll` 还是 `select` 方法,`select` 用于获取单个元素,`selectAll` 用于获取所有匹配选择器的元素。
.in(this)[all?'selectAll' :'select'](selector)
// 调用 `boundingClientRect` 方法来获取元素的边界矩形信息(如位置、大小等),传入一个回调函数,在获取到信息后进行处理。
.boundingClientRect(rect => { .boundingClientRect(rect => {
if (all && Array.isArray(rect) && rect.length) { // 如果 `all` 参数为 `true`,并且获取到的 `rect` 是数组且长度大于 0说明获取到了多个元素的矩形信息此时通过 `resolve` 方法将 `rect` 数组传递出去,用于后续处理(在 `Promise` 中表示成功获取到了期望的数据)。
resolve(rect); if (all && Array.isArray(rect) && rect.length) {
} resolve(rect);
if (!all && rect) { }
resolve(rect); // 如果 `all` 参数为 `false`,并且获取到了单个元素的 `rect` 信息(即 `rect` 不为空),同样通过 `resolve` 方法将 `rect` 传递出去,表示成功获取到了单个元素的矩形信息。
} if (!all && rect) {
}) resolve(rect);
}
})
// 最后调用 `exec` 方法执行整个查询操作,触发实际的查找和获取元素信息的流程。
.exec(); .exec();
}); });
} }
} }
}); });

@ -1,18 +1,34 @@
// 使用 `export` 关键字将名为 `button` 的变量作为模块的导出项,这样其他模块就能引入并使用它。
// `button` 被赋值为通过 `Behavior` 函数创建的一个行为对象(在微信小程序等框架里,行为对象可以被组件引入,以复用其中定义的各类属性、方法以及外部类名等内容)。
export const button = Behavior({ export const button = Behavior({
// `externalClasses` 属性用于定义该行为对象外部可传入的类名列表,这里定义了一个名为 `hover-class` 的外部类名。
// 外部类名意味着使用了这个行为的组件,在外部使用时可以通过这个类名来自定义相应的样式,增强组件样式的可定制性。
externalClasses: ['hover-class'], externalClasses: ['hover-class'],
// `properties` 属性用于定义该行为对象包含的一系列属性,这些属性可以被使用了这个行为的组件所接收和使用,相当于组件对外暴露的可配置属性。
properties: { properties: {
// `id` 属性,其值被限定为字符串类型(`String`),外部使用组件时传入的 `id` 值需要符合字符串的格式要求,可用于唯一标识组件等用途。
id: String, id: String,
// `lang` 属性,详细定义了它的类型为字符串类型(`type: String`),并且设置了默认值为 `'en'`。意味着如果外部没有传入该属性值,组件内部会默认使用 `'en'` 这个值,常用于设置语言相关的配置。
lang: { lang: {
type: String, type: String,
value: 'en' value: 'en'
}, },
// `businessId` 属性,限定接收的值类型为数字类型(`Number`),用于接收和处理与业务相关的数字标识等信息。
businessId: Number, businessId: Number,
// `sessionFrom` 属性,其值限定为字符串类型(`String`),可用于表示会话来源等相关信息,外部使用组件时需要传入符合字符串格式的数据。
sessionFrom: String, sessionFrom: String,
// `sendMessageTitle` 属性,限定为字符串类型(`String`),可能用于设置发送消息时的标题内容等,外部传入对应字符串来配置此功能相关信息。
sendMessageTitle: String, sendMessageTitle: String,
// `sendMessagePath` 属性,同样限定为字符串类型(`String`),也许用于指定发送消息的路径相关信息,由外部传入合适的字符串值进行配置。
sendMessagePath: String, sendMessagePath: String,
// `sendMessageImg` 属性,也是字符串类型(`String`),大概用于配置发送消息时所附带的图片相关信息,外部传入对应字符串进行定制。
sendMessageImg: String, sendMessageImg: String,
// `showMessageCard` 属性,其类型被定义为布尔类型(`Boolean`),用于控制是否显示消息卡片等相关功能的显示与否,外部传入 `true` 或 `false` 来进行相应设置。
showMessageCard: Boolean, showMessageCard: Boolean,
// `appParameter` 属性,限定为字符串类型(`String`),可用于传递应用相关的参数信息,外部传入对应字符串来配置相应功能。
appParameter: String, appParameter: String,
// `ariaLabel` 属性,同样是字符串类型(`String`),在无障碍访问相关的场景下,用于设置元素的可访问性标签等信息,方便屏幕阅读器等辅助工具使用。
ariaLabel: String ariaLabel: String
} }
}); });

@ -1,17 +1,30 @@
// 使用 `export` 关键字将名为 `link` 的变量作为模块的导出项,这样其他模块就可以引入并使用它。
// `link` 在这里被赋值为通过 `Behavior` 函数创建的一个行为对象,在微信小程序等框架中,行为对象能够被组件引入,以此复用其中定义的属性、方法等内容,来扩展组件的功能。
export const link = Behavior({ export const link = Behavior({
// `properties` 属性用于定义该行为对象所包含的组件可接收的外部属性,相当于对外暴露的配置项,外部使用组件时可以传入对应的值来配置组件的行为和状态。
properties: { properties: {
// `url` 属性,其值被限定为字符串类型(`String`),外部使用组件时需要传入符合字符串格式的数据,通常用于指定链接的地址,比如页面跳转的目标地址等。
url: String, url: String,
// `linkType` 属性,详细定义了它的类型为字符串类型(`type: String`),并且设置了默认值为 `'navigateTo'`。
// 这个属性大概率用于指定链接跳转的方式,默认是 `'navigateTo'`,表示以小程序中对应的页面跳转方式(类似 `wx.navigateTo`)进行跳转,外部也可以传入其他符合要求的跳转方式字符串来改变默认行为。
linkType: { linkType: {
type: String, type: String,
value: 'navigateTo' value: 'navigateTo'
} }
}, },
// `methods` 属性用于定义该行为对象中包含的方法,这些方法可以被使用了这个行为的组件所调用,用于实现特定的功能逻辑。
methods: { methods: {
// 定义名为 `jumpLink` 的方法,该方法用于触发链接跳转的操作,接收一个可选参数 `urlKey`,默认值为 `'url'`。
jumpLink(urlKey = 'url') { jumpLink(urlKey = 'url') {
// 从组件实例的数据(`this.data`)中获取由 `urlKey` 指定的属性值,并赋值给变量 `url`。
// 通常情况下,如果 `urlKey` 使用默认值 `'url'`,就是获取之前定义的 `url` 属性所对应的值,也就是目标跳转地址。
const url = this.data[urlKey]; const url = this.data[urlKey];
// 判断获取到的 `url` 是否存在(不为空字符串、`null`、`undefined` 等假值情况),如果存在,则执行页面跳转操作。
if (url) { if (url) {
// 通过 `wx` 对象(微信小程序提供的全局 API 对象),使用 `this.data.linkType` 指定的跳转方式来进行页面跳转,将获取到的 `url` 作为参数传递给对应的跳转方法。
// 例如,如果 `linkType` 的值为 `'navigateTo'`,就相当于执行 `wx.navigateTo({ url })`,实现跳转到指定的页面地址。
wx[this.data.linkType]({ url }); wx[this.data.linkType]({ url });
} }
} }
} }
}); });

@ -1,25 +1,37 @@
// 使用 `export` 关键字将名为 `openType` 的变量作为模块的导出项,使得其他模块能够引入并使用它。
// `openType` 被赋值为通过 `Behavior` 函数创建的一个行为对象,在微信小程序的开发框架中,行为对象可供组件引入,以复用其中定义好的属性、方法等内容,从而扩展组件的功能。
export const openType = Behavior({ export const openType = Behavior({
// `properties` 属性用于定义该行为对象所关联组件能够接收的外部属性,也就是组件对外暴露的可配置项,外部在使用组件时可以传入相应的值来设定组件的相关特性。
properties: { properties: {
// `openType` 属性,其值被限定为字符串类型(`String`),外部使用组件时需传入符合字符串格式的数据,该属性通常用于指定某种开放类型相关的设置,具体含义会依据微信小程序不同的开放能力场景而定。
openType: String openType: String
}, },
// `methods` 属性用于定义行为对象中包含的一系列方法,这些方法可以被引入该行为的组件所调用,用于实现特定的业务逻辑以及与外部的交互操作。
methods: { methods: {
// 定义名为 `bindGetUserInfo` 的方法,它接收一个 `event` 参数,该参数通常包含了微信小程序触发对应事件时传递过来的相关信息。
// 此方法的作用是,当触发获取用户信息的相关事件时,通过 `this.$emit` 向外(可能是组件的父组件等监听者)触发一个名为 `getuserinfo` 的自定义事件,并将 `event.detail`(即包含用户信息详情的数据)作为参数传递出去,方便外部进行相应的处理,比如获取到用户信息后进行展示或者存储等操作。
bindGetUserInfo(event) { bindGetUserInfo(event) {
this.$emit('getuserinfo', event.detail); this.$emit('getuserinfo', event.detail);
}, },
// 定义名为 `bindContact` 的方法,同样接收 `event` 参数,当触发与联系人相关的事件时,通过 `this.$emit` 向外触发名为 `contact` 的自定义事件,并传递 `event.detail`(包含联系人相关详细信息的数据),使得外部可以处理联系人相关的业务逻辑,例如更新联系人列表等操作。
bindContact(event) { bindContact(event) {
this.$emit('contact', event.detail); this.$emit('contact', event.detail);
}, },
// 定义名为 `bindGetPhoneNumber` 的方法,接收 `event` 参数,在触发获取手机号码相关事件时,使用 `this.$emit` 向外触发 `getphonenumber` 自定义事件,同时将 `event.detail`(包含手机号码相关信息的数据)传递出去,便于外部获取并使用该手机号码信息,比如进行验证或者存储等处理。
bindGetPhoneNumber(event) { bindGetPhoneNumber(event) {
this.$emit('getphonenumber', event.detail); this.$emit('getphonenumber', event.detail);
}, },
// 定义名为 `bindError` 的方法,接收 `event` 参数,当出现错误相关的事件触发时,通过 `this.$emit` 向外触发 `error` 自定义事件,将 `event.detail`(包含错误详细信息的数据)传递出去,方便外部根据错误信息进行相应的错误处理,例如提示用户或者记录日志等操作。
bindError(event) { bindError(event) {
this.$emit('error', event.detail); this.$emit('error', event.detail);
}, },
// 定义名为 `bindLaunchApp` 的方法,接收 `event` 参数,在触发启动应用相关的事件时,利用 `this.$emit` 向外触发 `launchapp` 自定义事件,把 `event.detail`(包含启动应用相关详细信息的数据)传递出去,使得外部可以根据启动情况做后续处理,比如判断启动是否成功等操作。
bindLaunchApp(event) { bindLaunchApp(event) {
this.$emit('launchapp', event.detail); this.$emit('launchapp', event.detail);
}, },
// 定义名为 `bindOpenSetting` 的方法,接收 `event` 参数,当触发打开设置相关的事件时,通过 `this.$emit` 向外触发 `opensetting` 自定义事件,同时传递 `event.detail`(包含打开设置相关详细信息的数据),方便外部知晓设置页面的打开情况以及进行相应的后续操作,例如判断用户是否进行了相关设置调整等操作。
bindOpenSetting(event) { bindOpenSetting(event) {
this.$emit('opensetting', event.detail); this.$emit('opensetting', event.detail);
}, }
} }
}); });

@ -1,39 +1,57 @@
// 定义一个全局变量 `cache`,用于缓存获取到的安全区域相关信息,初始值设置为 `null`。
let cache = null; let cache = null;
// 定义 `getSafeArea` 函数,该函数用于获取设备的安全区域相关信息,返回一个 `Promise` 对象,方便进行异步操作的处理。
function getSafeArea() { function getSafeArea() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (cache != null) { // 首先检查 `cache` 是否已经有值(不为 `null`),如果已经缓存了安全区域信息,则直接通过 `resolve` 将缓存的信息传递出去,避免重复获取。
if (cache!= null) {
resolve(cache); resolve(cache);
} }
// 如果 `cache` 为 `null`,说明还没有获取过安全区域信息,需要调用微信小程序的 `wx.getSystemInfo` 方法来获取系统相关信息。
else { else {
wx.getSystemInfo({ wx.getSystemInfo({
// `success` 回调函数会在成功获取到系统信息后被调用,它接收一个包含系统信息的对象作为参数,解构出其中需要的属性(`model` 表示设备型号,`screenHeight` 表示屏幕高度,`statusBarHeight` 表示状态栏高度)。
success: ({ model, screenHeight, statusBarHeight }) => { success: ({ model, screenHeight, statusBarHeight }) => {
// 通过正则表达式判断设备型号是否匹配 `iphone x`(不区分大小写),用于识别是否为 iPhone X 系列设备,将结果赋值给 `iphoneX` 变量。
const iphoneX = /iphone x/i.test(model); const iphoneX = /iphone x/i.test(model);
// 判断设备型号是否匹配 `iPhone11`(不区分大小写)并且屏幕高度是否为 `812`,用于识别特定的 iPhone 11 相关机型,将结果赋值给 `iphoneNew` 变量。
const iphoneNew = /iPhone11/i.test(model) && screenHeight === 812; const iphoneNew = /iPhone11/i.test(model) && screenHeight === 812;
// 将安全区域相关信息缓存到 `cache` 变量中,包括是否为 iPhone X 系列或特定 iPhone 11 机型(通过 `iphoneX` 或 `iphoneNew` 判断)以及状态栏高度信息。
cache = { cache = {
isIPhoneX: iphoneX || iphoneNew, isIPhoneX: iphoneX || iphoneNew,
statusBarHeight statusBarHeight
}; };
// 通过 `resolve` 将缓存好的安全区域信息传递出去,用于后续的处理。
resolve(cache); resolve(cache);
}, },
// 如果获取系统信息失败,调用 `reject`,将错误信息传递出去,由调用 `getSafeArea` 函数的地方进行相应的错误处理。
fail: reject fail: reject
}); });
} }
}); });
} }
// 使用 `export` 关键字导出一个名为 `safeArea` 的函数,该函数返回一个通过 `Behavior` 创建的行为对象,用于在微信小程序组件中实现与安全区域相关的功能和属性配置。
export const safeArea = ({ safeAreaInsetBottom = true, safeAreaInsetTop = false } = {}) => Behavior({ export const safeArea = ({ safeAreaInsetBottom = true, safeAreaInsetTop = false } = {}) => Behavior({
// `properties` 属性用于定义行为对象关联的组件可接收的外部属性,这里定义了两个与安全区域插入相关的布尔类型属性。
properties: { properties: {
// `safeAreaInsetTop` 属性,其类型为布尔类型(`Boolean`),默认值由传入 `safeArea` 函数的参数 `safeAreaInsetTop` 决定(如果未传则默认为 `false`),用于控制是否在顶部插入安全区域相关的样式或布局调整等。
safeAreaInsetTop: { safeAreaInsetTop: {
type: Boolean, type: Boolean,
value: safeAreaInsetTop value: safeAreaInsetTop
}, },
// `safeAreaInsetBottom` 属性,同样是布尔类型(`Boolean`),默认值由传入 `safeArea` 函数的参数 `safeAreaInsetBottom` 决定(如果未传则默认为 `true`),用于控制是否在底部插入安全区域相关的样式或布局调整等。
safeAreaInsetBottom: { safeAreaInsetBottom: {
type: Boolean, type: Boolean,
value: safeAreaInsetBottom value: safeAreaInsetBottom
} }
}, },
// `created` 生命周期钩子函数,在组件被创建时会被调用,这里用于获取安全区域信息并设置到组件的 `data` 中,以便组件后续使用这些信息进行相应的布局或样式处理。
created() { created() {
// 调用 `getSafeArea` 函数获取安全区域信息,当获取成功后(`Promise` 状态变为 `resolved`),解构出 `isIPhoneX`(是否为 iPhone X 系列等相关机型)和 `statusBarHeight`(状态栏高度)信息,并通过 `this.set`(这里假设是微信小程序组件中用于设置数据的方法)将这些信息设置到组件的数据中。
getSafeArea().then(({ isIPhoneX, statusBarHeight }) => { getSafeArea().then(({ isIPhoneX, statusBarHeight }) => {
this.set({ isIPhoneX, statusBarHeight }); this.set({ isIPhoneX, statusBarHeight });
}); });
} }
}); });

@ -1,27 +1,50 @@
// 使用 `export` 关键字将名为 `touch` 的变量作为模块的导出项,这样其他模块就能引入并使用它。
// `touch` 被赋值为通过 `Behavior` 函数创建的一个行为对象,在微信小程序等框架里,行为对象可被组件引入,以复用其中定义的方法等内容,进而实现相应的触摸交互相关功能。
export const touch = Behavior({ export const touch = Behavior({
// `methods` 属性用于定义该行为对象中包含的一系列方法,这些方法可被使用了这个行为的组件所调用,用于处理触摸相关的事件逻辑。
methods: { methods: {
// 定义名为 `touchStart` 的方法,它接收一个 `event` 参数,该参数包含了触摸开始事件触发时微信小程序传递过来的相关信息。
// 这个方法通常在触摸操作刚开始时被调用,用于初始化一些与触摸操作相关的变量,以便后续在触摸移动等过程中进行计算和判断。
touchStart(event) { touchStart(event) {
// 从 `event.touches` 数组中获取第一个触摸点的信息(一般在单指触摸情况下就是当前触摸点),并赋值给 `touch` 变量,方便后续获取触摸点的坐标等属性。
const touch = event.touches[0]; const touch = event.touches[0];
// 初始化 `direction` 属性为空字符串,表示触摸滑动方向尚未确定,后续会根据触摸移动情况来更新这个值,用于判断是水平滑动还是垂直滑动等情况。
this.direction = ''; this.direction = '';
// 将 `deltaX`(触摸点在 X 轴方向的偏移量)初始化为 0后续在触摸移动过程中会根据触摸点位置变化来更新该值。
this.deltaX = 0; this.deltaX = 0;
// 将 `deltaY`(触摸点在 Y 轴方向的偏移量)初始化为 0同样会在触摸移动时根据实际情况进行更新。
this.deltaY = 0; this.deltaY = 0;
// 将 `offsetX`(触摸点在 X 轴方向相对起始位置的偏移绝对值)初始化为 0用于记录触摸移动过程中的偏移情况方便后续判断滑动方向等操作。
this.offsetX = 0; this.offsetX = 0;
// 将 `offsetY`(触摸点在 Y 轴方向相对起始位置的偏移绝对值)初始化为 0作用与 `offsetX` 类似,用于触摸移动相关的逻辑处理。
this.offsetY = 0; this.offsetY = 0;
// 获取触摸开始时触摸点的 X 轴坐标(相对于浏览器客户端窗口左上角的位置),并赋值给 `startX` 属性,用于后续计算触摸点在 X 轴方向的偏移量等操作。
this.startX = touch.clientX; this.startX = touch.clientX;
// 获取触摸开始时触摸点的 Y 轴坐标(相对于浏览器客户端窗口左上角的位置),并赋值给 `startY` 属性,用于后续计算触摸点在 Y 轴方向的偏移量等操作。
this.startY = touch.clientY; this.startY = touch.clientY;
}, },
// 定义名为 `touchMove` 的方法,同样接收 `event` 参数,该方法会在触摸点在屏幕上移动时被触发,用于实时更新与触摸移动相关的变量,以判断触摸滑动的方向等信息。
touchMove(event) { touchMove(event) {
// 从 `event.touches` 数组中获取当前第一个触摸点的信息(单指触摸时的当前触摸点),赋值给 `touch` 变量,以便获取当前触摸点的坐标。
const touch = event.touches[0]; const touch = event.touches[0];
// 计算触摸点在 X 轴方向相对于触摸开始时的偏移量,通过当前触摸点的 X 轴坐标减去触摸开始时的 `startX` 值来得到,并更新 `deltaX` 属性。
this.deltaX = touch.clientX - this.startX; this.deltaX = touch.clientX - this.startX;
// 计算触摸点在 Y 轴方向相对于触摸开始时的偏移量,用当前触摸点的 Y 轴坐标减去触摸开始时的 `startY` 值,然后更新 `deltaY` 属性。
this.deltaY = touch.clientY - this.startY; this.deltaY = touch.clientY - this.startY;
// 计算触摸点在 X 轴方向相对于起始位置的偏移绝对值,使用 `Math.abs` 函数获取 `deltaX` 的绝对值,并赋值给 `offsetX` 属性,这有助于后续比较 X 轴和 Y 轴方向的偏移情况来判断滑动方向。
this.offsetX = Math.abs(this.deltaX); this.offsetX = Math.abs(this.deltaX);
// 计算触摸点在 Y 轴方向相对于起始位置的偏移绝对值,通过 `Math.abs` 函数获取 `deltaY` 的绝对值,将结果赋值给 `offsetY` 属性,同样用于滑动方向的判断等逻辑。
this.offsetY = Math.abs(this.deltaY); this.offsetY = Math.abs(this.deltaY);
// 根据 `offsetX` 和 `offsetY` 的大小关系来确定触摸滑动的方向,将结果赋值给 `direction` 属性。
// 如果 `offsetX`X 轴方向偏移绝对值)大于 `offsetY`Y 轴方向偏移绝对值),说明触摸滑动更偏向水平方向,将 `direction` 设置为 `'horizontal'`
// 如果 `offsetX` 小于 `offsetY`,表明触摸滑动更偏向垂直方向,把 `direction` 设置为 `'vertical'`
// 如果 `offsetX` 和 `offsetY` 相等,意味着滑动方向不太明确或者没有明显的水平或垂直偏向,将 `direction` 设置为空字符串 `''`。
this.direction = this.direction =
this.offsetX > this.offsetY this.offsetX > this.offsetY
? 'horizontal' ? 'horizontal'
: this.offsetX < this.offsetY : this.offsetX < this.offsetY
? 'vertical' ? 'vertical'
: ''; : '';
} }
} }
}); });

@ -1,87 +1,126 @@
// 从相对路径 '../common/utils' 导入名为 'isObj' 的函数,该函数可能用于判断传入的值是否为对象类型,以便后续在代码中进行相关类型判断的逻辑处理。
import { isObj } from '../common/utils'; import { isObj } from '../common/utils';
// 定义一个函数 'getClassNames',它接收一个参数 'name',用于根据给定的名称生成一系列与过渡效果相关的类名字符串。
// 返回一个对象,对象的每个属性对应不同过渡阶段的类名组合,方便后续在组件样式相关的逻辑中使用这些类名来实现动画效果。
const getClassNames = (name) => ({ const getClassNames = (name) => ({
// 'enter' 阶段的类名组合,用于表示元素进入时的过渡状态,包含了多个类名,可能会在 CSS 中定义相应的样式来实现动画效果。
enter: `van-${name}-enter van-${name}-enter-active enter-class enter-active-class`, enter: `van-${name}-enter van-${name}-enter-active enter-class enter-active-class`,
// 'enter-to' 阶段的类名组合,通常用于表示元素进入过渡的目标状态,同样结合多个类名用于样式定义和动画实现。
'enter-to': `van-${name}-enter-to van-${name}-enter-active enter-to-class enter-active-class`, 'enter-to': `van-${name}-enter-to van-${name}-enter-active enter-to-class enter-active-class`,
// 'leave' 阶段的类名组合,对应元素离开时的过渡起始状态相关类名,通过这些类名关联的 CSS 样式来展示离开的动画效果。
leave: `van-${name}-leave van-${name}-leave-active leave-class leave-active-class`, leave: `van-${name}-leave van-${name}-leave-active leave-class leave-active-class`,
// 'leave-to' 阶段的类名组合,用于表示元素离开过渡的目标状态,配合 CSS 可以呈现完整的离开动画效果。
'leave-to': `van-${name}-leave-to van-${name}-leave-active leave-to-class leave-active-class` 'leave-to': `van-${name}-leave-to van-${name}-leave-active leave-to-class leave-active-class`
}); });
// 定义 'nextTick' 函数,它返回一个 Promise用于模拟下一次事件循环的执行时机通过设置一个短暂的定时器这里设置为每 20 分之一秒,即 50 毫秒,对应帧率 20fps来实现类似浏览器的下一次重绘时机的效果常用于等待 DOM 更新等异步操作场景。
const nextTick = () => new Promise(resolve => setTimeout(resolve, 1000 / 20)); const nextTick = () => new Promise(resolve => setTimeout(resolve, 1000 / 20));
// 定义 'transition' 函数,它返回一个通过 'Behavior' 创建的行为对象,该行为对象用于在微信小程序组件等环境中实现过渡动画相关的功能逻辑,并且可以接收一个参数'showDefaultValue' 用于设置默认显示状态。
export const transition = function (showDefaultValue) { export const transition = function (showDefaultValue) {
return Behavior({ return Behavior({
// 'properties' 属性用于定义该行为对象所关联组件能够接收的外部属性,也就是组件对外暴露的可配置项,外部使用组件时可以传入相应的值来控制组件的行为和外观等方面。
properties: { properties: {
// 'customStyle' 属性,其值被限定为字符串类型(`String`),外部可传入符合字符串格式的数据,用于自定义组件的样式,提供额外的样式定制灵活性。
customStyle: String, customStyle: String,
//'show' 属性,定义其类型为布尔类型(`Boolean`),默认值由传入 'transition' 函数的'showDefaultValue' 参数决定,并且设置了一个名为 'observer' 的回调函数 'observeShow',意味着当该属性的值发生变化时,会自动触发 'observeShow' 函数进行相应的处理,该属性通常用于控制组件的显示与隐藏状态。
show: { show: {
type: Boolean, type: Boolean,
value: showDefaultValue, value: showDefaultValue,
observer: 'observeShow' observer: 'observeShow'
}, },
// 'duration' 属性,其类型可以是数字类型(`Number`)或者对象类型(`Object`),默认值为 300同样设置了 'observer' 回调函数 'observeDuration',用于在属性值变化时执行相应逻辑,该属性可能用于控制过渡动画的时长相关设置。
duration: { duration: {
type: [Number, Object], type: [Number, Object],
value: 300, value: 300,
observer: 'observeDuration' observer: 'observeDuration'
}, },
// 'name' 属性,类型为字符串类型(`String`),默认值为 'fade',并且设置了 'observer' 回调函数 'updateClasses',当该属性值改变时会触发 'updateClasses' 函数,该属性大概率用于指定过渡动画的名称或者类型,不同名称可能对应不同的样式和动画效果。
name: { name: {
type: String, type: String,
value: 'fade', value: 'fade',
observer: 'updateClasses' observer: 'updateClasses'
} }
}, },
// 'data' 属性用于定义组件内部的数据状态,这些数据可以在组件的各个方法中使用和修改,用于记录组件的一些状态信息以及与动画相关的一些临时数据等。
data: { data: {
// 'type' 用于记录当前过渡的类型相关信息,初始值为空字符串,后续可能根据不同的操作(如显示、隐藏操作)来更新其值以表示相应的过渡状态。
type: '', type: '',
// 'inited' 用于标记组件是否已经完成初始化,初始值为 false在组件进行一些初始化操作如首次显示时会将其设置为 true。
inited: false, inited: false,
// 'display' 用于控制组件是否显示,初始值为 false根据'show' 属性以及过渡动画的执行情况来动态改变其值,以实现组件的显示隐藏效果。
display: false, display: false,
// 'classNames' 通过调用 'getClassNames' 函数,传入默认的 'fade' 作为参数来获取初始的与过渡相关的类名对象,后续会根据 'name' 属性的变化来更新这些类名,用于应用相应的动画样式。
classNames: getClassNames('fade') classNames: getClassNames('fade')
}, },
// 'attached' 是微信小程序组件生命周期中的一个钩子函数,会在组件被添加到页面时执行,在这里用于判断组件初始状态是否为显示状态,如果是则调用'show' 方法来展示组件并触发相应的过渡动画效果。
attached() { attached() {
if (this.data.show) { if (this.data.show) {
this.show(); this.show();
} }
}, },
//'methods' 属性用于定义该行为对象中包含的一系列方法,这些方法可以被使用了这个行为的组件所调用,用于实现各种与过渡动画相关的具体功能逻辑,如显示、隐藏操作以及过渡状态变化的处理等。
methods: { methods: {
// 'observeShow' 方法是'show' 属性的观察者函数,当'show' 属性的值发生变化时会被调用,根据新传入的 'value'(即'show' 属性的新值)来决定是显示组件(调用'show' 方法)还是隐藏组件(调用 'leave' 方法)。
observeShow(value) { observeShow(value) {
if (value) { if (value) {
this.show(); this.show();
} } else {
else {
this.leave(); this.leave();
} }
}, },
// 'updateClasses' 方法是 'name' 属性的观察者函数,当 'name' 属性的值改变时会被调用,通过调用 'getClassNames' 函数并传入新的 'name' 值来获取对应的类名对象,然后使用'set' 方法(这里假设是微信小程序组件中用于更新数据的方法)更新组件内部的 'classNames' 数据,以应用新的过渡动画相关类名样式。
updateClasses(name) { updateClasses(name) {
this.set({ this.set({
classNames: getClassNames(name) classNames: getClassNames(name)
}); });
}, },
//'show' 方法用于处理组件显示的逻辑,实现显示时的过渡动画效果,涉及到更新组件的数据状态以及按顺序执行多个异步操作来逐步应用不同阶段的动画类名,从而实现平滑的过渡动画展示。
show() { show() {
// 解构出组件数据中的 'classNames'(过渡相关类名对象)和 'duration'(过渡时长相关数据)属性,方便后续使用。
const { classNames, duration } = this.data; const { classNames, duration } = this.data;
const currentDuration = isObj(duration) ? duration.leave : duration; // 根据 'duration' 的类型判断当前过渡时长,如果 'duration' 是对象类型,则取其 'leave' 属性的值作为过渡时长,否则直接使用 'duration' 的值作为过渡时长,这样可以支持更灵活的时长设置方式(比如针对进入和离开动画设置不同时长)。
const currentDuration = isObj(duration)? duration.leave : duration;
// 创建一个立即resolved的Promise用于开始链式调用后续的异步操作确保按顺序执行动画相关的状态更新步骤。
Promise.resolve() Promise.resolve()
// 调用 'nextTick' 函数,等待下一次事件循环时机,模拟浏览器下一次重绘之前的操作,常用于确保在 DOM 更新后进行下一步操作,在这里是过渡动画的一个时间节点控制。
.then(nextTick) .then(nextTick)
// 使用'set' 方法更新组件的多个数据属性,包括将 'inited' 设置为 true表示组件已初始化'display' 设置为 true显示组件'classes' 设置为 'classNames.enter'(应用进入动画的起始类名),以及更新 'currentDuration'(过渡时长),实现显示动画的第一步状态更新。
.then(() => this.set({ .then(() => this.set({
inited: true, inited: true,
display: true, display: true,
classes: classNames.enter, classes: classNames.enter,
currentDuration currentDuration
})) }))
// 再次调用 'nextTick' 函数,等待下一个合适的时间节点,继续过渡动画的下一步操作。
.then(nextTick) .then(nextTick)
// 使用'set' 方法更新组件的 'classes' 属性为 'classNames['enter-to']',应用进入动画的目标类名,完成进入动画的完整过渡过程。
.then(() => this.set({ .then(() => this.set({
classes: classNames['enter-to'] classes: classNames['enter-to']
})); }));
}, },
// 'leave' 方法用于处理组件隐藏的逻辑,实现隐藏时的过渡动画效果,同样涉及到更新组件数据状态以及通过多个异步操作配合定时器等方式来逐步应用不同阶段的动画类名,以实现平滑的过渡动画隐藏效果。
leave() { leave() {
const { classNames, duration } = this.data; const { classNames, duration } = this.data;
const currentDuration = isObj(duration) ? duration.leave : duration; const currentDuration = isObj(duration)? duration.leave : duration;
Promise.resolve() Promise.resolve()
.then(nextTick) .then(nextTick)
// 使用'set' 方法更新组件的 'classes' 属性为 'classNames.leave'(应用离开动画的起始类名),同时更新 'currentDuration'(过渡时长),开启隐藏动画的第一步操作。
.then(() => this.set({ .then(() => this.set({
classes: classNames.leave, classes: classNames.leave,
currentDuration currentDuration
})) }))
// 设置一个定时器,在 'currentDuration'(过渡时长)之后调用 'onTransitionEnd' 方法,用于在动画结束后执行一些收尾操作,比如完全隐藏组件等。
.then(() => setTimeout(() => this.onTransitionEnd(), currentDuration)) .then(() => setTimeout(() => this.onTransitionEnd(), currentDuration))
// 再次调用 'nextTick' 函数,等待下一个合适的时间节点,进行隐藏动画后续的类名更新操作。
.then(nextTick) .then(nextTick)
// 使用'set' 方法更新组件的 'classes' 属性为 'classNames['leave-to']'(应用离开动画的目标类名),完成隐藏动画的完整过渡过程。
.then(() => this.set({ .then(() => this.set({
classes: classNames['leave-to'] classes: classNames['leave-to']
})); }));
}, },
// 'onTransitionEnd' 方法在过渡动画结束时被调用,用于处理动画结束后的相关逻辑,比如根据组件当前的'show' 属性值决定是否彻底隐藏组件(将 'display' 属性设置为 false并且通过 '$emit' 方法(这里假设是微信小程序组件中用于触发自定义事件的方式)向外触发一个名为 'transitionEnd' 的自定义事件,方便外部组件监听该事件并进行相应的后续操作。
onTransitionEnd() { onTransitionEnd() {
if (!this.data.show) { if (!this.data.show) {
this.set({ display: false }); this.set({ display: false });
@ -90,4 +129,4 @@ export const transition = function (showDefaultValue) {
} }
} }
}); });
}; };

@ -1,74 +1,108 @@
// 从相对路径 '../common/component' 导入名为 'VantComponent' 的模块,这里假设 'VantComponent' 是一个用于创建特定组件的函数,遵循着相应的组件构建规范或框架约定。
import { VantComponent } from '../common/component'; import { VantComponent } from '../common/component';
// 调用 'VantComponent' 函数来创建一个组件,传入一个配置对象,该对象用于定义组件的各种属性、数据、监听以及方法等相关信息,以此来定制组件的行为和功能。
VantComponent({ VantComponent({
// 'field' 属性设置为 true这里可能暗示该组件与表单字段相关联或许会参与表单相关的交互逻辑比如在表单提交时的验证、取值等操作具体功能依赖于所在框架的实现方式。
field: true, field: true,
// 'classes' 属性定义了一个外部可传入的类名列表,这里包含了 'icon-class',意味着外部在使用这个组件时,可以通过传入这个类名来定制组件中与图标相关的样式,增强组件样式的可定制性。
classes: ['icon-class'], classes: ['icon-class'],
// 'props' 属性用于定义组件可接收的外部属性(类似于其他框架中的 props 概念),是一个对象,对象中的每个键值对代表一个具体的外部属性及其相关配置,包括类型、默认值等信息。
props: { props: {
// 'value' 属性,其值被限定为数字类型(`Number`),外部使用组件时需要传入符合数字格式的数据,该属性通常用于存储组件的一个数值相关的值,具体含义根据组件功能而定,可能是评分值之类的关键数据。
value: Number, value: Number,
//'readonly' 属性,类型为布尔类型(`Boolean`),用于控制组件是否处于只读状态,外部传入 `true` 或 `false` 来决定用户是否能够对组件进行操作,例如在展示评分但不允许修改的场景下可以设置为 `true`。
readonly: Boolean, readonly: Boolean,
// 'disabled' 属性,同样是布尔类型(`Boolean`),用于设置组件是否处于禁用状态,当设置为 `true` 时,用户通常不能与组件进行交互操作,常用于根据业务逻辑限制组件可用性的情况。
disabled: Boolean, disabled: Boolean,
// 'allowHalf' 属性,布尔类型(`Boolean`),可能用于控制组件是否允许出现半分值之类的情况,比如评分组件中是否支持打半分,具体功能取决于组件的业务逻辑设计。
allowHalf: Boolean, allowHalf: Boolean,
//'size' 属性,详细定义了它的类型为数字类型(`type: Number`),并且设置了默认值为 `20`,大概率用于控制组件中某个元素(可能是图标等)的尺寸大小,外部也可以传入其他数字来改变默认的尺寸设置。
size: { size: {
type: Number, type: Number,
value: 20 value: 20
}, },
// 'icon' 属性,限定接收的值类型为字符串类型(`String`),默认值为 `'star'`,很可能用于指定组件中表示某种状态(如选中状态)的图标名称,方便通过图标来展示组件的相关状态,外部可传入其他字符串来更换图标。
icon: { icon: {
type: String, type: String,
value: 'star' value: 'star'
}, },
// 'voidIcon' 属性,也是字符串类型(`String`),默认值为 `'star-o'`,可能用于表示组件中与 'icon' 相对应的另一种状态(如未选中状态)的图标名称,同样可由外部传入不同字符串进行定制。
voidIcon: { voidIcon: {
type: String, type: String,
value: 'star-o' value: 'star-o'
}, },
// 'color' 属性,类型为字符串类型(`String`),默认值为 `'#ffd21e'`,通常用于设置组件中某个元素(比如选中状态图标)的颜色,外部可以传入符合颜色表示规范的字符串来改变默认颜色。
color: { color: {
type: String, type: String,
value: '#ffd21e' value: '#ffd21e'
}, },
// 'voidColor' 属性,同样是字符串类型(`String`),默认值为 `'#c7c7c7'`,大概率用于设置未选中状态相关元素的颜色,可通过外部传入字符串来进行颜色定制。
voidColor: { voidColor: {
type: String, type: String,
value: '#c7c7c7' value: '#c7c7c7'
}, },
// 'disabledColor' 属性,类型为字符串类型(`String`),默认值为 `'#bdbdbd'`,用于设定组件处于禁用状态时相关元素呈现的颜色,外部可传入合适字符串改变此颜色设置。
disabledColor: { disabledColor: {
type: String, type: String,
value: '#bdbdbd' value: '#bdbdbd'
}, },
// 'count' 属性,其类型为数字类型(`Number`),默认值为 `5`,很可能用于表示组件中某种元素的数量,比如评分组件中星星图标的数量,外部可以传入不同数字来调整数量。
count: { count: {
type: Number, type: Number,
value: 5 value: 5
} }
}, },
// 'data' 属性用于定义组件内部的数据状态,这些数据可以在组件的各个方法中使用和修改,这里定义了 'innerValue' 并初始化为 `0`,可能用于在组件内部临时存储和处理一些与外部 'value' 属性相关的值,或者用于记录组件的中间状态等。
data: { data: {
innerValue: 0 innerValue: 0
}, },
// 'watch' 属性用于定义对组件数据的监听逻辑,这里监听了 'value' 属性的变化,当 'value' 的值发生改变时,会执行对应的回调函数。
watch: { watch: {
value(value) { value(value) {
if (value !== this.data.innerValue) { // 在 'value' 的新值与组件内部的 'innerValue' 不相等时,说明 'value' 属性发生了实际的变化,此时通过 'this.set' 方法(这里假设是所在框架用于更新组件数据的方法)更新 'innerValue' 的值,使其与 'value' 保持一致,确保组件内部状态与外部传入的属性值同步。
if (value!== this.data.innerValue) {
this.set({ innerValue: value }); this.set({ innerValue: value });
} }
} }
}, },
//'methods' 属性用于定义组件内部可调用的方法,这些方法实现了组件的各种交互逻辑和业务功能,比如响应用户的操作等。
methods: { methods: {
// 定义名为 'onSelect' 的方法,该方法可能在用户进行选择操作(比如点击某个图标来选择评分等情况)时被触发,接收一个 'event' 参数,包含了触发事件时的相关信息。
onSelect(event) { onSelect(event) {
// 解构出组件内部的数据对象,方便后续访问组件的各个数据属性,例如判断组件是否处于禁用或只读状态等。
const { data } = this; const { data } = this;
// 从触发事件的目标元素(`event.currentTarget`)的数据集(`dataset`)中获取名为'score' 的数据,这里的'score' 可能代表用户选择的某个分值相关的数据,具体含义取决于组件的业务逻辑设计。
const { score } = event.currentTarget.dataset; const { score } = event.currentTarget.dataset;
if (!data.disabled && !data.readonly) { // 判断组件当前既不处于禁用状态(`!data.disabled`)也不处于只读状态(`!data.readonly`),即用户可以进行操作的情况下,执行以下逻辑。
if (!data.disabled &&!data.readonly) {
// 通过 'this.set' 方法更新组件内部的 'innerValue' 属性为用户选择的分值加 `1`,这里加 `1` 的操作可能与组件具体的分值计算逻辑有关,比如索引从 `0` 开始但分值从 `1` 开始计数等情况。
this.set({ innerValue: score + 1 }); this.set({ innerValue: score + 1 });
// 使用 '$emit' 方法(这里假设是所在框架用于触发自定义事件的机制)向外触发一个名为 'input' 的自定义事件,并将用户选择的分值加 `1` 作为参数传递出去,方便外部组件监听该事件获取用户选择的分值,常用于与表单等相关的双向数据绑定场景。
this.$emit('input', score + 1); this.$emit('input', score + 1);
// 同样使用 '$emit' 方法向外触发一个名为 'change' 的自定义事件,也将用户选择的分值加 `1` 作为参数传递出去,外部可以监听该事件来执行相应的业务逻辑,比如更新页面其他部分显示的总分等情况。
this.$emit('change', score + 1); this.$emit('change', score + 1);
} }
}, },
// 定义名为 'onTouchMove' 的方法,该方法可能在用户触摸屏幕并移动手指(比如在移动端进行滑动操作)时被触发,接收 'event' 参数,包含触摸移动事件相关的信息。
onTouchMove(event) { onTouchMove(event) {
// 从触摸事件对象的第一个触摸点(`event.touches[0]`)中解构出 'clientX'(触摸点的横坐标,相对于浏览器窗口左上角的位置)和 'clientY'(触摸点的纵坐标)属性,方便后续判断触摸点所在的位置。
const { clientX, clientY } = event.touches[0]; const { clientX, clientY } = event.touches[0];
// 调用组件内部的 'getRect' 方法(这里假设该方法用于获取指定选择器对应的元素的布局矩形信息,可能是基于所在框架提供的相关 API 实现),传入选择器 '.van-rate__icon' 并设置 `true` 表示获取所有匹配的元素,返回一个 `Promise`,在获取到元素信息后进行后续处理。
this.getRect('.van-rate__icon', true).then((list) => { this.getRect('.van-rate__icon', true).then((list) => {
// 对获取到的元素列表(`list`)进行排序操作,排序规则是根据每个元素的右边界(`right`)减去左边界(`left`)的值来升序排列,这样可以方便后续按照从左到右的顺序查找触摸点所在的元素。
const target = list const target = list
.sort(item => item.right - item.left) .sort(item => item.right - item.left)
// 使用 `find` 方法在排好序的元素列表中查找符合条件的元素,条件是触摸点的横坐标(`clientX`)在元素的左边界(`left`)和右边界(`right`)之间,并且纵坐标(`clientY`)在元素的上边界(`top`)和下边界(`bottom`)之间,找到的元素即为触摸点所在的目标元素。
.find(item => clientX >= item.left && .find(item => clientX >= item.left &&
clientX <= item.right && clientX <= item.right &&
clientY >= item.top && clientY >= item.top &&
clientY <= item.bottom); clientY <= item.bottom);
if (target != null) { // 如果找到了目标元素(`target` 不为 `null`),则调用 'onSelect' 方法,模拟用户点击该目标元素的操作逻辑,传入一个经过处理的事件对象(将原事件对象和目标元素合并,模拟点击事件中目标元素的相关信息),以便执行相应的选择和事件触发逻辑。
if (target!= null) {
this.onSelect(Object.assign({}, event, { currentTarget: target })); this.onSelect(Object.assign({}, event, { currentTarget: target }));
} }
}); });
} }
} }
}); });

@ -1,94 +1,185 @@
{ {
"globals": { "globals": {
"$t": true, // truefalse
"Component": true,
"ComponentPublicInstance": true, "$t": true, //
"ComputedRef": true,
"Debounce": true, "Component": true, // Vue
"EffectScope": true,
"InjectionKey": true, "ComponentPublicInstance": true, //
"PropType": true,
"Ref": true, "ComputedRef": true, //
"VNode": true,
"clearLoginInfo": true, "Debounce": true, //
"computed": true,
"configDefInfo": true, "EffectScope": true, //
"createApp": true,
"customRef": true, "InjectionKey": true, // 使
"defineAsyncComponent": true,
"defineComponent": true, "PropType": true, // props
"effectScope": true,
"encrypt": true, "Ref": true, //
"flatten": true,
"checkFileUrl": true, "VNode": true, // DOM
"formatConfigInfo": true,
"getCurrentInstance": true, "clearLoginInfo": true, //
"getCurrentScope": true,
"getLevels": true, "computed": true, //
"getUUID": true,
"h": true, "configDefInfo": true, //
"http": true,
"idList": true, "createApp": true, // Vue
"inject": true,
"isAuth": true, "customRef": true, // ref
"isEmail": true,
"isHtmlNull": true, "defineAsyncComponent": true, //
"isMobile": true,
"isPhone": true, "defineComponent": true, //
"isProxy": true,
"isQq": true, "effectScope": true, //
"isReactive": true,
"isReadonly": true, "encrypt": true, //
"isRef": true,
"isURL": true, "flatten": true, //
"markRaw": true,
"nextTick": true, "checkFileUrl": true, // URL
"onActivated": true,
"onBeforeMount": true, "formatConfigInfo": true, //
"onBeforeRouteLeave": true,
"onBeforeRouteUpdate": true, "getCurrentInstance": true, //
"onBeforeUnmount": true,
"onBeforeUpdate": true, "getCurrentScope": true, //
"onDeactivated": true,
"onErrorCaptured": true, "getLevels": true, //
"onMounted": true,
"onRenderTracked": true, "getUUID": true, // UUID
"onRenderTriggered": true,
"onScopeDispose": true, "h": true, // DOM
"onServerPrefetch": true,
"onUnmounted": true, "http": true, // HTTP
"onUpdated": true,
"provide": true, "idList": true, // ID
"reactive": true,
"readonly": true, "inject": true, //
"ref": true,
"resolveComponent": true, "isAuth": true, //
"shallowReactive": true,
"shallowReadonly": true, "isEmail": true, //
"shallowRef": true,
"toRaw": true, "isHtmlNull": true, // HTML
"toRef": true,
"toRefs": true, "isMobile": true, //
"treeDataTranslate": true,
"triggerRef": true, "isPhone": true, //
"unref": true,
"uploadFile": true, "isProxy": true, //
"useAttrs": true,
"useCommonStore": true, "isQq": true, // QQ
"scoreProdStore":true,
"useCssModule": true, "isReactive": true, //
"useCssVars": true,
"useLink": true, "isReadonly": true, //
"useRoute": true,
"useRouter": true, "isRef": true, // ref
"useSlots": true,
"useUserStore": true, "isURL": true, // URL
"useWebConfigStore": true,
"validHtmlLength": true, "markRaw": true, //
"validNoEmptySpace": true,
"watch": true, "nextTick": true, // DOM
"watchEffect": true,
"watchPostEffect": true, "onActivated": true, //
"watchSyncEffect": true
"onBeforeMount": true, //
"onBeforeRouteLeave": true, //
"onBeforeRouteUpdate": true, //
"onBeforeUnmount": true, //
"onBeforeUpdate": true, //
"onDeactivated": true, //
"onErrorCaptured": true, //
"onMounted": true, //
"onRenderTracked": true, //
"onRenderTriggered": true, //
"onScopeDispose": true, //
"onServerPrefetch": true, //
"onUnmounted": true, //
"onUpdated": true, //
"provide": true, //
"reactive": true, //
"readonly": true, //
"ref": true, //
"resolveComponent": true, //
"shallowReactive": true, //
"shallowReadonly": true, //
"shallowRef": true, //
"toRaw": true, //
"toRef": true, // ref
"toRefs": true, // ref
"treeDataTranslate": true, //
"triggerRef": true, // ref
"unref": true, // ref
"uploadFile": true, //
"useAttrs": true, // 使API
"useCommonStore": true, // 使API
"scoreProdStore": true, // 使API
"useCssModule": true, // 使CSSAPI
"useCssVars": true, // 使CSSAPI
"useLink": true, // 使API
"useRoute": true, // 使API
"useRouter": true, // 使API
"useSlots": true, // 使API
"useUserStore": true, // 使API
"useWebConfigStore": true, // 使API
"validHtmlLength": true, // HTML
"validNoEmptySpace": true, //
"watch": true, //
"watchEffect": true, //
"watchPostEffect": true, //
"watchSyncEffect": true //
} }
} }

@ -1,16 +1,33 @@
<!DOCTYPE html> <!DOCTYPE html>
<!-- 声明文档类型为HTML5 -->
<html> <html>
<head> <head>
<!-- 设置文档的字符编码为UTF-8确保正确显示各种语言的文字 -->
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<!-- 指定页面图标favicon通常是一个小图标在浏览器标签上显示 -->
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<!-- 指示IE使用最新的渲染引擎和Chrome Frame如果安装的话来呈现页面 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<!-- 配置移动设备上的视口viewport以确保页面在不同尺寸的屏幕上正确缩放 -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<!-- 引入一个空的script标签ID为qqMap可能是为了之后通过JavaScript动态加载腾讯地图API或其他相关代码 -->
<script id="qqMap" charset="utf-8"></script> <script id="qqMap" charset="utf-8"></script>
<!-- 指定页面使用的渲染器为Webkit这主要用于兼容某些旧版浏览器的行为 -->
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<!-- 页面标题,这里留空,实际项目中应根据具体需求填写 -->
<title></title> <title></title>
</head> </head>
<body> <body>
<!-- 定义一个div元素ID为app这是单页应用(SPA)中常见的挂载点用于Vue.js或React等框架的应用程序入口 -->
<div id="app"></div> <div id="app"></div>
<!-- 引入主JavaScript文件使用type="module"表明这是一个ES6模块。该文件路径为/src/main.js表示应用程序逻辑的起点 -->
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>
</body> </body>
</html> </html>

@ -1,22 +1,44 @@
# 定义一个server块它代表一个虚拟主机或站点。
server { server {
# 监听在HTTP标准端口80上这是未加密的HTTP流量的标准端口。
listen 80; listen 80;
# 指定该服务器块响应哪些域名的请求。这里是mini-admin.mall4j.com。
server_name mini-admin.mall4j.com; server_name mini-admin.mall4j.com;
# 开启Gzip压缩功能以减少传输的数据量提高页面加载速度。
gzip on; gzip on;
# 如果存在预压缩的静态资源(如.html.gz则优先提供这些压缩后的资源。
gzip_static on; gzip_static on;
# 定义处理所有请求的location块。
location / { location / {
# 尝试按照顺序查找文件:先找精确匹配的文件路径,然后是目录,最后尝试根路径。
try_files $uri $uri/ /; try_files $uri $uri/ /;
# 设置Web根目录的位置即网站文件存放的目录。
root /usr/share/nginx/html/dist; root /usr/share/nginx/html/dist;
# 当访问根路径时,默认提供的首页文件名。
index index.html; index index.html;
} }
# 当出现404错误找不到页面重定向到指定的404错误页面。
error_page 404 /404.html; error_page 404 /404.html;
location = /404-light.html {
# 特别指定当访问路径为/404-light.html时的处理方式。
location = /404-light.html {
# 这里没有额外配置,意味着它将按照默认规则处理此特定路径。
# 使用"="表示精确匹配只有当请求完全符合这个URL时才会触发。
} }
# 对于500系列的服务器错误重定向到指定的50x错误页面。
error_page 500 502 503 504 /50x.html; error_page 500 502 503 504 /50x.html;
location = /50x.html {
# 特别指定当访问路径为/50x.html时的处理方式。
location = /50x.html {
# 类似于上面的404-light.html配置这里也没有额外配置。
# "location = /50x.html"确保了只有当请求的是/50x.html时才会应用这个location块。
} }
} }

@ -1,66 +1,82 @@
{ {
"name": "mall4v", "name": "mall4v",
// npm
"private": true, "private": true,
//
"version": "0.0.0", "version": "0.0.0",
// 使ES
"type": "module", "type": "module",
"scripts": { "scripts": {
// 使pnpm
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
// 使Vite
"dev": "vite", "dev": "vite",
//
"dev:test": "vite --mode testing", "dev:test": "vite --mode testing",
//
"build": "vite build", "build": "vite build",
//
"build:test": "vite build --mode testing", "build:test": "vite build --mode testing",
// 使eslintsrc.js.vue
"lint": "eslint --ext .js,.vue src", "lint": "eslint --ext .js,.vue src",
// eslint
"lint:fix": "eslint --fix --ext .js,.vue src", "lint:fix": "eslint --fix --ext .js,.vue src",
//
"preview": "vite preview", "preview": "vite preview",
// lint
"lint:staged": "lint-staged" "lint:staged": "lint-staged"
}, },
// Node.jspnpm
"engines": { "engines": {
"node": ">=16", "node": ">=16",
"pnpm": ">=6" "pnpm": ">=6"
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "2.1.0", //
"@smallwei/avue": "^3.2.22", "@element-plus/icons-vue": "2.1.0", // Element Plus
"axios": "1.3.4", "@smallwei/avue": "^3.2.22", // Avue
"big.js": "6.2.1", "axios": "1.3.4", // HTTP
"browser-image-compression": "2.0.2", "big.js": "6.2.1", //
"crypto-js": "4.1.1", "browser-image-compression": "2.0.2", //
"echarts": "5.4.1", "crypto-js": "4.1.1", //
"element-plus": "2.3.6", "echarts": "5.4.1", //
"element-resize-detector": "1.2.4", "element-plus": "2.3.6", // Element Plus UI
"js-base64": "3.7.5", "element-resize-detector": "1.2.4", // DOM
"lodash": "4.17.21", "js-base64": "3.7.5", // Base64
"moment": "2.29.4", "lodash": "4.17.21", // JavaScript
"pinia": "2.0.33", "moment": "2.29.4", // 使date-fnsdayjs
"qs": "6.11.1", "pinia": "2.0.33", // Vue
"vue": "3.2.47", "qs": "6.11.1", //
"vue-cookies": "1.8.3", "vue": "3.2.47", // Vue
"vue-draggable-next": "2.1.1", "vue-cookies": "1.8.3", // Vuecookies
"vue-router": "4.1.6" "vue-draggable-next": "2.1.1", // Vue
"vue-router": "4.1.6" // Vue
}, },
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "^7.21.3", // 使
"@vitejs/plugin-vue": "^4.1.0", "@babel/eslint-parser": "^7.21.3", // Babel ESLint
"eslint": "^8.38.0", "@vitejs/plugin-vue": "^4.1.0", // ViteVue
"eslint-config-standard": "^17.0.0", "eslint": "^8.38.0", // ESLint
"eslint-plugin-import": "^2.27.5", "eslint-config-standard": "^17.0.0", // ESLint
"eslint-plugin-n": "^15.7.0", "eslint-plugin-import": "^2.27.5", // ESLintimport/export
"eslint-plugin-promise": "^6.1.1", "eslint-plugin-n": "^15.7.0", // ESLintNode.js
"eslint-plugin-vue": "^9.10.0", "eslint-plugin-promise": "^6.1.1", // ESLintPromise
"eslint-plugin-vue-scoped-css": "^2.4.0", "eslint-plugin-vue": "^9.10.0", // ESLintVue
"lint-staged": "13.2.2", "eslint-plugin-vue-scoped-css": "^2.4.0", // ESLintVueCSS
"sass": "^1.59.3", "lint-staged": "13.2.2", // git
"unplugin-auto-import": "^0.15.1", "sass": "^1.59.3", // Sass
"unplugin-vue-components": "^0.24.1", "unplugin-auto-import": "^0.15.1", // API
"vite": "^4.3.9", "unplugin-vue-components": "^0.24.1", // Vue
"vite-plugin-compression": "^0.5.1", "vite": "^4.3.9", //
"vite-plugin-eslint": "^1.8.1", "vite-plugin-compression": "^0.5.1", // Vite
"vite-plugin-svg-icons": "^2.0.1", "vite-plugin-eslint": "^1.8.1", // ViteESLint
"vue-eslint-parser": "^9.1.1" "vite-plugin-svg-icons": "^2.0.1", // Vite便使SVG
"vue-eslint-parser": "^9.1.1" // VueESLint
}, },
// *.js*.vue
"lint-staged": { "lint-staged": {
"*.{js,vue}": [ "*.{js,vue}": [
"eslint --fix" "eslint --fix"
] ]
} }
} }

@ -1,15 +1,29 @@
{ {
"compilerOptions": { "compilerOptions": {
// "commonjs"Node.js
"module": "commonjs", "module": "commonjs",
// JavaScriptECMAScript"es5"
"target": "es5", "target": "es5",
// source mapTypeScriptJavaScript
"sourceMap": true, "sourceMap": true,
// .d.ts
"skipLibCheck": true "skipLibCheck": true
}, },
// node_modulesTypeScript
"exclude": [ "exclude": [
"node_modules" "node_modules"
], ],
//
"include": [ "include": [
// TypeScript
"src/auto-import/components.d.ts", "src/auto-import/components.d.ts",
// TypeScript
"src/auto-import/imports.d.ts" "src/auto-import/imports.d.ts"
] ]
} }

@ -8,9 +8,9 @@
* *
*/ */
// 该类所属的包名表明其处于管理端相关的包下作为整个Web应用的启动类所在的位置
package com.yami.shop.admin; package com.yami.shop.admin;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@ -21,18 +21,45 @@ import org.springframework.context.annotation.ComponentScan;
/** /**
* @author lgh * @author lgh
* Spring BootWeb
* Spring Boot使
* ServletWAR
*/ */
// @SpringBootApplication是一个组合注解相当于同时使用了@Configuration、@EnableAutoConfiguration和@ComponentScan这三个注解
// 下面分别对其作用进行详细解释:
// @Configuration注解表明这个类是一个Java配置类可用于定义Spring容器中的Bean以及配置相关的信息类似于传统的XML配置文件的作用。
// @EnableAutoConfiguration注解开启Spring Boot的自动配置功能它会根据项目中添加的依赖以及一些默认的配置规则自动配置Spring应用的各种组件
// 例如自动配置数据源、Web相关组件等极大地减少了手动配置的工作量。
// @ComponentScan注解用于指定Spring要扫描的基础包路径在这里表示会扫描com.yami.shop及其子包下的所有组件比如使用@Component、@Service、@Repository、@Controller等注解标注的类
// 找到这些组件后会将它们纳入Spring容器进行管理使得它们能够在应用中被自动注入和使用。
@SpringBootApplication @SpringBootApplication
// 通过@ComponentScan注解指定Spring要扫描的基础包路径为com.yami.shop及其子包确保应用中的各种组件能被正确扫描并加载到Spring容器中进行管理。
@ComponentScan("com.yami.shop") @ComponentScan("com.yami.shop")
public class WebApplication extends SpringBootServletInitializer{ // @EnableCaching注解用于开启Spring的缓存功能当应用中需要使用缓存来提高性能比如缓存数据库查询结果、方法返回值等情况时启用此注解后
// 可以通过在相应的方法上使用缓存相关注解(如@Cacheable、@CachePut、@CacheEvict等来配置缓存策略。在这里表示应用开启了缓存功能后续可按需配置具体的缓存细节。
@EnableCaching
public class WebApplication extends SpringBootServletInitializer {
public static void main(String[] args) { /**
* Java
* SpringApplicationrunSpring BootWebApplication.classargs
* Spring Boot
* @ComponentScanWebWeb
*
* @param args
*/
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args); SpringApplication.run(WebApplication.class, args);
} }
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(WebApplication.class);
}
} /**
* WARServletTomcatJettySpring
* SpringApplicationBuilderWebApplication.class
* ServletSpring
*
*/
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(WebApplication.class);
}
}

@ -16,16 +16,76 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
/** /**
* AdminBeanConfigSpringBean便Spring使
* BeanID
*
* @author lanhai
*/
@Configuration
// @Configuration注解表明这个类是一个配置类Spring会扫描到这个类并根据其中定义的Bean配置方法来创建和管理相应的Bean对象。
@AllArgsConstructor
// @AllArgsConstructor注解会为这个类生成一个包含所有参数的构造函数方便依赖注入操作在这里用于注入AdminConfig实例。
public class AdminBeanConfig {
// 通过构造函数注入AdminConfig实例AdminConfig应该是包含了相关配置信息的类此处用于获取创建Snowflake实例所需的参数。
private final AdminConfig adminConfig;
/**
* snowflakeSpringSnowflakeBean
* SnowflakeID
* AdminConfigworkerIddatacenterIdSnowflakeID
*
* @return SnowflakeSpringID使
*/
@Bean
public Snowflake snowflake() {
return new Snowflake(adminConfig.getWorkerId(), adminConfig.getDatacenterId());
}
}
java
/*
* Copyright (c) 2018 - 2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.admin.config;
import cn.hutool.core.lang.Snowflake;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* AdminBeanConfigSpringBean便Spring使
* BeanID
*
* @author lanhai * @author lanhai
*/ */
@Configuration @Configuration
// @Configuration注解表明这个类是一个配置类Spring会扫描到这个类并根据其中定义的Bean配置方法来创建和管理相应的Bean对象
@AllArgsConstructor @AllArgsConstructor
// @AllArgsConstructor注解会为这个类生成一个包含所有参数的构造函数方便依赖注入操作在这里用于注入AdminConfig实例
public class AdminBeanConfig { public class AdminBeanConfig {
// 通过构造函数注入AdminConfig实例AdminConfig应该是包含了相关配置信息的类此处用于获取创建Snowflake实例所需的参数
private final AdminConfig adminConfig; private final AdminConfig adminConfig;
/**
* snowflakeSpringSnowflakeBean
* SnowflakeID
* AdminConfigworkerIddatacenterIdSnowflakeID
*
* @return SnowflakeSpringID使
*/
@Bean @Bean
public Snowflake snowflake() { public Snowflake snowflake() {
return new Snowflake(adminConfig.getWorkerId(), adminConfig.getDatacenterId()); return new Snowflake(adminConfig.getWorkerId(), adminConfig.getDatacenterId());
} }
} }

@ -1,5 +1,7 @@
```java
/* /*
* Copyright (c) 2018-2999 广 All rights reserved. * Copyright (c) 2018 - 2999 广 All rights reserved.
* *
* https://www.mall4j.com/ * https://www.mall4j.com/
* *
@ -15,25 +17,44 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* * AdminConfig
* Spring便使
*
* @author lgh * @author lgh
*/ */
@Data
@Component @Component
/**
* @ComponentSpringSpring
* 使
*/
@PropertySource("classpath:admin.properties") @PropertySource("classpath:admin.properties")
/**
* @PropertySourceadmin.properties
*/
@ConfigurationProperties(prefix = "admin") @ConfigurationProperties(prefix = "admin")
/**
* @ConfigurationProperties"admin"
*/
@Data
/**
* @DataLombokGetterSetter
* toStringequalshashCode便访
*/
public class AdminConfig { public class AdminConfig {
/** /**
* ID * ID
*/ * ID使Snowflake
private Integer datacenterId; * ID
*/
/** private Integer datacenterId;
* ID
*/
private Integer workerId;
/**
* ID
* ID
* ID
*/
private Integer workerId;
} }
```

@ -18,26 +18,39 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
/** /**
* Swagger使 * SwaggerSwagger
* 使Swagger
* @author LGH * @author LGH
*/ */
@Configuration @Configuration
public class SwaggerConfiguration { public class SwaggerConfiguration {
@Bean /**
public GroupedOpenApi baseRestApi() { * Rest API便Swagger
return GroupedOpenApi.builder() * "接口文档""com.yami"
.group("接口文档") *
.packagesToScan("com.yami").build(); * @return GroupedOpenApi
} */
@Bean
public GroupedOpenApi baseRestApi() {
return GroupedOpenApi.builder()
.group("接口文档")
.packagesToScan("com.yami")
.build();
}
@Bean /**
public OpenAPI springShopOpenApi() { * Spring ShopOpenAPI使
return new OpenAPI() * Swagger便使
.info(new Info().title("Mall4j接口文档") * @return OpenAPI
.description("Mall4j接口文档openapi3.0 接口,用于前端对接") */
.version("v0.0.1") @Bean
.license(new License().name("使用请遵守AGPL3.0授权协议").url("https://www.mall4j.com"))); public OpenAPI springShopOpenApi() {
} return new OpenAPI()
} .info(new Info().title("Mall4j接口文档")
.description("Mall4j接口文档openapi3.0 接口,用于前端对接")
.version("v0.0.1")
.license(new License().name("使用请遵守AGPL3.0授权协议").url("https://www.mall4j.com")));
}
}

@ -7,6 +7,7 @@
* *
* *
*/ */
package com.yami.shop.admin.config; package com.yami.shop.admin.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
@ -19,47 +20,77 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
/** /**
* xxl-job config * xxl-jobXXL-JOBXXL-JOB
* xxl-job *
* spring quartzquartzxxl-job *
* xxl-jobxxl-job * 使SpringQuartz
* xxl-jobxxl-job-adminxxl-job * 使QuartzQuartz
* XXL-JOBXXL-JOB
* XXL-JOBXXL-JOB
*
* 使XXL-JOB
* 1. XXL-JOBxxl-job-admin
* 2. xxl-job-admin
* 3. XXL-JOB
*
* @author FrozenWatermelon * @author FrozenWatermelon
* @date 2021/1/18 * @date 2021/1/18
*/ */
@Configuration @Configuration
public class XxlJobConfig { public class XxlJobConfig {
// 创建一个日志记录器用于记录与XXL-JOB配置相关的日志信息方便后续排查问题以及了解配置初始化等过程的情况。
private final Logger logger = LoggerFactory.getLogger(XxlJobConfig.class); private final Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
// 通过@Value注解从配置文件中读取xxl-job.admin.addresses属性值该值代表了XXL-JOB管理端的地址用于执行器与管理端进行通信连接。
@Value("${xxl-job.admin.addresses}") @Value("${xxl-job.admin.addresses}")
private String adminAddresses; private String adminAddresses;
// 通过@Value注解从配置文件中读取xxl-job.accessToken属性值该值作为访问令牌用于在执行器与XXL-JOB管理端通信时进行身份验证等安全相关操作。
@Value("${xxl-job.accessToken}") @Value("${xxl-job.accessToken}")
private String accessToken; private String accessToken;
// 通过@Value注解从配置文件中读取xxl-job.logPath属性值该值指定了定时任务执行日志的存储路径方便后续查看任务执行情况以及排查可能出现的问题。
@Value("${xxl-job.logPath}") @Value("${xxl-job.logPath}")
private String logPath; private String logPath;
// 通过@Value注解从配置文件中读取server.port属性值即当前项目服务所监听的端口号后续可能会基于此端口号来确定执行器相关的端口等信息这里是做了端口偏移使用
@Value("${server.port}") @Value("${server.port}")
private int port; private int port;
// 注入InetUtils对象该对象是Spring Cloud提供的一个工具类用于处理网络相关的操作在这里主要用于获取合适的IP地址
// 特别是在多网卡、容器内部署等复杂网络环境下能够帮助准确获取到执行器要绑定的IP地址避免IP地址获取不准确导致的通信问题。
@Autowired @Autowired
private InetUtils inetUtils; private InetUtils inetUtils;
// 以下是配置XXL-JOB执行器的方法目前被注释掉了原因在类的开头注释部分已经详细说明了。
// 如果要启用XXL-JOB执行定时任务需要取消这段代码的注释并确保已经正确配置好了XXL-JOB相关环境。
// @Bean注解表示该方法会创建一个Spring管理的Bean对象在这里就是创建一个XxlJobSpringExecutor类型的Bean
// 该对象是XXL-JOB在Spring环境下的执行器实现类负责与XXL-JOB管理端交互以及执行具体的定时任务等操作。
// @Bean // @Bean
// public XxlJobSpringExecutor xxlJobExecutor() { // public XxlJobSpringExecutor xxlJobExecutor() {
// //
// // 记录一条日志信息表示开始进行XXL-JOB配置初始化方便在启动项目时查看相关配置是否正常加载等情况。
// logger.info(">>>>>>>>>>> xxl-job config init."); // logger.info(">>>>>>>>>>> xxl-job config init.");
// // 创建一个XxlJobSpringExecutor实例用于后续配置并返回作为Spring管理的Bean对象。
// XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); // XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
// // 设置XXL-JOB管理端的地址让执行器知道要与哪个管理端进行通信连接这个地址就是从配置文件中读取到的adminAddresses的值。
// xxlJobSpringExecutor.setAdminAddresses(adminAddresses); // xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
// // 设置应用名称,这里固定设置为"mall4j"在XXL-JOB管理端可以通过这个名称来区分不同的执行器所属的应用方便管理和监控。
// xxlJobSpringExecutor.setAppname("mall4j"); // xxlJobSpringExecutor.setAppname("mall4j");
// // 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP // // 针对多网卡、容器内部署等情况,借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP
// // 通过调用InetUtils的findFirstNonLoopbackAddress方法获取到第一个非回环地址即有效的网络IP地址并设置为执行器绑定的IP地址
// // 确保执行器能够在复杂网络环境下正确地与XXL-JOB管理端进行通信。
// xxlJobSpringExecutor.setIp(inetUtils.findFirstNonLoopbackAddress().getHostAddress()); // xxlJobSpringExecutor.setIp(inetUtils.findFirstNonLoopbackAddress().getHostAddress());
// // 设置执行器监听的端口号这里采用了项目服务端口号port加上1000的方式来确定避免端口冲突等问题同时也方便统一管理端口分配。
// xxlJobSpringExecutor.setPort(port + 1000); // xxlJobSpringExecutor.setPort(port + 1000);
// // 设置访问令牌用于在执行器与XXL-JOB管理端通信时进行身份验证等安全相关操作这个值就是从配置文件中读取到的accessToken的值。
// xxlJobSpringExecutor.setAccessToken(accessToken); // xxlJobSpringExecutor.setAccessToken(accessToken);
// // 设置定时任务执行日志的存储路径方便后续查看任务执行情况以及排查可能出现的问题这个值就是从配置文件中读取到的logPath的值。
// xxlJobSpringExecutor.setLogPath(logPath); // xxlJobSpringExecutor.setLogPath(logPath);
// // 设置日志保留天数这里设置为3天表示定时任务执行日志只会保留最近3天的记录超过这个天数的旧日志会被自动清理
// // 这样可以避免日志文件过多占用磁盘空间,同时也能保证在一定时间范围内可以查看历史任务执行情况。
// xxlJobSpringExecutor.setLogRetentionDays(3); // xxlJobSpringExecutor.setLogRetentionDays(3);
// return xxlJobSpringExecutor; // return xxlJobSpringExecutor;
// } // }
} }

@ -7,6 +7,7 @@
* *
* *
*/ */
package com.yami.shop.admin.controller; package com.yami.shop.admin.controller;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
@ -43,6 +44,8 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* token
*
* @author FrozenWatermelon * @author FrozenWatermelon
* @date 2020/6/30 * @date 2020/6/30
*/ */
@ -50,51 +53,72 @@ import java.util.stream.Collectors;
@Tag(name = "登录") @Tag(name = "登录")
public class AdminLoginController { public class AdminLoginController {
// 注入TokenStore用于存储和获取用户登录相关的token信息比如生成token、从存储中获取token相关数据等操作。
@Autowired @Autowired
private TokenStore tokenStore; private TokenStore tokenStore;
// 注入SysUserService用于与系统用户相关的数据库操作比如根据用户名查询用户信息等。
@Autowired @Autowired
private SysUserService sysUserService; private SysUserService sysUserService;
// 注入SysMenuService用于与系统菜单相关的数据库操作例如查询系统菜单列表等此处主要用于获取权限相关信息。
@Autowired @Autowired
private SysMenuService sysMenuService; private SysMenuService sysMenuService;
// 注入PasswordCheckManager用于对用户输入的密码进行合法性、安全性等方面的检查比如验证密码是否符合规则、是否在一定时间内多次输错被限制登录等。
@Autowired @Autowired
private PasswordCheckManager passwordCheckManager; private PasswordCheckManager passwordCheckManager;
// 注入CaptchaService用于处理验证码相关的操作比如验证验证码是否正确、是否过期等。
@Autowired @Autowired
private CaptchaService captchaService; private CaptchaService captchaService;
// 注入PasswordManager用于对密码进行解密等相关操作比如将前端传来的加密密码进行解密以便后续进行密码验证。
@Autowired @Autowired
private PasswordManager passwordManager; private PasswordManager passwordManager;
/**
* token
*
* @param captchaAuthenticationDTO @Valid
* @return ServerResponseEntitytoken
*/
@PostMapping("/adminLogin") @PostMapping("/adminLogin")
@Operation(summary = "账号密码 + 验证码登录(用于后台登录)" , description = "通过账号/手机号/用户名密码登录") @Operation(summary = "账号密码 + 验证码登录(用于后台登录)", description = "通过账号/手机号/用户名密码登录")
public ServerResponseEntity<?> login( public ServerResponseEntity<?> login(
@Valid @RequestBody CaptchaAuthenticationDTO captchaAuthenticationDTO) { @Valid @RequestBody CaptchaAuthenticationDTO captchaAuthenticationDTO) {
// 登陆后台登录需要再校验一遍验证码 // 登陆后台登录需要再校验一遍验证码
// 创建CaptchaVO对象用于传递给验证码服务进行验证将前端传来的验证码验证信息设置进去。
CaptchaVO captchaVO = new CaptchaVO(); CaptchaVO captchaVO = new CaptchaVO();
captchaVO.setCaptchaVerification(captchaAuthenticationDTO.getCaptchaVerification()); captchaVO.setCaptchaVerification(captchaAuthenticationDTO.getCaptchaVerification());
// 调用验证码服务的验证方法传入CaptchaVO对象获取验证结果响应模型。
ResponseModel response = captchaService.verification(captchaVO); ResponseModel response = captchaService.verification(captchaVO);
// 如果验证不成功,即验证码有误或者已过期,返回相应的错误提示信息给前端。
if (!response.isSuccess()) { if (!response.isSuccess()) {
return ServerResponseEntity.showFailMsg("验证码有误或已过期"); return ServerResponseEntity.showFailMsg("验证码有误或已过期");
} }
// 根据用户名从数据库中查询系统用户信息,如果未查询到用户,说明账号不存在,抛出相应的异常提示账号或密码不正确。
SysUser sysUser = sysUserService.getByUserName(captchaAuthenticationDTO.getUserName()); SysUser sysUser = sysUserService.getByUserName(captchaAuthenticationDTO.getUserName());
if (sysUser == null) { if (sysUser == null) {
throw new YamiShopBindException("账号或密码不正确"); throw new YamiShopBindException("账号或密码不正确");
} }
// 半小时内密码输入错误十次已限制登录30分钟 // 半小时内密码输入错误十次已限制登录30分钟
// 先对前端传来的密码进行解密操作,以便后续与数据库中存储的密码进行比对验证。
String decryptPassword = passwordManager.decryptPassword(captchaAuthenticationDTO.getPassWord()); String decryptPassword = passwordManager.decryptPassword(captchaAuthenticationDTO.getPassWord());
passwordCheckManager.checkPassword(SysTypeEnum.ADMIN,captchaAuthenticationDTO.getUserName(), decryptPassword, sysUser.getPassword()); // 调用密码检查管理器,传入系统类型、用户名、解密后的密码以及数据库中存储的用户密码,进行密码验证,若不符合规则会抛出相应异常。
passwordCheckManager.checkPassword(SysTypeEnum.ADMIN, captchaAuthenticationDTO.getUserName(), decryptPassword, sysUser.getPassword());
// 不是店铺超级管理员,并且是禁用状态,无法登录 // 不是店铺超级管理员,并且是禁用状态,无法登录
if (Objects.equals(sysUser.getStatus(),0)) { if (Objects.equals(sysUser.getStatus(), 0)) {
// 若用户状态为禁用这里假设状态0表示禁用抛出异常提示未找到此用户信息此处实际意思应该是用户不可用
// 未找到此用户信息 // 未找到此用户信息
throw new YamiShopBindException("未找到此用户信息"); throw new YamiShopBindException("未找到此用户信息");
} }
// 创建用于存储在token中的用户信息对象用于后续生成token以及传递给前端展示相关用户信息。
UserInfoInTokenBO userInfoInToken = new UserInfoInTokenBO(); UserInfoInTokenBO userInfoInToken = new UserInfoInTokenBO();
userInfoInToken.setUserId(String.valueOf(sysUser.getUserId())); userInfoInToken.setUserId(String.valueOf(sysUser.getUserId()));
userInfoInToken.setSysType(SysTypeEnum.ADMIN.value()); userInfoInToken.setSysType(SysTypeEnum.ADMIN.value());
@ -102,23 +126,35 @@ public class AdminLoginController {
userInfoInToken.setPerms(getUserPermissions(sysUser.getUserId())); userInfoInToken.setPerms(getUserPermissions(sysUser.getUserId()));
userInfoInToken.setNickName(sysUser.getUsername()); userInfoInToken.setNickName(sysUser.getUsername());
userInfoInToken.setShopId(sysUser.getShopId()); userInfoInToken.setShopId(sysUser.getShopId());
// 存储token返回vo
// 调用token存储服务将用户信息存储并生成token相关信息获取包含token等详细信息的TokenInfoVO对象然后返回登录成功的响应结果给前端包含了token信息。
TokenInfoVO tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken); TokenInfoVO tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken);
return ServerResponseEntity.success(tokenInfoVO); return ServerResponseEntity.success(tokenInfoVO);
} }
/**
* IDID
*
*
* @param userId ID
* @return
*/
private Set<String> getUserPermissions(Long userId) { private Set<String> getUserPermissions(Long userId) {
List<String> permsList; List<String> permsList;
//系统管理员,拥有最高权限 // 系统管理员,拥有最高权限
if(userId == Constant.SUPER_ADMIN_ID){ if (userId == Constant.SUPER_ADMIN_ID) {
// 如果是系统超级管理员,查询所有的系统菜单列表,获取每个菜单对应的权限字符串,组成权限列表。
List<SysMenu> menuList = sysMenuService.list(Wrappers.emptyWrapper()); List<SysMenu> menuList = sysMenuService.list(Wrappers.emptyWrapper());
permsList = menuList.stream().map(SysMenu::getPerms).collect(Collectors.toList()); permsList = menuList.stream().map(SysMenu::getPerms).collect(Collectors.toList());
}else{ } else {
// 如果不是系统超级管理员,调用用户服务的方法查询该用户的所有权限信息,得到权限列表。
permsList = sysUserService.queryAllPerms(userId); permsList = sysUserService.queryAllPerms(userId);
} }
return permsList.stream().flatMap((perms)->{
// 将权限列表中的每个权限字符串进行分割(假设权限字符串之间以逗号分隔),然后扁平化处理,去除重复的权限,最终收集为一个权限集合返回。
return permsList.stream().flatMap((perms) -> {
if (StrUtil.isBlank(perms)) { if (StrUtil.isBlank(perms)) {
return null; return null;
} }
@ -126,4 +162,4 @@ public class AdminLoginController {
} }
).collect(Collectors.toSet()); ).collect(Collectors.toSet());
} }
} }

@ -27,38 +27,55 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
/** /**
* AreaControllerArea
* ID
* @author lgh on 2018/10/26. * @author lgh on 2018/10/26.
*/ */
@RestController @RestController
@RequestMapping("/admin/area") @RequestMapping("/admin/area")
public class AreaController { public class AreaController {
// 自动注入AreaService用于调用与地区相关的业务逻辑方法
@Autowired @Autowired
private AreaService areaService; private AreaService areaService;
/** /**
* *
* AreaPageParam
* AreaServicepageServerResponseEntity
* @param area
* @param page
* @return ServerResponseEntityIPage<Area>
*/ */
@GetMapping("/page") @GetMapping("/page")
@PreAuthorize("@pms.hasPermission('admin:area:page')") @PreAuthorize("@pms.hasPermission('admin:area:page')")
public ServerResponseEntity<IPage<Area>> page(Area area,PageParam<Area> page) { public ServerResponseEntity<IPage<Area>> page(Area area, PageParam<Area> page) {
IPage<Area> sysUserPage = areaService.page(page, new LambdaQueryWrapper<Area>()); IPage<Area> sysUserPage = areaService.page(page, new LambdaQueryWrapper<Area>());
return ServerResponseEntity.success(sysUserPage); return ServerResponseEntity.success(sysUserPage);
} }
/** /**
* *
* Area
* AreaServicelist使LambdaQueryWrapper
* ServerResponseEntity
* @param area
* @return ServerResponseEntityList<Area>
*/ */
@GetMapping("/list") @GetMapping("/list")
@PreAuthorize("@pms.hasPermission('admin:area:list')") @PreAuthorize("@pms.hasPermission('admin:area:list')")
public ServerResponseEntity<List<Area>> list(Area area) { public ServerResponseEntity<List<Area>> list(Area area) {
List<Area> areas = areaService.list(new LambdaQueryWrapper<Area>() List<Area> areas = areaService.list(new LambdaQueryWrapper<Area>()
.like(area.getAreaName() != null, Area::getAreaName, area.getAreaName())); .like(area.getAreaName()!= null, Area::getAreaName, area.getAreaName()));
return ServerResponseEntity.success(areas); return ServerResponseEntity.success(areas);
} }
/** /**
* id * id
* AreaServicelistByPidID
* ServerResponseEntity
* @param pid ID
* @return ServerResponseEntityList<Area>
*/ */
@GetMapping("/listByPid") @GetMapping("/listByPid")
public ServerResponseEntity<List<Area>> listByPid(Long pid) { public ServerResponseEntity<List<Area>> listByPid(Long pid) {
@ -67,7 +84,11 @@ public class AreaController {
} }
/** /**
* * ID
* IDAreaServicegetById
* ServerResponseEntity
* @param id ID
* @return ServerResponseEntityArea
*/ */
@GetMapping("/info/{id}") @GetMapping("/info/{id}")
@PreAuthorize("@pms.hasPermission('admin:area:info')") @PreAuthorize("@pms.hasPermission('admin:area:info')")
@ -77,12 +98,17 @@ public class AreaController {
} }
/** /**
* *
* ID
* AreaServiceremoveAreaCacheByParentIdID
* AreaServicesaveServerResponseEntity
* @param area @Valid
* @return ServerResponseEntityVoid
*/ */
@PostMapping @PostMapping
@PreAuthorize("@pms.hasPermission('admin:area:save')") @PreAuthorize("@pms.hasPermission('admin:area:save')")
public ServerResponseEntity<Void> save(@Valid @RequestBody Area area) { public ServerResponseEntity<Void> save(@Valid @RequestBody Area area) {
if (area.getParentId() != null) { if (area.getParentId()!= null) {
Area parentArea = areaService.getById(area.getParentId()); Area parentArea = areaService.getById(area.getParentId());
area.setLevel(parentArea.getLevel() + 1); area.setLevel(parentArea.getLevel() + 1);
areaService.removeAreaCacheByParentId(area.getParentId()); areaService.removeAreaCacheByParentId(area.getParentId());
@ -92,17 +118,23 @@ public class AreaController {
} }
/** /**
* *
* IDareaDb
*
* hasSameName
* AreaServiceupdateByIdIDServerResponseEntity
* @param area @Valid
* @return ServerResponseEntityVoid
*/ */
@PutMapping @PutMapping
@PreAuthorize("@pms.hasPermission('admin:area:update')") @PreAuthorize("@pms.hasPermission('admin:area:update')")
public ServerResponseEntity<Void> update(@Valid @RequestBody Area area) { public ServerResponseEntity<Void> update(@Valid @RequestBody Area area) {
Area areaDb = areaService.getById(area.getAreaId()); Area areaDb = areaService.getById(area.getAreaId());
// 判断当前省市区级别如果是1级、2级则不能修改级别不能修改成别人的下级 // 判断当前省市区级别如果是1级、2级则不能修改级别不能修改成别人的下级
if(Objects.equals(areaDb.getLevel(), AreaLevelEnum.FIRST_LEVEL.value()) && !Objects.equals(area.getLevel(),AreaLevelEnum.FIRST_LEVEL.value())){ if (Objects.equals(areaDb.getLevel(), AreaLevelEnum.FIRST_LEVEL.value()) &&!Objects.equals(area.getLevel(), AreaLevelEnum.FIRST_LEVEL.value())) {
throw new YamiShopBindException("不能改变一级行政地区的级别"); throw new YamiShopBindException("不能改变一级行政地区的级别");
} }
if(Objects.equals(areaDb.getLevel(),AreaLevelEnum.SECOND_LEVEL.value()) && !Objects.equals(area.getLevel(),AreaLevelEnum.SECOND_LEVEL.value())){ if (Objects.equals(areaDb.getLevel(), AreaLevelEnum.SECOND_LEVEL.value()) &&!Objects.equals(area.getLevel(), AreaLevelEnum.SECOND_LEVEL.value())) {
throw new YamiShopBindException("不能改变二级行政地区的级别"); throw new YamiShopBindException("不能改变二级行政地区的级别");
} }
hasSameName(area); hasSameName(area);
@ -112,7 +144,12 @@ public class AreaController {
} }
/** /**
* *
* IDAreaServicegetById
* AreaServiceremoveByIdID
* ServerResponseEntity
* @param id ID
* @return ServerResponseEntityVoid
*/ */
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('admin:area:delete')") @PreAuthorize("@pms.hasPermission('admin:area:delete')")
@ -123,14 +160,20 @@ public class AreaController {
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
/**
*
* LambdaQueryWrapperIDID
* AreaServicecount0
* @param area
*/
private void hasSameName(Area area) { private void hasSameName(Area area) {
long count = areaService.count(new LambdaQueryWrapper<Area>() long count = areaService.count(new LambdaQueryWrapper<Area>()
.eq(Area::getParentId, area.getParentId()) .eq(Area::getParentId, area.getParentId())
.eq(Area::getAreaName, area.getAreaName()) .eq(Area::getAreaName, area.getAreaName())
.ne(Objects.nonNull(area.getAreaId()) && !Objects.equals(area.getAreaId(), 0L), Area::getAreaId, area.getAreaId()) .ne(Objects.nonNull(area.getAreaId()) &&!Objects.equals(area.getAreaId(), 0L), Area::getAreaId, area.getAreaId())
); );
if (count > 0) { if (count > 0) {
throw new YamiShopBindException("该地区已存在"); throw new YamiShopBindException("该地区已存在");
} }
} }
} }

@ -26,73 +26,103 @@ import jakarta.validation.Valid;
import java.util.Objects; import java.util.Objects;
/** /**
* *
*
*
* @author lgh * @author lgh
*/ */
@RestController @RestController
@RequestMapping("/admin/attribute") @RequestMapping("/admin/attribute")
public class AttributeController { public class AttributeController {
// 注入ProdPropService用于与商品属性相关的业务逻辑处理例如查询、保存、更新、删除商品属性及其相关值等操作。
@Autowired @Autowired
private ProdPropService prodPropService; private ProdPropService prodPropService;
/** /**
* * ProdPropPageParam
*/ *
* @PreAuthorize"admin:attribute:page"访
*/
@GetMapping("/page") @GetMapping("/page")
@PreAuthorize("@pms.hasPermission('admin:attribute:page')") @PreAuthorize("@pms.hasPermission('admin:attribute:page')")
public ServerResponseEntity<IPage<ProdProp>> page(ProdProp prodProp,PageParam<ProdProp> page){ public ServerResponseEntity<IPage<ProdProp>> page(ProdProp prodProp, PageParam<ProdProp> page) {
prodProp.setRule(ProdPropRule.ATTRIBUTE.value()); // 设置商品属性的规则为属性类型这里假设ProdPropRule.ATTRIBUTE表示属性类型用于后续业务逻辑中区分不同类型的商品属性规则。
prodProp.setShopId(SecurityUtils.getSysUser().getShopId()); prodProp.setRule(ProdPropRule.ATTRIBUTE.value());
IPage<ProdProp> prodPropPage = prodPropService.pagePropAndValue(prodProp,page); // 设置商品属性所属的店铺ID通过SecurityUtils工具类获取当前登录用户所属的店铺ID确保查询的是当前店铺下的商品属性信息。
return ServerResponseEntity.success(prodPropPage); prodProp.setShopId(SecurityUtils.getSysUser().getShopId());
} // 调用ProdPropService的pagePropAndValue方法传入设置好的商品属性对象和分页参数获取分页后的商品属性信息结果集。
IPage<ProdProp> prodPropPage = prodPropService.pagePropAndValue(prodProp, page);
// 将查询到的分页商品属性信息封装在成功的响应实体中返回给前端。
return ServerResponseEntity.success(prodPropPage);
}
/** /**
* * IDID
*/ * @PreAuthorize"admin:attribute:info"访
@GetMapping("/info/{id}") */
@PreAuthorize("@pms.hasPermission('admin:attribute:info')") @GetMapping("/info/{id}")
public ServerResponseEntity<ProdProp> info(@PathVariable("id") Long id){ @PreAuthorize("@pms.hasPermission('admin:attribute:info')")
ProdProp prodProp = prodPropService.getById(id); public ServerResponseEntity<ProdProp> info(@PathVariable("id") Long id) {
return ServerResponseEntity.success(prodProp); // 调用ProdPropService的getById方法根据传入的商品属性ID从数据库中获取对应的商品属性对象。
} ProdProp prodProp = prodPropService.getById(id);
// 将获取到的商品属性对象封装在成功的响应实体中返回给前端。
return ServerResponseEntity.success(prodProp);
}
/** /**
* * @ValidProdProp
*/ * ID
@PostMapping * @PreAuthorize"admin:attribute:save"访
@PreAuthorize("@pms.hasPermission('admin:attribute:save')") */
public ServerResponseEntity<Void> save(@Valid ProdProp prodProp){ @PostMapping
prodProp.setRule(ProdPropRule.ATTRIBUTE.value()); @PreAuthorize("@pms.hasPermission('admin:attribute:save')")
prodProp.setShopId(SecurityUtils.getSysUser().getShopId()); public ServerResponseEntity<Void> save(@Valid ProdProp prodProp) {
prodPropService.saveProdPropAndValues(prodProp); // 设置商品属性的规则为属性类型这里假设ProdPropRule.ATTRIBUTE表示属性类型用于后续业务逻辑中区分不同类型的商品属性规则。
return ServerResponseEntity.success(); prodProp.setRule(ProdPropRule.ATTRIBUTE.value());
} // 设置商品属性所属的店铺ID通过SecurityUtils工具类获取当前登录用户所属的店铺ID确保保存的是当前店铺下的商品属性信息。
prodProp.setShopId(SecurityUtils.getSysUser().getShopId());
// 调用ProdPropService的saveProdPropAndValues方法将设置好的商品属性对象及其相关值保存到数据库中。
prodPropService.saveProdPropAndValues(prodProp);
// 返回表示操作成功的响应实体由于这里只是执行保存操作无需返回具体数据所以返回的是Void类型的成功响应。
return ServerResponseEntity.success();
}
/** /**
* * @ValidProdProp
*/ * IDID
@PutMapping * ID
@PreAuthorize("@pms.hasPermission('admin:attribute:update')") * @PreAuthorize"admin:attribute:update"访
public ServerResponseEntity<Void> update(@Valid ProdProp prodProp){ */
ProdProp dbProdProp = prodPropService.getById(prodProp.getPropId()); @PutMapping
if (!Objects.equals(dbProdProp.getShopId(), SecurityUtils.getSysUser().getShopId())) { @PreAuthorize("@pms.hasPermission('admin:attribute:update')")
throw new YamiShopBindException("没有权限获取该商品规格信息"); public ServerResponseEntity<Void> update(@Valid ProdProp prodProp) {
} // 根据传入的商品属性对象中的ID调用ProdPropService的getById方法从数据库中获取对应的商品属性对象用于后续权限判断等操作。
prodProp.setRule(ProdPropRule.ATTRIBUTE.value()); ProdProp dbProdProp = prodPropService.getById(prodProp.getPropId());
prodProp.setShopId(SecurityUtils.getSysUser().getShopId()); // 判断数据库中查询到的商品属性所属的店铺ID与当前登录用户所属的店铺ID是否一致若不一致则说明当前用户没有权限修改该商品属性信息抛出相应异常。
prodPropService.updateProdPropAndValues(prodProp); if (!Objects.equals(dbProdProp.getShopId(), SecurityUtils.getSysUser().getShopId())) {
return ServerResponseEntity.success(); throw new YamiShopBindException("没有权限获取该商品规格信息");
} }
// 设置商品属性的规则为属性类型这里假设ProdPropRule.ATTRIBUTE表示属性类型用于后续业务逻辑中区分不同类型的商品属性规则。
prodProp.setRule(ProdPropRule.ATTRIBUTE.value());
// 设置商品属性所属的店铺ID通过SecurityUtils工具类获取当前登录用户所属的店铺ID确保更新的是当前店铺下的商品属性信息。
prodProp.setShopId(SecurityUtils.getSysUser().getShopId());
// 调用ProdPropService的updateProdPropAndValues方法将设置好的商品属性对象及其相关值更新到数据库中。
prodPropService.updateProdPropAndValues(prodProp);
// 返回表示操作成功的响应实体由于这里只是执行更新操作无需返回具体数据所以返回的是Void类型的成功响应。
return ServerResponseEntity.success();
}
/** /**
* * ID
*/ * @PreAuthorize"admin:attribute:delete"访
@DeleteMapping("/{id}") */
@PreAuthorize("@pms.hasPermission('admin:attribute:delete')") @DeleteMapping("/{id}")
public ServerResponseEntity<Void> delete(@PathVariable Long id){ @PreAuthorize("@pms.hasPermission('admin:attribute:delete')")
prodPropService.deleteProdPropAndValues(id,ProdPropRule.ATTRIBUTE.value(),SecurityUtils.getSysUser().getShopId()); public ServerResponseEntity<Void> delete(@PathVariable Long id) {
return ServerResponseEntity.success(); // 调用ProdPropService的deleteProdPropAndValues方法传入商品属性ID、属性规则这里指定为属性类型以及当前登录用户所属的店铺ID执行删除操作。
} prodPropService.deleteProdPropAndValues(id, ProdPropRule.ATTRIBUTE.value(), SecurityUtils.getSysUser().getShopId());
} // 返回表示操作成功的响应实体由于这里只是执行删除操作无需返回具体数据所以返回的是Void类型的成功响应。
return ServerResponseEntity.success();
}
}

@ -25,33 +25,44 @@ import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import java.util.Objects; import java.util.Objects;
/** /**
* *
* *
* @author lgh * @author lgh
*/ */
@RestController @RestController
@RequestMapping("/admin/brand") @RequestMapping("/admin/brand")
public class BrandController { public class BrandController {
// 自动注入BrandService用于调用与品牌相关的业务逻辑方法
@Autowired @Autowired
private BrandService brandService; private BrandService brandService;
/** /**
* *
* BrandPageParam
* BrandServicepageLambdaQueryWrapper
* ServerResponseEntity
* @param brand
* @param page
* @return ServerResponseEntityIPage<Brand>
*/ */
@GetMapping("/page") @GetMapping("/page")
@PreAuthorize("@pms.hasPermission('admin:brand:page')") @PreAuthorize("@pms.hasPermission('admin:brand:page')")
public ServerResponseEntity<IPage<Brand>> page(Brand brand,PageParam<Brand> page) { public ServerResponseEntity<IPage<Brand>> page(Brand brand, PageParam<Brand> page) {
IPage<Brand> brands = brandService.page(page, IPage<Brand> brands = brandService.page(page,
new LambdaQueryWrapper<Brand>() new LambdaQueryWrapper<Brand>()
.like(StrUtil.isNotBlank(brand.getBrandName()), Brand::getBrandName, brand.getBrandName()).orderByAsc(Brand::getFirstChar)); .like(StrUtil.isNotBlank(brand.getBrandName()), Brand::getBrandName, brand.getBrandName())
.orderByAsc(Brand::getFirstChar));
return ServerResponseEntity.success(brands); return ServerResponseEntity.success(brands);
} }
/** /**
* * ID
* IDBrandServicegetById
* ServerResponseEntity
* @param id ID
* @return ServerResponseEntityBrand
*/ */
@GetMapping("/info/{id}") @GetMapping("/info/{id}")
@PreAuthorize("@pms.hasPermission('admin:brand:info')") @PreAuthorize("@pms.hasPermission('admin:brand:info')")
@ -61,13 +72,18 @@ public class BrandController {
} }
/** /**
* *
* BrandServicegetByBrandName
* BrandServicesave
* ServerResponseEntity
* @param brand @Valid
* @return ServerResponseEntityVoid
*/ */
@PostMapping @PostMapping
@PreAuthorize("@pms.hasPermission('admin:brand:save')") @PreAuthorize("@pms.hasPermission('admin:brand:save')")
public ServerResponseEntity<Void> save(@Valid Brand brand) { public ServerResponseEntity<Void> save(@Valid Brand brand) {
Brand dbBrand = brandService.getByBrandName(brand.getBrandName()); Brand dbBrand = brandService.getByBrandName(brand.getBrandName());
if (dbBrand != null) { if (dbBrand!= null) {
throw new YamiShopBindException("该品牌名称已存在"); throw new YamiShopBindException("该品牌名称已存在");
} }
brandService.save(brand); brandService.save(brand);
@ -75,13 +91,18 @@ public class BrandController {
} }
/** /**
* *
* BrandServicegetByBrandName
* IDBrandServiceupdateById
* ServerResponseEntity
* @param brand @Valid
* @return ServerResponseEntityVoid
*/ */
@PutMapping @PutMapping
@PreAuthorize("@pms.hasPermission('admin:brand:update')") @PreAuthorize("@pms.hasPermission('admin:brand:update')")
public ServerResponseEntity<Void> update(@Valid Brand brand) { public ServerResponseEntity<Void> update(@Valid Brand brand) {
Brand dbBrand = brandService.getByBrandName(brand.getBrandName()); Brand dbBrand = brandService.getByBrandName(brand.getBrandName());
if (dbBrand != null && !Objects.equals(dbBrand.getBrandId(), brand.getBrandId())) { if (dbBrand!= null &&!Objects.equals(dbBrand.getBrandId(), brand.getBrandId())) {
throw new YamiShopBindException("该品牌名称已存在"); throw new YamiShopBindException("该品牌名称已存在");
} }
brandService.updateById(brand); brandService.updateById(brand);
@ -89,7 +110,11 @@ public class BrandController {
} }
/** /**
* *
* IDBrandServicedeleteByBrand
* ServerResponseEntity
* @param id ID
* @return ServerResponseEntityVoid
*/ */
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('admin:brand:delete')") @PreAuthorize("@pms.hasPermission('admin:brand:delete')")
@ -97,5 +122,4 @@ public class BrandController {
brandService.deleteByBrand(id); brandService.deleteByBrand(id);
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
}
}

@ -25,10 +25,10 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
/** /**
* *
*
*
* @author lgh * @author lgh
* *
*/ */
@ -36,110 +36,154 @@ import java.util.Objects;
@RequestMapping("/prod/category") @RequestMapping("/prod/category")
public class CategoryController { public class CategoryController {
@Autowired // 注入CategoryService用于与商品分类相关的业务逻辑处理例如查询、保存、更新、删除分类等操作。
private CategoryService categoryService; @Autowired
private CategoryService categoryService;
/**
* /**
* @return * CategoryService
*/ * ID
@GetMapping("/table") * @PreAuthorize"prod:category:page"访
@PreAuthorize("@pms.hasPermission('prod:category:page')") *
public ServerResponseEntity<List<Category>> table(){ * @return ServerResponseEntity
List<Category> categoryMenuList = categoryService.tableCategory(SecurityUtils.getSysUser().getShopId()); */
return ServerResponseEntity.success(categoryMenuList); @GetMapping("/table")
} @PreAuthorize("@pms.hasPermission('prod:category:page')")
public ServerResponseEntity<List<Category>> table() {
/** // 调用CategoryService的tableCategory方法传入当前登录用户所属的店铺ID获取用于菜单页面展示的分类列表信息。
* List<Category> categoryMenuList = categoryService.tableCategory(SecurityUtils.getSysUser().getShopId());
*/ // 将获取到的分类列表信息封装在成功的响应实体中返回给前端。
@GetMapping("/info/{categoryId}") return ServerResponseEntity.success(categoryMenuList);
public ServerResponseEntity<Category> info(@PathVariable("categoryId") Long categoryId){ }
Category category = categoryService.getById(categoryId);
return ServerResponseEntity.success(category); /**
} * IDIDCategoryServicegetById
*
*
* @param categoryId
/** * @return ServerResponseEntity
* */
*/ @GetMapping("/info/{categoryId}")
@SysLog("保存分类") public ServerResponseEntity<Category> info(@PathVariable("categoryId") Long categoryId) {
@PostMapping Category category = categoryService.getById(categoryId);
@PreAuthorize("@pms.hasPermission('prod:category:save')") return ServerResponseEntity.success(category);
public ServerResponseEntity<Void> save(@RequestBody Category category){ }
category.setShopId(SecurityUtils.getSysUser().getShopId());
category.setRecTime(new Date()); /**
Category categoryName = categoryService.getOne(new LambdaQueryWrapper<Category>().eq(Category::getCategoryName,category.getCategoryName()) * Category
.eq(Category::getShopId,category.getShopId())); * IDID
if(Objects.nonNull(categoryName)){ * CategoryServicesaveCategory
throw new YamiShopBindException("类目名称已存在!"); * @PreAuthorize"prod:category:save"访
} * 使@SysLog便
categoryService.saveCategory(category); *
return ServerResponseEntity.success(); * @param category ID
} * @return ServerResponseEntityVoid
*/
/** @SysLog("保存分类")
* @PostMapping
*/ @PreAuthorize("@pms.hasPermission('prod:category:save')")
@SysLog("更新分类") public ServerResponseEntity<Void> save(@RequestBody Category category) {
@PutMapping category.setShopId(SecurityUtils.getSysUser().getShopId());
@PreAuthorize("@pms.hasPermission('prod:category:update')") category.setRecTime(new Date());
public ServerResponseEntity<String> update(@RequestBody Category category){ // 通过LambdaQueryWrapper构建查询条件查询在当前店铺下分类名称与要保存的分类名称相同的分类记录用于判断名称是否已存在。
category.setShopId(SecurityUtils.getSysUser().getShopId()); Category categoryName = categoryService.getOne(new LambdaQueryWrapper<Category>()
if (Objects.equals(category.getParentId(),category.getCategoryId())) { .eq(Category::getCategoryName, category.getCategoryName())
return ServerResponseEntity.showFailMsg("分类的上级不能是自己本身"); .eq(Category::getShopId, category.getShopId()));
} if (Objects.nonNull(categoryName)) {
Category categoryName = categoryService.getOne(new LambdaQueryWrapper<Category>().eq(Category::getCategoryName,category.getCategoryName()) throw new YamiShopBindException("类目名称已存在!");
.eq(Category::getShopId,category.getShopId()).ne(Category::getCategoryId,category.getCategoryId())); }
if(categoryName != null){ categoryService.saveCategory(category);
throw new YamiShopBindException("类目名称已存在!"); return ServerResponseEntity.success();
} }
Category categoryDb = categoryService.getById(category.getCategoryId());
// 如果从下线改成正常,则需要判断上级的状态 /**
if (Objects.equals(categoryDb.getStatus(),0) && Objects.equals(category.getStatus(),1) && !Objects.equals(category.getParentId(),0L)){ * Category
Category parentCategory = categoryService.getOne(new LambdaQueryWrapper<Category>().eq(Category::getCategoryId, category.getParentId())); * IDID
if(Objects.isNull(parentCategory) || Objects.equals(parentCategory.getStatus(),0)){ * 线
// 修改失败,上级分类不存在或者不为正常状态 * CategoryServiceupdateCategory
throw new YamiShopBindException("修改失败,上级分类不存在或者不为正常状态"); * @PreAuthorize"prod:category:update"访
} * 使@SysLog便
} *
categoryService.updateCategory(category); * @param category ID
return ServerResponseEntity.success(); * @return ServerResponseEntity
} */
@SysLog("更新分类")
/** @PutMapping
* @PreAuthorize("@pms.hasPermission('prod:category:update')")
*/ public ServerResponseEntity<String> update(@RequestBody Category category) {
@SysLog("删除分类") category.setShopId(SecurityUtils.getSysUser().getShopId());
@DeleteMapping("/{categoryId}") if (Objects.equals(category.getParentId(), category.getCategoryId())) {
@PreAuthorize("@pms.hasPermission('prod:category:delete')") return ServerResponseEntity.showFailMsg("分类的上级不能是自己本身");
public ServerResponseEntity<String> delete(@PathVariable("categoryId") Long categoryId){ }
if (categoryService.count(new LambdaQueryWrapper<Category>().eq(Category::getParentId,categoryId)) >0) { // 通过LambdaQueryWrapper构建查询条件查询在当前店铺下分类名称与要更新的分类名称相同且ID不同排除自身的分类记录用于判断名称是否已存在。
return ServerResponseEntity.showFailMsg("请删除子分类,再删除该分类"); Category categoryName = categoryService.getOne(new LambdaQueryWrapper<Category>()
} .eq(Category::getCategoryName, category.getCategoryName())
categoryService.deleteCategory(categoryId); .eq(Category::getShopId, category.getShopId())
return ServerResponseEntity.success(); .ne(Category::getCategoryId, category.getCategoryId()));
} if (categoryName!= null) {
throw new YamiShopBindException("类目名称已存在!");
/** }
* Category categoryDb = categoryService.getById(category.getCategoryId());
*/ // 如果从下线状态为0改成正常状态为1则需要判断上级的状态确保上级分类存在且为正常状态。
@GetMapping("/listCategory") if (Objects.equals(categoryDb.getStatus(), 0) && Objects.equals(category.getStatus(), 1) &&!Objects.equals(category.getParentId(), 0L)) {
public ServerResponseEntity<List<Category>> listCategory(){ Category parentCategory = categoryService.getOne(new LambdaQueryWrapper<Category>()
.eq(Category::getCategoryId, category.getParentId()));
return ServerResponseEntity.success(categoryService.list(new LambdaQueryWrapper<Category>() if (Objects.isNull(parentCategory) || Objects.equals(parentCategory.getStatus(), 0)) {
.le(Category::getGrade, 2) // 修改失败,上级分类不存在或者不为正常状态
.eq(Category::getShopId, SecurityUtils.getSysUser().getShopId()) throw new YamiShopBindException("修改失败,上级分类不存在或者不为正常状态");
.orderByAsc(Category::getSeq))); }
} }
categoryService.updateCategory(category);
/** return ServerResponseEntity.success();
* }
*/
@GetMapping("/listProdCategory") /**
public ServerResponseEntity<List<Category>> listProdCategory(){ * ID
List<Category> categories = categoryService.treeSelect(SecurityUtils.getSysUser().getShopId(),2); * CategoryServicedeleteCategory
return ServerResponseEntity.success(categories); * @PreAuthorize"prod:category:delete"访
} * 使@SysLog便
} *
* @param categoryId
* @return ServerResponseEntity
*/
@SysLog("删除分类")
@DeleteMapping("/{categoryId}")
@PreAuthorize("@pms.hasPermission('prod:category:delete')")
public ServerResponseEntity<String> delete(@PathVariable("categoryId") Long categoryId) {
// 通过LambdaQueryWrapper构建查询条件统计以当前分类ID为上级分类的子分类数量若数量大于0则说明还有子分类不允许删除。
if (categoryService.count(new LambdaQueryWrapper<Category>().eq(Category::getParentId, categoryId)) > 0) {
return ServerResponseEntity.showFailMsg("请删除子分类,再删除该分类");
}
categoryService.deleteCategory(categoryId);
return ServerResponseEntity.success();
}
/**
* LambdaQueryWrapper
* 2ID
*
*
* @return ServerResponseEntity
*/
@GetMapping("/listCategory")
public ServerResponseEntity<List<Category>> listCategory() {
return ServerResponseEntity.success(categoryService.list(new LambdaQueryWrapper<Category>()
.le(Category::getGrade, 2)
.eq(Category::getShopId, SecurityUtils.getSysUser().getShopId())
.orderByAsc(Category::getSeq)));
}
/**
* CategoryServicetreeSelect
* IDservice
*
*
* @return ServerResponseEntity
*/
@GetMapping("/listProdCategory")
public ServerResponseEntity<List<Category>> listProdCategory() {
List<Category> categories = categoryService.treeSelect(SecurityUtils.getSysUser().getShopId(), 2);
return ServerResponseEntity.success(categories);
}
}

@ -8,6 +8,7 @@
* *
*/ */
// 该类所属的包名,用于在项目中对类进行组织和分类管理
package com.yami.shop.admin.controller; package com.yami.shop.admin.controller;
import java.util.List; import java.util.List;
@ -18,28 +19,35 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
// 引入对应的实体类这里是Delivery类用于表示配送相关的业务对象
import com.yami.shop.bean.model.Delivery; import com.yami.shop.bean.model.Delivery;
// 引入配送服务层接口,用于调用具体的与配送相关的业务逻辑方法
import com.yami.shop.service.DeliveryService; import com.yami.shop.service.DeliveryService;
/** /**
* * DeliveryControllerSpring RESTfulDelivery
*
* @author lgh on 2018/11/26. * @author lgh on 2018/11/26.
*/ */
@RestController @RestController
// 定义该控制器类的基础请求路径,所有该类中的接口请求路径都将以此为前缀
@RequestMapping("/admin/delivery") @RequestMapping("/admin/delivery")
public class DeliveryController { public class DeliveryController {
// 通过Spring的依赖注入机制自动注入DeliveryService的实例以便调用其提供的业务方法
@Autowired @Autowired
private DeliveryService deliveryService; private DeliveryService deliveryService;
/** /**
* *
*/ * DeliveryServicelist
*
* ServerResponseEntity
* @return ServerResponseEntityList<Delivery>
*/
@GetMapping("/list") @GetMapping("/list")
public ServerResponseEntity<List<Delivery>> page(){ public ServerResponseEntity<List<Delivery>> page() {
List<Delivery> list = deliveryService.list();
List<Delivery> list = deliveryService.list(); return ServerResponseEntity.success(list);
return ServerResponseEntity.success(list); }
} }
}

@ -27,6 +27,7 @@ import java.util.Objects;
/** /**
* controller * controller
*
* @author lgh * @author lgh
* *
*/ */
@ -34,32 +35,52 @@ import java.util.Objects;
@RequestMapping("/admin/file") @RequestMapping("/admin/file")
public class FileController { public class FileController {
@Autowired // 自动注入附件文件服务层对象,用于处理文件上传等相关业务逻辑
private AttachFileService attachFileService; @Autowired
@Autowired private AttachFileService attachFileService;
private Qiniu qiniu; // 自动注入七牛相关配置对象,用于在文件上传到七牛云存储时获取相关配置信息
@Autowired @Autowired
private ImgUploadUtil imgUploadUtil; private Qiniu qiniu;
// 自动注入图片上传工具类对象,用于获取上传类型、路径等相关信息,辅助文件上传操作
@Autowired
private ImgUploadUtil imgUploadUtil;
@PostMapping("/upload/element") /**
public ServerResponseEntity<String> uploadElementFile(@RequestParam("file") MultipartFile file) throws IOException{ * element
if(file.isEmpty()){ * @param file MultipartFile
* @return ServerResponseEntity<String>
* @throws IOException IO
*/
@PostMapping("/upload/element")
public ServerResponseEntity<String> uploadElementFile(@RequestParam("file") MultipartFile file) throws IOException {
// 判断上传的文件是否为空,如果为空则直接返回成功状态的空响应
if (file.isEmpty()) {
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
String fileName = attachFileService.uploadFile(file); // 调用附件文件服务层的方法上传文件,并获取上传后的文件名
String fileName = attachFileService.uploadFile(file);
return ServerResponseEntity.success(fileName); return ServerResponseEntity.success(fileName);
} }
@PostMapping("/upload/tinymceEditor") /**
public ServerResponseEntity<String> uploadTinymceEditorImages(@RequestParam("editorFile") MultipartFile editorFile) throws IOException{ * tinymce
String fileName = attachFileService.uploadFile(editorFile); * @param editorFile MultipartFile
String data = ""; * @return ServerResponseEntity<String>访
if (Objects.equals(imgUploadUtil.getUploadType(), UploadType.LOCAL.value())) { * @throws IOException IO
data = imgUploadUtil.getUploadPath() + fileName; */
} else if (Objects.equals(imgUploadUtil.getUploadType(), UploadType.QINIU.value())) { @PostMapping("/upload/tinymceEditor")
data = qiniu.getResourcesUrl() + fileName; public ServerResponseEntity<String> uploadTinymceEditorImages(@RequestParam("editorFile") MultipartFile editorFile) throws IOException {
} // 调用附件文件服务层的方法上传文件,并获取上传后的文件名
String fileName = attachFileService.uploadFile(editorFile);
String data = "";
// 根据图片上传工具类获取的上传类型进行不同的路径拼接处理
if (Objects.equals(imgUploadUtil.getUploadType(), UploadType.LOCAL.value())) {
// 如果是本地存储类型,将本地存储路径和文件名拼接起来作为图片的访问路径
data = imgUploadUtil.getUploadPath() + fileName;
} else if (Objects.equals(imgUploadUtil.getUploadType(), UploadType.QINIU.value())) {
// 如果是七牛云存储类型将七牛云资源的基础访问URL和文件名拼接起来作为图片的访问路径
data = qiniu.getResourcesUrl() + fileName;
}
return ServerResponseEntity.success(data); return ServerResponseEntity.success(data);
} }
}
}

@ -27,76 +27,95 @@ import java.util.Date;
import java.util.List; import java.util.List;
/** /**
* *
* @author lgh on 2019/03/27. * @author lgh on 2019/03/27.
*/ */
@RestController @RestController
@RequestMapping("/admin/hotSearch") @RequestMapping("/admin/hotSearch")
public class HotSearchController { public class HotSearchController {
// 自动注入热门搜索服务层接口,通过该接口调用具体的业务逻辑方法
@Autowired @Autowired
private HotSearchService hotSearchService; private HotSearchService hotSearchService;
/** /**
* *
*/ *
* 'admin:hotSearch:page' 访
*/
@GetMapping("/page") @GetMapping("/page")
@PreAuthorize("@pms.hasPermission('admin:hotSearch:page')") @PreAuthorize("@pms.hasPermission('admin:hotSearch:page')")
public ServerResponseEntity<IPage<HotSearch>> page(HotSearch hotSearch,PageParam<HotSearch> page){ public ServerResponseEntity<IPage<HotSearch>> page(HotSearch hotSearch, PageParam<HotSearch> page) {
IPage<HotSearch> hotSearchs = hotSearchService.page(page,new LambdaQueryWrapper<HotSearch>() // 创建一个 LambdaQueryWrapper 用于构建查询条件,结合 MyBatis Plus 进行数据库查询
.eq(HotSearch::getShopId, SecurityUtils.getSysUser().getShopId()) IPage<HotSearch> hotSearchs = hotSearchService.page(page, new LambdaQueryWrapper<HotSearch>()
.like(StrUtil.isNotBlank(hotSearch.getContent()), HotSearch::getContent,hotSearch.getContent()) // 根据当前登录用户所属店铺的 ID 进行筛选,确保只获取该店铺相关的热门搜索记录
.like(StrUtil.isNotBlank(hotSearch.getTitle()), HotSearch::getTitle,hotSearch.getTitle()) .eq(HotSearch::getShopId, SecurityUtils.getSysUser().getShopId())
.eq(hotSearch.getStatus()!=null, HotSearch::getStatus,hotSearch.getStatus()) // 如果传入的热门搜索内容不为空,则模糊匹配内容字段进行查询
.orderByAsc(HotSearch::getSeq) .like(StrUtil.isNotBlank(hotSearch.getContent()), HotSearch::getContent, hotSearch.getContent())
); // 如果传入的热门搜索标题不为空,则模糊匹配标题字段进行查询
return ServerResponseEntity.success(hotSearchs); .like(StrUtil.isNotBlank(hotSearch.getTitle()), HotSearch::getTitle, hotSearch.getTitle())
} // 如果传入的状态不为空,则精确匹配状态字段进行查询
.eq(hotSearch.getStatus()!= null, HotSearch::getStatus, hotSearch.getStatus())
// 按照序号字段升序排序结果,方便呈现顺序相关的展示需求
.orderByAsc(HotSearch::getSeq)
);
return ServerResponseEntity.success(hotSearchs);
}
/** /**
* * ID
*/ * ID
@GetMapping("/info/{id}") */
public ServerResponseEntity<HotSearch> info(@PathVariable("id") Long id){ @GetMapping("/info/{id}")
HotSearch hotSearch = hotSearchService.getById(id); public ServerResponseEntity<HotSearch> info(@PathVariable("id") Long id) {
return ServerResponseEntity.success(hotSearch); // 通过服务层的 getById 方法,根据传入的 ID 获取对应的热门搜索记录
} HotSearch hotSearch = hotSearchService.getById(id);
return ServerResponseEntity.success(hotSearch);
}
/** /**
* *
*/ * HotSearch ID
@PostMapping * 'admin:hotSearch:save'
@PreAuthorize("@pms.hasPermission('admin:hotSearch:save')") */
public ServerResponseEntity<Void> save(@RequestBody @Valid HotSearch hotSearch){ @PostMapping
hotSearch.setRecDate(new Date()); @PreAuthorize("@pms.hasPermission('admin:hotSearch:save')")
hotSearch.setShopId(SecurityUtils.getSysUser().getShopId()); public ServerResponseEntity<Void> save(@RequestBody @Valid HotSearch hotSearch) {
hotSearchService.save(hotSearch); // 设置记录的创建日期为当前日期
//清除缓存 hotSearch.setRecDate(new Date());
hotSearchService.removeHotSearchDtoCacheByShopId(SecurityUtils.getSysUser().getShopId()); // 设置记录所属的店铺 ID从当前登录用户的信息中获取
return ServerResponseEntity.success(); hotSearch.setShopId(SecurityUtils.getSysUser().getShopId());
} hotSearchService.save(hotSearch);
// 清除对应店铺的热门搜索缓存,保证数据的一致性
hotSearchService.removeHotSearchDtoCacheByShopId(SecurityUtils.getSysUser().getShopId());
return ServerResponseEntity.success();
}
/** /**
* *
*/ * HotSearch
@PutMapping * 'admin:hotSearch:update'
@PreAuthorize("@pms.hasPermission('admin:hotSearch:update')") */
public ServerResponseEntity<Void> update(@RequestBody @Valid HotSearch hotSearch){ @PutMapping
hotSearchService.updateById(hotSearch); @PreAuthorize("@pms.hasPermission('admin:hotSearch:update')")
//清除缓存 public ServerResponseEntity<Void> update(@RequestBody @Valid HotSearch hotSearch) {
hotSearchService.removeHotSearchDtoCacheByShopId(SecurityUtils.getSysUser().getShopId()); hotSearchService.updateById(hotSearch);
return ServerResponseEntity.success(); // 清除对应店铺的热门搜索缓存,确保缓存数据与数据库最新数据一致
} hotSearchService.removeHotSearchDtoCacheByShopId(SecurityUtils.getSysUser().getShopId());
return ServerResponseEntity.success();
}
/** /**
* * ID
*/ * ID
@DeleteMapping * 'admin:hotSearch:delete'
@PreAuthorize("@pms.hasPermission('admin:hotSearch:delete')") */
public ServerResponseEntity<Void> delete(@RequestBody List<Long> ids){ @DeleteMapping
hotSearchService.removeByIds(ids); @PreAuthorize("@pms.hasPermission('admin:hotSearch:delete')")
//清除缓存 public ServerResponseEntity<Void> delete(@RequestBody List<Long> ids) {
hotSearchService.removeHotSearchDtoCacheByShopId(SecurityUtils.getSysUser().getShopId()); hotSearchService.removeByIds(ids);
return ServerResponseEntity.success(); // 清除对应店铺的热门搜索缓存,保证数据的准确性
} hotSearchService.removeHotSearchDtoCacheByShopId(SecurityUtils.getSysUser().getShopId());
} return ServerResponseEntity.success();
}
}

@ -29,42 +29,64 @@ import java.util.Date;
import java.util.Objects; import java.util.Objects;
/** /**
* IndexImg
*
*
*
* @author lgh on 2018/11/26. * @author lgh on 2018/11/26.
*/ */
@RestController @RestController
@RequestMapping("/admin/indexImg") @RequestMapping("/admin/indexImg")
public class IndexImgController { public class IndexImgController {
// 注入IndexImgService用于处理与首页图片相关的业务逻辑例如图片信息的查询、保存、更新、删除等操作。
@Autowired @Autowired
private IndexImgService indexImgService; private IndexImgService indexImgService;
// 注入ProductService用于获取商品相关信息在涉及首页图片与商品关联的业务逻辑中会用到比如校验商品状态等情况。
@Autowired @Autowired
private ProductService productService; private ProductService productService;
/** /**
* * IndexImgPageParam
* IndexImgService
* @PreAuthorize"admin:indexImg:page"访
*
* @param indexImg IndexImg
* @param page
* @return ServerResponseEntity
*/ */
@GetMapping("/page") @GetMapping("/page")
@PreAuthorize("@pms.hasPermission('admin:indexImg:page')") @PreAuthorize("@pms.hasPermission('admin:indexImg:page')")
public ServerResponseEntity<IPage<IndexImg>> page(IndexImg indexImg, PageParam<IndexImg> page) { public ServerResponseEntity<IPage<IndexImg>> page(IndexImg indexImg, PageParam<IndexImg> page) {
// 使用IndexImgService的page方法进行分页查询传入分页参数和LambdaQueryWrapper构建的查询条件
// 根据传入的indexImg对象的状态是否不为空来决定是否添加状态筛选条件同时按照顺序排序。
IPage<IndexImg> indexImgPage = indexImgService.page(page, IPage<IndexImg> indexImgPage = indexImgService.page(page,
new LambdaQueryWrapper<IndexImg>() new LambdaQueryWrapper<IndexImg>()
.eq(indexImg.getStatus() != null, IndexImg::getStatus, indexImg.getStatus()) .eq(indexImg.getStatus()!= null, IndexImg::getStatus, indexImg.getStatus())
.orderByAsc(IndexImg::getSeq)); .orderByAsc(IndexImg::getSeq));
return ServerResponseEntity.success(indexImgPage); return ServerResponseEntity.success(indexImgPage);
} }
/** /**
* * IDIDID
* IndexImgService
*
* @PreAuthorize"admin:indexImg:info"访
*
* @param imgId
* @return ServerResponseEntity
*/ */
@GetMapping("/info/{imgId}") @GetMapping("/info/{imgId}")
@PreAuthorize("@pms.hasPermission('admin:indexImg:info')") @PreAuthorize("@pms.hasPermission('admin:indexImg:info')")
public ServerResponseEntity<IndexImg> info(@PathVariable("imgId") Long imgId) { public ServerResponseEntity<IndexImg> info(@PathVariable("imgId") Long imgId) {
Long shopId = SecurityUtils.getSysUser().getShopId(); Long shopId = SecurityUtils.getSysUser().getShopId();
// 通过IndexImgService根据店铺ID和图片ID查询对应的首页图片对象使用LambdaQueryWrapper构建查询条件。
IndexImg indexImg = indexImgService.getOne(new LambdaQueryWrapper<IndexImg>().eq(IndexImg::getShopId, shopId).eq(IndexImg::getImgId, imgId)); IndexImg indexImg = indexImgService.getOne(new LambdaQueryWrapper<IndexImg>().eq(IndexImg::getShopId, shopId).eq(IndexImg::getImgId, imgId));
if (Objects.nonNull(indexImg.getRelation())) { if (Objects.nonNull(indexImg.getRelation())) {
// 若图片存在关联商品通过relation属性判断则通过ProductService根据商品ID获取商品对象。
Product product = productService.getProductByProdId(indexImg.getRelation()); Product product = productService.getProductByProdId(indexImg.getRelation());
// 将商品的图片和商品名称设置到首页图片对象的相应属性中,方便前端展示关联商品的相关信息。
indexImg.setPic(product.getPic()); indexImg.setPic(product.getPic());
indexImg.setProdName(product.getProdName()); indexImg.setProdName(product.getProdName());
} }
@ -72,7 +94,13 @@ public class IndexImgController {
} }
/** /**
* * @ValidIndexImg
* IDIDcheckProdStatus
* IndexImgServicesave
* @PreAuthorize"admin:indexImg:save"访
*
* @param indexImg
* @return ServerResponseEntityVoid
*/ */
@PostMapping @PostMapping
@PreAuthorize("@pms.hasPermission('admin:indexImg:save')") @PreAuthorize("@pms.hasPermission('admin:indexImg:save')")
@ -87,7 +115,13 @@ public class IndexImgController {
} }
/** /**
* * @ValidIndexImg
* checkProdStatusIndexImgServicesaveOrUpdate
*
* @PreAuthorize"admin:indexImg:update"访
*
* @param indexImg
* @return ServerResponseEntityVoid
*/ */
@PutMapping @PutMapping
@PreAuthorize("@pms.hasPermission('admin:indexImg:update')") @PreAuthorize("@pms.hasPermission('admin:indexImg:update')")
@ -99,7 +133,12 @@ public class IndexImgController {
} }
/** /**
* * IDIndexImgServicedeleteIndexImgByIds
* ID
* @PreAuthorize"admin:indexImg:delete"访
*
* @param ids ID
* @return ServerResponseEntityVoid
*/ */
@DeleteMapping @DeleteMapping
@PreAuthorize("@pms.hasPermission('admin:indexImg:delete')") @PreAuthorize("@pms.hasPermission('admin:indexImg:delete')")
@ -109,6 +148,13 @@ public class IndexImgController {
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
/**
* 0
* IDID
* 1
*
* @param indexImg ID
*/
private void checkProdStatus(IndexImg indexImg) { private void checkProdStatus(IndexImg indexImg) {
if (!Objects.equals(indexImg.getType(), 0)) { if (!Objects.equals(indexImg.getType(), 0)) {
return; return;
@ -124,4 +170,4 @@ public class IndexImgController {
throw new YamiShopBindException("该商品未上架,请选择别的商品"); throw new YamiShopBindException("该商品未上架,请选择别的商品");
} }
} }
} }

@ -13,7 +13,6 @@ package com.yami.shop.admin.controller;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import com.yami.shop.common.response.ServerResponseEntity; import com.yami.shop.common.response.ServerResponseEntity;
@ -27,7 +26,6 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.yami.shop.common.util.PageParam; import com.yami.shop.common.util.PageParam;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yami.shop.bean.enums.MessageStatus; import com.yami.shop.bean.enums.MessageStatus;
@ -36,31 +34,44 @@ import com.yami.shop.service.MessageService;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
/** /**
* MessageControllerSpring RESTfulMessage
*
* @author lgh on 2018/10/15. * @author lgh on 2018/10/15.
*/ */
@RestController @RestController
@RequestMapping("/admin/message") @RequestMapping("/admin/message")
public class MessageController { public class MessageController {
// 通过Spring的依赖注入机制自动注入MessageService的实例以便调用其提供的业务方法来处理消息相关逻辑。
@Autowired @Autowired
private MessageService messageService; private MessageService messageService;
/** /**
* *
* MessagePageParam
* 使MessageServicepageLambdaQueryWrapper
*
* ServerResponseEntity
* @param message
* @param page
* @return ServerResponseEntityIPage<Message>
*/ */
@GetMapping("/page") @GetMapping("/page")
@PreAuthorize("@pms.hasPermission('admin:message:page')") @PreAuthorize("@pms.hasPermission('admin:message:page')")
public ServerResponseEntity<IPage<Message>> page(Message message,PageParam<Message> page) { public ServerResponseEntity<IPage<Message>> page(Message message, PageParam<Message> page) {
IPage<Message> messages = messageService.page(page, new LambdaQueryWrapper<Message>() IPage<Message> messages = messageService.page(page, new LambdaQueryWrapper<Message>()
.like(StrUtil.isNotBlank(message.getUserName()), Message::getUserName, message.getUserName()) .like(StrUtil.isNotBlank(message.getUserName()), Message::getUserName, message.getUserName())
.eq(message.getStatus() != null, Message::getStatus, message.getStatus())); .eq(message.getStatus()!= null, Message::getStatus, message.getStatus()));
return ServerResponseEntity.success(messages); return ServerResponseEntity.success(messages);
} }
/** /**
* * ID
* IDMessageServicegetById
* ServerResponseEntity
* @param id ID
* @return ServerResponseEntityMessage
*/ */
@GetMapping("/info/{id}") @GetMapping("/info/{id}")
@PreAuthorize("@pms.hasPermission('admin:message:info')") @PreAuthorize("@pms.hasPermission('admin:message:info')")
@ -70,7 +81,11 @@ public class MessageController {
} }
/** /**
* *
* MessageMessageServicesave
* ServerResponseEntityVoid
* @param message
* @return ServerResponseEntityVoid
*/ */
@PostMapping @PostMapping
@PreAuthorize("@pms.hasPermission('admin:message:save')") @PreAuthorize("@pms.hasPermission('admin:message:save')")
@ -80,7 +95,11 @@ public class MessageController {
} }
/** /**
* *
* MessageMessageServiceupdateByIdID
* ServerResponseEntityVoid
* @param message
* @return ServerResponseEntityVoid
*/ */
@PutMapping @PutMapping
@PreAuthorize("@pms.hasPermission('admin:message:update')") @PreAuthorize("@pms.hasPermission('admin:message:update')")
@ -90,7 +109,11 @@ public class MessageController {
} }
/** /**
* *
* IDMessageIDIDMessageStatus.RELEASE.value()
* MessageServiceupdateByIdServerResponseEntityVoid
* @param id ID
* @return ServerResponseEntityVoid
*/ */
@PutMapping("/release/{id}") @PutMapping("/release/{id}")
@PreAuthorize("@pms.hasPermission('admin:message:release')") @PreAuthorize("@pms.hasPermission('admin:message:release')")
@ -103,7 +126,11 @@ public class MessageController {
} }
/** /**
* *
* IDMessageIDIDMessageStatus.CANCEL.value()
* MessageServiceupdateByIdServerResponseEntityVoid
* @param id ID
* @return ServerResponseEntityVoid
*/ */
@PutMapping("/cancel/{id}") @PutMapping("/cancel/{id}")
@PreAuthorize("@pms.hasPermission('admin:message:cancel')") @PreAuthorize("@pms.hasPermission('admin:message:cancel')")
@ -116,7 +143,11 @@ public class MessageController {
} }
/** /**
* *
* IDListMessageServiceremoveByIds
* IDServerResponseEntityVoid
* @param ids ID
* @return ServerResponseEntityVoid
*/ */
@DeleteMapping("/{ids}") @DeleteMapping("/{ids}")
@PreAuthorize("@pms.hasPermission('admin:message:delete')") @PreAuthorize("@pms.hasPermission('admin:message:delete')")
@ -124,4 +155,4 @@ public class MessageController {
messageService.removeByIds(Arrays.asList(ids)); messageService.removeByIds(Arrays.asList(ids));
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
} }

@ -27,7 +27,7 @@ import java.util.Date;
/** /**
* *
* *
* @author hzm * @author hzm
* @date * @date
*/ */
@ -36,30 +36,31 @@ import java.util.Date;
@RequestMapping("/shop/notice") @RequestMapping("/shop/notice")
public class NoticeController { public class NoticeController {
// 通过构造函数注入NoticeService用于调用业务层方法来处理公告相关的业务逻辑
private final NoticeService noticeService; private final NoticeService noticeService;
/** /**
* *
* *
* @param page * @param page
* @param notice * @param notice
* @return * @return ServerResponseEntity<IPage<Notice>>
*/ */
@GetMapping("/page") @GetMapping("/page")
public ServerResponseEntity<IPage<Notice>> getNoticePage(PageParam<Notice> page, Notice notice) { public ServerResponseEntity<IPage<Notice>> getNoticePage(PageParam<Notice> page, Notice notice) {
// 使用MyBatis Plus的LambdaQueryWrapper构建查询条件根据传入的公告对象中的非空字段进行筛选
// 例如根据状态、是否置顶、标题等进行筛选,并按照更新时间降序排列
IPage<Notice> noticePage = noticeService.page(page, new LambdaQueryWrapper<Notice>() IPage<Notice> noticePage = noticeService.page(page, new LambdaQueryWrapper<Notice>()
.eq(notice.getStatus() != null, Notice::getStatus, notice.getStatus()) .eq(notice.getStatus()!= null, Notice::getStatus, notice.getStatus())
.eq(notice.getIsTop()!=null,Notice::getIsTop,notice.getIsTop()) .eq(notice.getIsTop()!= null, Notice::getIsTop, notice.getIsTop())
.like(notice.getTitle() != null, Notice::getTitle, notice.getTitle()).orderByDesc(Notice::getUpdateTime)); .like(notice.getTitle()!= null, Notice::getTitle, notice.getTitle()).orderByDesc(Notice::getUpdateTime));
return ServerResponseEntity.success(noticePage); return ServerResponseEntity.success(noticePage);
} }
/** /**
* id * id
* * @param id id
* @param id id * @return ServerResponseEntity<Notice>id
* @return
*/ */
@GetMapping("/info/{id}") @GetMapping("/info/{id}")
public ServerResponseEntity<Notice> getById(@PathVariable("id") Long id) { public ServerResponseEntity<Notice> getById(@PathVariable("id") Long id) {
@ -67,57 +68,65 @@ public class NoticeController {
} }
/** /**
* *
* *
* @param notice * @param notice
* @return * @return ServerResponseEntity<Boolean>truefalse
*/ */
@SysLog("新增公告管理") @SysLog("新增公告管理")
@PostMapping @PostMapping
@PreAuthorize("@pms.hasPermission('shop:notice:save')") @PreAuthorize("@pms.hasPermission('shop:notice:save')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'shop:notice:save'权限的用户才能访问该接口
public ServerResponseEntity<Boolean> save(@RequestBody @Valid Notice notice) { public ServerResponseEntity<Boolean> save(@RequestBody @Valid Notice notice) {
// 设置公告所属店铺的id从当前登录用户信息中获取
notice.setShopId(SecurityUtils.getSysUser().getShopId()); notice.setShopId(SecurityUtils.getSysUser().getShopId());
if (notice.getStatus() == 1) { if (notice.getStatus() == 1) {
// 如果公告状态为已发布1表示已发布则设置发布时间为当前时间
notice.setPublishTime(new Date()); notice.setPublishTime(new Date());
} }
notice.setUpdateTime(new Date()); notice.setUpdateTime(new Date());
// 调用业务层方法先移除相关的公告列表具体逻辑由业务层的removeNoticeList方法决定
noticeService.removeNoticeList(); noticeService.removeNoticeList();
return ServerResponseEntity.success(noticeService.save(notice)); return ServerResponseEntity.success(noticeService.save(notice));
} }
/** /**
* *
* *
* @param notice * @param notice
* @return * @return ServerResponseEntity<Boolean>truefalse
*/ */
@SysLog("修改公告管理") @SysLog("修改公告管理")
@PutMapping @PutMapping
@PreAuthorize("@pms.hasPermission('shop:notice:update')") @PreAuthorize("@pms.hasPermission('shop:notice:update')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'shop:notice:update'权限的用户才能访问该接口
public ServerResponseEntity<Boolean> updateById(@RequestBody @Valid Notice notice) { public ServerResponseEntity<Boolean> updateById(@RequestBody @Valid Notice notice) {
Notice oldNotice = noticeService.getById(notice.getId()); Notice oldNotice = noticeService.getById(notice.getId());
if (oldNotice.getStatus() == 0 && notice.getStatus() == 1) { if (oldNotice.getStatus() == 0 && notice.getStatus() == 1) {
// 如果原公告状态为未发布0表示未发布修改后变为已发布1表示已发布则设置发布时间为当前时间
notice.setPublishTime(new Date()); notice.setPublishTime(new Date());
} }
notice.setUpdateTime(new Date()); notice.setUpdateTime(new Date());
// 调用业务层方法先移除相关的公告列表具体逻辑由业务层的removeNoticeList方法决定
noticeService.removeNoticeList(); noticeService.removeNoticeList();
// 移除要修改的这条公告具体逻辑由业务层的removeNoticeById方法决定可能是缓存清除等相关操作
noticeService.removeNoticeById(notice.getId()); noticeService.removeNoticeById(notice.getId());
return ServerResponseEntity.success(noticeService.updateById(notice)); return ServerResponseEntity.success(noticeService.updateById(notice));
} }
/** /**
* id * id
* * id
* @param id id * @param id id
* @return * @return ServerResponseEntity<Boolean>truefalse
*/ */
@SysLog("删除公告管理") @SysLog("删除公告管理")
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('shop:notice:delete')") @PreAuthorize("@pms.hasPermission('shop:notice:delete')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'shop:notice:delete'权限的用户才能访问该接口
public ServerResponseEntity<Boolean> removeById(@PathVariable Long id) { public ServerResponseEntity<Boolean> removeById(@PathVariable Long id) {
noticeService.removeNoticeList(); noticeService.removeNoticeList();
noticeService.removeNoticeById(id); noticeService.removeNoticeById(id);
return ServerResponseEntity.success(noticeService.removeById(id)); return ServerResponseEntity.success(noticeService.removeById(id));
} }
}
}

@ -44,6 +44,7 @@ import java.util.Date;
import java.util.List; import java.util.List;
/** /**
* Excel
* @author lgh on 2018/09/15. * @author lgh on 2018/09/15.
*/ */
@Slf4j @Slf4j
@ -51,104 +52,143 @@ import java.util.List;
@RequestMapping("/order/order") @RequestMapping("/order/order")
public class OrderController { public class OrderController {
// 自动注入订单服务层接口,通过该接口调用具体的业务逻辑方法,处理订单相关业务
@Autowired @Autowired
private OrderService orderService; private OrderService orderService;
// 自动注入订单项服务层接口,用于处理与订单中具体商品项相关的业务逻辑
@Autowired @Autowired
private OrderItemService orderItemService; private OrderItemService orderItemService;
// 自动注入用户地址订单服务层接口,用于获取订单相关的用户地址信息等业务逻辑
@Autowired @Autowired
private UserAddrOrderService userAddrOrderService; private UserAddrOrderService userAddrOrderService;
// 自动注入商品服务层接口,可能用于商品相关的业务操作,比如缓存清理等
@Autowired @Autowired
private ProductService productService; private ProductService productService;
// 自动注入库存单元SKU服务层接口同样可能涉及到缓存清理等与库存单元相关的业务操作
@Autowired @Autowired
private SkuService skuService; private SkuService skuService;
/** /**
* *
* OrderParamPageParam
* 'order:order:page' 访
*/ */
@GetMapping("/page") @GetMapping("/page")
@PreAuthorize("@pms.hasPermission('order:order:page')") @PreAuthorize("@pms.hasPermission('order:order:page')")
public ServerResponseEntity<IPage<Order>> page(OrderParam orderParam,PageParam<Order> page) { public ServerResponseEntity<IPage<Order>> page(OrderParam orderParam, PageParam<Order> page) {
// 获取当前登录用户所属店铺的 ID用于后续筛选该店铺下的订单数据
Long shopId = SecurityUtils.getSysUser().getShopId(); Long shopId = SecurityUtils.getSysUser().getShopId();
// 将店铺 ID 设置到订单查询参数中,确保查询的是当前店铺的订单
orderParam.setShopId(shopId); orderParam.setShopId(shopId);
// 调用订单服务层的方法,按照订单参数和分页参数获取详细的订单分页信息
IPage<Order> orderPage = orderService.pageOrdersDetailByOrderParam(page, orderParam); IPage<Order> orderPage = orderService.pageOrdersDetailByOrderParam(page, orderParam);
return ServerResponseEntity.success(orderPage); return ServerResponseEntity.success(orderPage);
} }
/** /**
* *
*
* 'order:order:info'
*/ */
@GetMapping("/orderInfo/{orderNumber}") @GetMapping("/orderInfo/{orderNumber}")
@PreAuthorize("@pms.hasPermission('order:order:info')") @PreAuthorize("@pms.hasPermission('order:order:info')")
public ServerResponseEntity<Order> info(@PathVariable("orderNumber") String orderNumber) { public ServerResponseEntity<Order> info(@PathVariable("orderNumber") String orderNumber) {
// 获取当前登录用户所属店铺的 ID用于权限校验
Long shopId = SecurityUtils.getSysUser().getShopId(); Long shopId = SecurityUtils.getSysUser().getShopId();
// 通过订单服务层的方法,根据订单编号获取订单基本信息
Order order = orderService.getOrderByOrderNumber(orderNumber); Order order = orderService.getOrderByOrderNumber(orderNumber);
// 校验当前用户所属店铺 ID 是否与订单所属店铺 ID 一致,不一致则抛出无权限异常
if (!Objects.equal(shopId, order.getShopId())) { if (!Objects.equal(shopId, order.getShopId())) {
throw new YamiShopBindException("您没有权限获取该订单信息"); throw new YamiShopBindException("您没有权限获取该订单信息");
} }
// 通过订单项服务层的方法,根据订单编号获取该订单下的所有订单项信息
List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber); List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber);
// 将订单项信息设置到订单对象中,完善订单的详细信息
order.setOrderItems(orderItems); order.setOrderItems(orderItems);
// 通过用户地址订单服务层的方法,根据订单关联的地址 ID 获取用户地址信息
UserAddrOrder userAddrOrder = userAddrOrderService.getById(order.getAddrOrderId()); UserAddrOrder userAddrOrder = userAddrOrderService.getById(order.getAddrOrderId());
// 将用户地址信息设置到订单对象中
order.setUserAddrOrder(userAddrOrder); order.setUserAddrOrder(userAddrOrder);
return ServerResponseEntity.success(order); return ServerResponseEntity.success(order);
} }
/** /**
* *
* DeliveryOrderParam
* SKU 'order:order:delivery'
*/ */
@PutMapping("/delivery") @PutMapping("/delivery")
@PreAuthorize("@pms.hasPermission('order:order:delivery')") @PreAuthorize("@pms.hasPermission('order:order:delivery')")
public ServerResponseEntity<Void> delivery(@RequestBody DeliveryOrderParam deliveryOrderParam) { public ServerResponseEntity<Void> delivery(@RequestBody DeliveryOrderParam deliveryOrderParam) {
// 获取当前登录用户所属店铺的 ID用于权限校验
Long shopId = SecurityUtils.getSysUser().getShopId(); Long shopId = SecurityUtils.getSysUser().getShopId();
// 通过订单服务层的方法,根据订单编号获取订单基本信息
Order order = orderService.getOrderByOrderNumber(deliveryOrderParam.getOrderNumber()); Order order = orderService.getOrderByOrderNumber(deliveryOrderParam.getOrderNumber());
// 校验当前用户所属店铺 ID 是否与订单所属店铺 ID 一致,不一致则抛出无权限异常
if (!Objects.equal(shopId, order.getShopId())) { if (!Objects.equal(shopId, order.getShopId())) {
throw new YamiShopBindException("您没有权限修改该订单信息"); throw new YamiShopBindException("您没有权限修改该订单信息");
} }
// 创建一个新的 Order 对象,用于设置要更新的订单信息
Order orderParam = new Order(); Order orderParam = new Order();
// 设置订单 ID确保更新的是正确的订单记录
orderParam.setOrderId(order.getOrderId()); orderParam.setOrderId(order.getOrderId());
// 设置快递 ID来自发货参数
orderParam.setDvyId(deliveryOrderParam.getDvyId()); orderParam.setDvyId(deliveryOrderParam.getDvyId());
// 设置快递单号,来自发货参数
orderParam.setDvyFlowId(deliveryOrderParam.getDvyFlowId()); orderParam.setDvyFlowId(deliveryOrderParam.getDvyFlowId());
// 设置发货时间为当前时间
orderParam.setDvyTime(new Date()); orderParam.setDvyTime(new Date());
// 设置订单状态为已发货(通过 OrderStatus 枚举获取对应状态值)
orderParam.setStatus(OrderStatus.CONSIGNMENT.value()); orderParam.setStatus(OrderStatus.CONSIGNMENT.value());
// 设置用户 ID与原订单一致
orderParam.setUserId(order.getUserId()); orderParam.setUserId(order.getUserId());
// 调用订单服务层的方法,更新订单的发货相关信息
orderService.delivery(orderParam); orderService.delivery(orderParam);
// 通过订单项服务层的方法,根据订单编号获取该订单下的所有订单项信息
List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(deliveryOrderParam.getOrderNumber()); List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(deliveryOrderParam.getOrderNumber());
// 遍历订单项清除对应商品和库存单元SKU的缓存
for (OrderItem orderItem : orderItems) { for (OrderItem orderItem : orderItems) {
productService.removeProductCacheByProdId(orderItem.getProdId()); productService.removeProductCacheByProdId(orderItem.getProdId());
skuService.removeSkuCacheBySkuId(orderItem.getSkuId(),orderItem.getProdId()); skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId());
} }
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
/** /**
* * Excel
*
* 使 Hutool Excel Excel
* 'order:order:waitingConsignmentExcel' 访
* *
* @param order * @param order ID
* @param consignmentName * @param startTime @DateTimeFormat
* @param consignmentMobile * @param endTime @DateTimeFormat
* @param consignmentAddr * @param consignmentName Excel
* @param consignmentMobile Excel
* @param consignmentAddr Excel
* @param response HttpServletResponse Excel
*/ */
@GetMapping("/waitingConsignmentExcel") @GetMapping("/waitingConsignmentExcel")
@PreAuthorize("@pms.hasPermission('order:order:waitingConsignmentExcel')") @PreAuthorize("@pms.hasPermission('order:order:waitingConsignmentExcel')")
public void waitingConsignmentExcel(Order order, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date startTime, public void waitingConsignmentExcel(Order order, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date startTime,
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date endTime, String consignmentName, String consignmentMobile, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date endTime, String consignmentName, String consignmentMobile,
String consignmentAddr, HttpServletResponse response) { String consignmentAddr, HttpServletResponse response) {
// 获取当前登录用户所属店铺的 ID设置到订单筛选条件对象中确保查询的是当前店铺的待发货订单
Long shopId = SecurityUtils.getSysUser().getShopId(); Long shopId = SecurityUtils.getSysUser().getShopId();
order.setShopId(shopId); order.setShopId(shopId);
// 设置订单状态为已支付(待发货状态对应的前置状态,这里假设已支付的订单待发货),用于筛选待发货订单
order.setStatus(OrderStatus.PADYED.value()); order.setStatus(OrderStatus.PADYED.value());
// 调用订单服务层的方法,根据订单筛选条件(包括时间范围、订单状态等)查询符合条件的待发货订单列表
List<Order> orders = orderService.listOrdersDetailByOrder(order, startTime, endTime); List<Order> orders = orderService.listOrdersDetailByOrder(order, startTime, endTime);
//通过工具类创建writer // 通过 Hutool 的 ExcelUtil 工具类创建一个 ExcelWriter 对象,用于后续写入 Excel 数据
ExcelWriter writer = ExcelUtil.getBigWriter(); ExcelWriter writer = ExcelUtil.getBigWriter();
// 获取 Excel 表格的 Sheet 对象,用于设置列宽等表格样式相关操作
Sheet sheet = writer.getSheet(); Sheet sheet = writer.getSheet();
// 设置各列的宽度,单位是字符宽度的 1/256这里根据实际需求设置了不同列的宽度
sheet.setColumnWidth(0, 20 * 256); sheet.setColumnWidth(0, 20 * 256);
sheet.setColumnWidth(1, 20 * 256); sheet.setColumnWidth(1, 20 * 256);
sheet.setColumnWidth(2, 20 * 256); sheet.setColumnWidth(2, 20 * 256);
@ -157,19 +197,27 @@ public class OrderController {
sheet.setColumnWidth(7, 60 * 256); sheet.setColumnWidth(7, 60 * 256);
sheet.setColumnWidth(8, 60 * 256); sheet.setColumnWidth(8, 60 * 256);
sheet.setColumnWidth(9, 60 * 256); sheet.setColumnWidth(9, 60 * 256);
// 待发货
// 设置 Excel 表格的表头信息,定义了要展示的各列字段名称
String[] hearder = {"订单编号", "收件人", "手机", "收货地址", "商品名称", "数量", "发件人姓名", "发件人手机号", "发货地址", "备注"}; String[] hearder = {"订单编号", "收件人", "手机", "收货地址", "商品名称", "数量", "发件人姓名", "发件人手机号", "发货地址", "备注"};
// 合并表头单元格,用于展示标题等整体说明信息,这里合并了除第一列外的所有表头列
writer.merge(hearder.length - 1, "发货信息整理"); writer.merge(hearder.length - 1, "发货信息整理");
// 将表头行数据写入 Excel 表格
writer.writeRow(Arrays.asList(hearder)); writer.writeRow(Arrays.asList(hearder));
int row = 1; int row = 1;
// 遍历查询到的每个订单
for (Order dbOrder : orders) { for (Order dbOrder : orders) {
// 获取订单对应的用户地址信息
UserAddrOrder addr = dbOrder.getUserAddrOrder(); UserAddrOrder addr = dbOrder.getUserAddrOrder();
// 拼接完整的收货地址信息
String addrInfo = addr.getProvince() + addr.getCity() + addr.getArea() + addr.getAddr(); String addrInfo = addr.getProvince() + addr.getCity() + addr.getArea() + addr.getAddr();
// 获取该订单下的所有订单项信息
List<OrderItem> orderItems = dbOrder.getOrderItems(); List<OrderItem> orderItems = dbOrder.getOrderItems();
row++; row++;
// 遍历每个订单项,将订单和订单项相关信息逐行写入 Excel 表格
for (OrderItem orderItem : orderItems) { for (OrderItem orderItem : orderItems) {
// 第0列开始 // 0 列开始写入数据,列索引递增
int col = 0; int col = 0;
writer.writeCellValue(col++, row, dbOrder.getOrderNumber()); writer.writeCellValue(col++, row, dbOrder.getOrderNumber());
writer.writeCellValue(col++, row, addr.getReceiver()); writer.writeCellValue(col++, row, addr.getReceiver());
@ -183,105 +231,51 @@ public class OrderController {
writer.writeCellValue(col++, row, dbOrder.getRemarks()); writer.writeCellValue(col++, row, dbOrder.getRemarks());
} }
} }
// 将整理好的 Excel 数据通过响应流输出给客户端,实现下载功能
writeExcel(response, writer); writeExcel(response, writer);
} }
/** /**
* * Excel
*
* 使 Hutool Excel Excel
* 'order:order:soldExcel' 访
* *
* @param order * @param order ID
* @param startTime @DateTimeFormat
* @param endTime @DateTimeFormat
* @param response HttpServletResponse Excel
*/ */
@GetMapping("/soldExcel") @GetMapping("/soldExcel")
@PreAuthorize("@pms.hasPermission('order:order:soldExcel')") @PreAuthorize("@pms.hasPermission('order:order:soldExcel')")
public void soldExcel(Order order, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date startTime, public void soldExcel(Order order, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date startTime,
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date endTime, HttpServletResponse response) { @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date endTime, HttpServletResponse response) {
// 获取当前登录用户所属店铺的 ID设置到订单筛选条件对象中确保查询的是当前店铺的已销售订单
Long shopId = SecurityUtils.getSysUser().getShopId(); Long shopId = SecurityUtils.getSysUser().getShopId();
order.setShopId(shopId); order.setShopId(shopId);
// 设置订单为已支付状态,用于筛选已销售的订单(这里简单以是否已支付作为已销售的判断条件之一)
order.setIsPayed(1); order.setIsPayed(1);
// 调用订单服务层的方法,根据订单筛选条件(包括时间范围、是否已支付等)查询符合条件的已销售订单列表
List<Order> orders = orderService.listOrdersDetailByOrder(order, startTime, endTime); List<Order> orders = orderService.listOrdersDetailByOrder(order, startTime, endTime);
//通过工具类创建writer // 通过 Hutool 的 ExcelUtil 工具类创建一个 ExcelWriter 对象,用于后续写入 Excel 数据
ExcelWriter writer = ExcelUtil.getBigWriter(); ExcelWriter writer = ExcelUtil.getBigWriter();
// 待发货 // 获取 Excel 表格的 Sheet 对象,用于设置列宽等表格样式相关操作
String[] hearder = {"订单编号", "下单时间", "收件人", "手机", "收货地址", "商品名称", "数量", "订单应付", "订单运费", "订单实付"};
Sheet sheet = writer.getSheet(); Sheet sheet = writer.getSheet();
// 设置各列的宽度,单位是字符宽度的 1/256这里根据实际需求设置了不同列的宽度
sheet.setColumnWidth(0, 20 * 256); sheet.setColumnWidth(0, 20 * 256);
sheet.setColumnWidth(1, 20 * 256); sheet.setColumnWidth(1, 20 * 256);
sheet.setColumnWidth(3, 20 * 256); sheet.setColumnWidth(3, 20 * 256);
sheet.setColumnWidth(4, 60 * 256); sheet.setColumnWidth(4, 60 * 256);
sheet.setColumnWidth(5, 60 * 256); sheet.setColumnWidth(5, 60 * 256);
// 设置 Excel 表格的表头信息,定义了要展示的各列字段名称
String[] hearder = {"订单编号", "下单时间", "收件人", "手机", "收货地址", "商品名称", "数量", "订单应付", "订单运费", "订单实付"};
// 合并表头单元格,用于展示标题等整体说明信息,这里合并了除第一列外的所有表头列
writer.merge(hearder.length - 1, "销售信息整理"); writer.merge(hearder.length - 1, "销售信息整理");
// 将表头行数据写入 Excel 表格
writer.writeRow(Arrays.asList(hearder)); writer.writeRow(Arrays.asList(hearder));
int row = 1; int row = 1;
for (Order dbOrder : orders) { // 遍历查询到的每个订单
UserAddrOrder addr = dbOrder.getUserAddrOrder(); for (Order db
String addrInfo = addr.getProvince() + addr.getCity() + addr.getArea() + addr.getAddr();
List<OrderItem> orderItems = dbOrder.getOrderItems();
int firstRow = row + 1;
int lastRow = row + orderItems.size();
int col = -1;
// 订单编号
mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getOrderNumber());
// 下单时间
mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getCreateTime());
// 收件人
mergeIfNeed(writer, firstRow, lastRow, ++col, col, addr.getReceiver());
// "手机"
mergeIfNeed(writer, firstRow, lastRow, ++col, col, addr.getMobile());
// "收货地址"
mergeIfNeed(writer, firstRow, lastRow, ++col, col, addrInfo);
int prodNameCol = ++col;
int prodCountCol = ++col;
for (OrderItem orderItem : orderItems) {
row++;
// 商品名称
writer.writeCellValue(prodNameCol, row, orderItem.getProdName());
// 数量
writer.writeCellValue(prodCountCol, row, orderItem.getProdCount());
}
// 订单应付
mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getTotal());
// 订单运费
mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getFreightAmount());
// 订单实付
mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getActualTotal());
}
writeExcel(response, writer);
}
/**
*
*/
private void mergeIfNeed(ExcelWriter writer, int firstRow, int lastRow, int firstColumn, int lastColumn, Object content) {
if (content instanceof Date) {
content = DateUtil.format((Date) content, DatePattern.NORM_DATETIME_PATTERN);
}
if (lastRow - firstRow > 0 || lastColumn - firstColumn > 0) {
writer.merge(firstRow, lastRow, firstColumn, lastColumn, content, false);
} else {
writer.writeCellValue(firstColumn, firstRow, content);
}
}
private void writeExcel(HttpServletResponse response, ExcelWriter writer) {
//response为HttpServletResponse对象
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
//test.xls是弹出下载对话框的文件名不能为中文中文请自行编码
response.setHeader("Content-Disposition", "attachment;filename=1.xls");
ServletOutputStream servletOutputStream = null;
try {
servletOutputStream = response.getOutputStream();
writer.flush(servletOutputStream);
servletOutputStream.flush();
} catch (IORuntimeException | IOException e) {
log.error("写出Excel错误", e);
} finally {
IoUtil.close(writer);
}
}
}

@ -28,9 +28,10 @@ import jakarta.validation.Valid;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
/** /**
* PickAddr
*
*
* *
* @author lgh on 2018/10/17. * @author lgh on 2018/10/17.
*/ */
@ -38,64 +39,97 @@ import java.util.Objects;
@RequestMapping("/shop/pickAddr") @RequestMapping("/shop/pickAddr")
public class PickAddrController { public class PickAddrController {
// 注入PickAddrService用于与提货地址相关的业务逻辑处理例如查询、保存、更新、删除提货地址等操作。
@Autowired @Autowired
private PickAddrService pickAddrService; private PickAddrService pickAddrService;
/** /**
* * PickAddrPageParam
*/ * PickAddrServiceID
*
* @PreAuthorize"shop:pickAddr:page"访
*
* @param pickAddr PickAddr
* @param page
* @return ServerResponseEntity
*/
@GetMapping("/page") @GetMapping("/page")
@PreAuthorize("@pms.hasPermission('shop:pickAddr:page')") @PreAuthorize("@pms.hasPermission('shop:pickAddr:page')")
public ServerResponseEntity<IPage<PickAddr>> page(PickAddr pickAddr,PageParam<PickAddr> page){ public ServerResponseEntity<IPage<PickAddr>> page(PickAddr pickAddr, PageParam<PickAddr> page) {
IPage<PickAddr> pickAddrs = pickAddrService.page(page,new LambdaQueryWrapper<PickAddr>() // 使用PickAddrService的page方法进行分页查询传入分页参数和LambdaQueryWrapper构建的查询条件。
.like(StrUtil.isNotBlank(pickAddr.getAddrName()),PickAddr::getAddrName,pickAddr.getAddrName()) // 通过StrUtil.isNotBlank判断提货地址名称是否不为空若不为空则添加模糊查询条件按照地址名称进行模糊匹配最后按照地址ID倒序排序。
.orderByDesc(PickAddr::getAddrId)); IPage<PickAddr> pickAddrs = pickAddrService.page(page, new LambdaQueryWrapper<PickAddr>()
return ServerResponseEntity.success(pickAddrs); .like(StrUtil.isNotBlank(pickAddr.getAddrName()), PickAddr::getAddrName, pickAddr.getAddrName())
} .orderByDesc(PickAddr::getAddrId));
return ServerResponseEntity.success(pickAddrs);
}
/** /**
* * IDIDPickAddrServicegetById
*/ *
@GetMapping("/info/{id}") * @PreAuthorize"shop:pickAddr:info"访
@PreAuthorize("@pms.hasPermission('shop:pickAddr:info')") *
public ServerResponseEntity<PickAddr> info(@PathVariable("id") Long id){ * @param id
PickAddr pickAddr = pickAddrService.getById(id); * @return ServerResponseEntity
return ServerResponseEntity.success(pickAddr); */
} @GetMapping("/info/{id}")
@PreAuthorize("@pms.hasPermission('shop:pickAddr:info')")
public ServerResponseEntity<PickAddr> info(@PathVariable("id") Long id) {
PickAddr pickAddr = pickAddrService.getById(id);
return ServerResponseEntity.success(pickAddr);
}
/** /**
* * @ValidPickAddr
*/ * IDIDPickAddrServicesave
@PostMapping *
@PreAuthorize("@pms.hasPermission('shop:pickAddr:save')") * @PreAuthorize"shop:pickAddr:save"访
public ServerResponseEntity<Void> save(@Valid @RequestBody PickAddr pickAddr){ *
pickAddr.setShopId(SecurityUtils.getSysUser().getShopId()); * @param pickAddr
pickAddrService.save(pickAddr); * @return ServerResponseEntityVoid
return ServerResponseEntity.success(); */
} @PostMapping
@PreAuthorize("@pms.hasPermission('shop:pickAddr:save')")
public ServerResponseEntity<Void> save(@Valid @RequestBody PickAddr pickAddr) {
pickAddr.setShopId(SecurityUtils.getSysUser().getShopId());
pickAddrService.save(pickAddr);
return ServerResponseEntity.success();
}
/** /**
* * @ValidPickAddr
*/ * PickAddrServicegetByIdID
@PutMapping * IDID
@PreAuthorize("@pms.hasPermission('shop:pickAddr:update')") * PickAddrServiceupdateById
public ServerResponseEntity<Void> update(@Valid @RequestBody PickAddr pickAddr){ * @PreAuthorize"shop:pickAddr:update"访
PickAddr dbPickAddr = pickAddrService.getById(pickAddr.getAddrId()); *
* @param pickAddr
* @return ServerResponseEntityVoid
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('shop:pickAddr:update')")
public ServerResponseEntity<Void> update(@Valid @RequestBody PickAddr pickAddr) {
PickAddr dbPickAddr = pickAddrService.getById(pickAddr.getAddrId());
if (!Objects.equals(dbPickAddr.getShopId(),SecurityUtils.getSysUser().getShopId())) { if (!Objects.equals(dbPickAddr.getShopId(), SecurityUtils.getSysUser().getShopId())) {
throw new YamiShopBindException(ResponseEnum.UNAUTHORIZED); throw new YamiShopBindException(ResponseEnum.UNAUTHORIZED);
} }
pickAddrService.updateById(pickAddr); pickAddrService.updateById(pickAddr);
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
/** /**
* * IDPickAddrServiceremoveByIds
*/ * ID
@DeleteMapping * @PreAuthorize"shop:pickAddr:delete"访
@PreAuthorize("@pms.hasPermission('shop:pickAddr:delete')") *
public ServerResponseEntity<Void> delete(@RequestBody Long[] ids){ * @param ids ID
pickAddrService.removeByIds(Arrays.asList(ids)); * @return ServerResponseEntityVoid
return ServerResponseEntity.success(); */
} @DeleteMapping
} @PreAuthorize("@pms.hasPermission('shop:pickAddr:delete')")
public ServerResponseEntity<Void> delete(@RequestBody Long[] ids) {
pickAddrService.removeByIds(Arrays.asList(ids));
return ServerResponseEntity.success();
}
}

@ -10,100 +10,126 @@
package com.yami.shop.admin.controller; package com.yami.shop.admin.controller;
import com.fasterxml.jackson.databind.JavaType; import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yami.shop.bean.model.ProdComm; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yami.shop.common.annotation.SysLog; import com.yami.shop.bean.model.PickAddr;
import com.yami.shop.common.util.Json; import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.service.ProdCommService; import com.yami.shop.common.response.ResponseEnum;
import lombok.AllArgsConstructor;
import jakarta.validation.Valid;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import com.yami.shop.common.response.ServerResponseEntity; import com.yami.shop.common.response.ServerResponseEntity;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.security.admin.util.SecurityUtils;
import com.yami.shop.service.PickAddrService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import jakarta.validation.Valid;
import com.yami.shop.common.util.PageParam; import java.util.Arrays;
import com.baomidou.mybatisplus.core.metadata.IPage; import java.util.Objects;
import java.util.Date;
/** /**
* * PickAddr
*
*
* *
* @author xwc * @author lgh on 2018/10/17.
* @date 2019-04-19 10:43:57
*/ */
@RestController @RestController
@AllArgsConstructor @RequestMapping("/shop/pickAddr")
@RequestMapping("/prod/prodComm" ) public class PickAddrController {
public class ProdCommController {
private final ProdCommService prodCommService; // 注入PickAddrService用于与提货地址相关的业务逻辑处理例如查询、保存、更新、删除提货地址等操作。
@Autowired
private PickAddrService pickAddrService;
/** /**
* * PickAddrPageParam
* @param page * PickAddrServiceID
* @param prodComm *
* @return * @PreAuthorize"shop:pickAddr:page"访
*
* @param pickAddr PickAddr
* @param page
* @return ServerResponseEntity
*/ */
@GetMapping("/page" ) @GetMapping("/page")
@PreAuthorize("@pms.hasPermission('prod:prodComm:page')" ) @PreAuthorize("@pms.hasPermission('shop:pickAddr:page')")
public ServerResponseEntity<IPage<ProdComm>> getProdCommPage(PageParam page, ProdComm prodComm) { public ServerResponseEntity<IPage<PickAddr>> page(PickAddr pickAddr, PageParam<PickAddr> page) {
return ServerResponseEntity.success(prodCommService.getProdCommPage(page,prodComm)); // 使用PickAddrService的page方法进行分页查询传入分页参数和LambdaQueryWrapper构建的查询条件。
// 通过StrUtil.isNotBlank判断提货地址名称是否不为空若不为空则添加模糊查询条件按照地址名称进行模糊匹配最后按照地址ID倒序排序。
IPage<PickAddr> pickAddrs = pickAddrService.page(page, new LambdaQueryWrapper<PickAddr>()
.like(StrUtil.isNotBlank(pickAddr.getAddrName()), PickAddr::getAddrName, pickAddr.getAddrName())
.orderByDesc(PickAddr::getAddrId));
return ServerResponseEntity.success(pickAddrs);
} }
/** /**
* id * IDIDPickAddrServicegetById
* @param prodCommId id *
* @return * @PreAuthorize"shop:pickAddr:info"访
*
* @param id
* @return ServerResponseEntity
*/ */
@GetMapping("/info/{prodCommId}" ) @GetMapping("/info/{id}")
@PreAuthorize("@pms.hasPermission('prod:prodComm:info')" ) @PreAuthorize("@pms.hasPermission('shop:pickAddr:info')")
public ServerResponseEntity<ProdComm> getById(@PathVariable("prodCommId" ) Long prodCommId) { public ServerResponseEntity<PickAddr> info(@PathVariable("id") Long id) {
return ServerResponseEntity.success(prodCommService.getById(prodCommId)); PickAddr pickAddr = pickAddrService.getById(id);
return ServerResponseEntity.success(pickAddr);
} }
/** /**
* * @ValidPickAddr
* @param prodComm * IDIDPickAddrServicesave
* @return *
* @PreAuthorize"shop:pickAddr:save"访
*
* @param pickAddr
* @return ServerResponseEntityVoid
*/ */
@SysLog("新增商品评论" )
@PostMapping @PostMapping
@PreAuthorize("@pms.hasPermission('prod:prodComm:save')" ) @PreAuthorize("@pms.hasPermission('shop:pickAddr:save')")
public ServerResponseEntity<Boolean> save(@RequestBody @Valid ProdComm prodComm) { public ServerResponseEntity<Void> save(@Valid @RequestBody PickAddr pickAddr) {
return ServerResponseEntity.success(prodCommService.save(prodComm)); pickAddr.setShopId(SecurityUtils.getSysUser().getShopId());
pickAddrService.save(pickAddr);
return ServerResponseEntity.success();
} }
/** /**
* * @ValidPickAddr
* @param prodComm * PickAddrServicegetByIdID
* @return * IDID
* PickAddrServiceupdateById
* @PreAuthorize"shop:pickAddr:update"访
*
* @param pickAddr
* @return ServerResponseEntityVoid
*/ */
@SysLog("修改商品评论" )
@PutMapping @PutMapping
@PreAuthorize("@pms.hasPermission('prod:prodComm:update')" ) @PreAuthorize("@pms.hasPermission('shop:pickAddr:update')")
public ServerResponseEntity<Boolean> updateById(@RequestBody @Valid ProdComm prodComm) { public ServerResponseEntity<Void> update(@Valid @RequestBody PickAddr pickAddr) {
prodComm.setReplyTime(new Date()); PickAddr dbPickAddr = pickAddrService.getById(pickAddr.getAddrId());
return ServerResponseEntity.success(prodCommService.updateById(prodComm));
if (!Objects.equals(dbPickAddr.getShopId(), SecurityUtils.getSysUser().getShopId())) {
throw new YamiShopBindException(ResponseEnum.UNAUTHORIZED);
}
pickAddrService.updateById(pickAddr);
return ServerResponseEntity.success();
} }
/** /**
* id * IDPickAddrServiceremoveByIds
* @param prodCommId id * ID
* @return * @PreAuthorize"shop:pickAddr:delete"访
*
* @param ids ID
* @return ServerResponseEntityVoid
*/ */
@SysLog("删除商品评论" ) @DeleteMapping
@DeleteMapping("/{prodCommId}" ) @PreAuthorize("@pms.hasPermission('shop:pickAddr:delete')")
@PreAuthorize("@pms.hasPermission('prod:prodComm:delete')" ) public ServerResponseEntity<Void> delete(@RequestBody Long[] ids) {
public ServerResponseEntity<Boolean> removeById(@PathVariable Long prodCommId) { pickAddrService.removeByIds(Arrays.asList(ids));
return ServerResponseEntity.success(prodCommService.removeById(prodCommId)); return ServerResponseEntity.success();
} }
}
}

@ -10,7 +10,6 @@
package com.yami.shop.admin.controller; package com.yami.shop.admin.controller;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
@ -31,7 +30,7 @@ import java.util.List;
/** /**
* *
* *
* @author hzm * @author hzm
* @date 2019-04-18 09:08:36 * @date 2019-04-18 09:08:36
*/ */
@ -39,33 +38,34 @@ import java.util.List;
@RequestMapping("/prod/prodTag") @RequestMapping("/prod/prodTag")
public class ProdTagController { public class ProdTagController {
// 通过Spring的自动注入机制注入ProdTagService对象用于调用业务层方法来处理商品分组相关的业务逻辑
@Autowired @Autowired
private ProdTagService prodTagService; private ProdTagService prodTagService;
/** /**
* *
* *
* @param page * @param page
* @param prodTag * @param prodTag
* @return * @return ServerResponseEntity<IPage<ProdTag>>
*/ */
@GetMapping("/page") @GetMapping("/page")
public ServerResponseEntity<IPage<ProdTag>> getProdTagPage(PageParam<ProdTag> page, ProdTag prodTag) { public ServerResponseEntity<IPage<ProdTag>> getProdTagPage(PageParam<ProdTag> page, ProdTag prodTag) {
// 使用MyBatis Plus的LambdaQueryWrapper构建查询条件根据传入的商品分组标签对象中的非空字段进行筛选
// 例如根据状态、标题等进行筛选并按照序号seq和创建时间降序排列
IPage<ProdTag> tagPage = prodTagService.page( IPage<ProdTag> tagPage = prodTagService.page(
page, new LambdaQueryWrapper<ProdTag>() page, new LambdaQueryWrapper<ProdTag>()
.eq(prodTag.getStatus() != null, ProdTag::getStatus, prodTag.getStatus()) .eq(prodTag.getStatus()!= null, ProdTag::getStatus, prodTag.getStatus())
.like(prodTag.getTitle() != null, ProdTag::getTitle, prodTag.getTitle()) .like(prodTag.getTitle()!= null, ProdTag::getTitle, prodTag.getTitle())
.orderByDesc(ProdTag::getSeq, ProdTag::getCreateTime)); .orderByDesc(ProdTag::getSeq, ProdTag::getCreateTime));
return ServerResponseEntity.success(tagPage); return ServerResponseEntity.success(tagPage);
} }
/** /**
* id * id
* * @param id id
* @param id id * @return ServerResponseEntity<ProdTag>id
* @return
*/ */
@GetMapping("/info/{id}") @GetMapping("/info/{id}")
public ServerResponseEntity<ProdTag> getById(@PathVariable("id") Long id) { public ServerResponseEntity<ProdTag> getById(@PathVariable("id") Long id) {
@ -73,67 +73,85 @@ public class ProdTagController {
} }
/** /**
* *
* *
* @param prodTag * @param prodTag
* @return * @return ServerResponseEntity<Boolean>truefalse
*/ */
@SysLog("新增商品分组标签") @SysLog("新增商品分组标签")
@PostMapping @PostMapping
@PreAuthorize("@pms.hasPermission('prod:prodTag:save')") @PreAuthorize("@pms.hasPermission('prod:prodTag:save')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'prod:prodTag:save'权限的用户才能访问该接口
public ServerResponseEntity<Boolean> save(@RequestBody @Valid ProdTag prodTag) { public ServerResponseEntity<Boolean> save(@RequestBody @Valid ProdTag prodTag) {
// 查看是否相同的标签 // 查看是否已存在相同名称的标签,通过标题进行模糊查询
List<ProdTag> list = prodTagService.list(new LambdaQueryWrapper<ProdTag>().like(ProdTag::getTitle, prodTag.getTitle())); List<ProdTag> list = prodTagService.list(new LambdaQueryWrapper<ProdTag>().like(ProdTag::getTitle, prodTag.getTitle()));
if (CollectionUtil.isNotEmpty(list)) { if (CollectionUtil.isNotEmpty(list)) {
// 如果查询到相同名称的标签列表不为空,则抛出异常,提示标签名称已存在,不能添加相同的标签
throw new YamiShopBindException("标签名称已存在,不能添加相同的标签"); throw new YamiShopBindException("标签名称已存在,不能添加相同的标签");
} }
// 设置默认属性将是否为默认标签设置为0表示非默认
prodTag.setIsDefault(0); prodTag.setIsDefault(0);
// 设置商品数量初始值为0
prodTag.setProdCount(0L); prodTag.setProdCount(0L);
// 设置创建时间为当前时间
prodTag.setCreateTime(new Date()); prodTag.setCreateTime(new Date());
// 设置更新时间为当前时间
prodTag.setUpdateTime(new Date()); prodTag.setUpdateTime(new Date());
// 设置商品分组标签所属店铺的id从当前登录用户信息中获取
prodTag.setShopId(SecurityUtils.getSysUser().getShopId()); prodTag.setShopId(SecurityUtils.getSysUser().getShopId());
// 调用业务层方法先进行相关的商品分组标签清理操作具体逻辑由业务层的removeProdTag方法决定
prodTagService.removeProdTag(); prodTagService.removeProdTag();
return ServerResponseEntity.success(prodTagService.save(prodTag)); return ServerResponseEntity.success(prodTagService.save(prodTag));
} }
/** /**
* *
* *
* @param prodTag * @param prodTag
* @return * @return ServerResponseEntity<Boolean>truefalse
*/ */
@SysLog("修改商品分组标签") @SysLog("修改商品分组标签")
@PutMapping @PutMapping
@PreAuthorize("@pms.hasPermission('prod:prodTag:update')") @PreAuthorize("@pms.hasPermission('prod:prodTag:update')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'prod:prodTag:update'权限的用户才能访问该接口
public ServerResponseEntity<Boolean> updateById(@RequestBody @Valid ProdTag prodTag) { public ServerResponseEntity<Boolean> updateById(@RequestBody @Valid ProdTag prodTag) {
// 更新商品分组标签的更新时间为当前时间
prodTag.setUpdateTime(new Date()); prodTag.setUpdateTime(new Date());
// 调用业务层方法先进行相关的商品分组标签清理操作具体逻辑由业务层的removeProdTag方法决定
prodTagService.removeProdTag(); prodTagService.removeProdTag();
return ServerResponseEntity.success(prodTagService.updateById(prodTag)); return ServerResponseEntity.success(prodTagService.updateById(prodTag));
} }
/** /**
* id * id
* * id
* @param id id * @param id id
* @return * @return ServerResponseEntity<Boolean>truefalse
*/ */
@SysLog("删除商品分组标签") @SysLog("删除商品分组标签")
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('prod:prodTag:delete')") @PreAuthorize("@pms.hasPermission('prod:prodTag:delete')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'prod:prodTag:delete'权限的用户才能访问该接口
public ServerResponseEntity<Boolean> removeById(@PathVariable Long id) { public ServerResponseEntity<Boolean> removeById(@PathVariable Long id) {
// 根据传入的id获取对应的商品分组标签对象
ProdTag prodTag = prodTagService.getById(id); ProdTag prodTag = prodTagService.getById(id);
if (prodTag.getIsDefault() != 0) { if (prodTag.getIsDefault()!= 0) {
// 如果该商品分组标签是默认标签isDefault不为0则抛出异常提示默认标签不能删除
throw new YamiShopBindException("默认标签不能删除"); throw new YamiShopBindException("默认标签不能删除");
} }
// 调用业务层方法先进行相关的商品分组标签清理操作具体逻辑由业务层的removeProdTag方法决定
prodTagService.removeProdTag(); prodTagService.removeProdTag();
return ServerResponseEntity.success(prodTagService.removeById(id)); return ServerResponseEntity.success(prodTagService.removeById(id));
} }
/**
*
*
* @return ServerResponseEntity<List<ProdTag>>
*/
@GetMapping("/listTagList") @GetMapping("/listTagList")
public ServerResponseEntity<List<ProdTag>> listTagList() { public ServerResponseEntity<List<ProdTag>> listTagList() {
return ServerResponseEntity.success(prodTagService.listProdTag()); return ServerResponseEntity.success(prodTagService.listProdTag());
} }
}
}

@ -10,7 +10,6 @@
package com.yami.shop.admin.controller; package com.yami.shop.admin.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yami.shop.common.util.PageParam; import com.yami.shop.common.util.PageParam;
@ -25,74 +24,95 @@ import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid; import jakarta.validation.Valid;
/** /**
* *
* ID
* *
* @author hzm * @author hzm
* @date 2019-04-18 16:28:01 * @date 2019-04-18 16:28:01
*/ */
@RestController @RestController
@AllArgsConstructor @AllArgsConstructor
@RequestMapping("/generator/prodTagReference" ) @RequestMapping("/generator/prodTagReference")
public class ProdTagReferenceController { public class ProdTagReferenceController {
// 通过构造函数注入的方式,引入分组标签引用服务层接口,用于调用具体的业务逻辑方法
private final ProdTagReferenceService prodTagReferenceService; private final ProdTagReferenceService prodTagReferenceService;
/** /**
* *
* @param page * PageParamProdTagReference
* @param prodTagReference * MyBatis Plus LambdaQueryWrapper
* @return *
*
* @param page
* @param prodTagReference
* @return ServerResponseEntity 便
*/ */
@GetMapping("/page" ) @GetMapping("/page")
public ServerResponseEntity<IPage<ProdTagReference>> getProdTagReferencePage(PageParam page, ProdTagReference prodTagReference) { public ServerResponseEntity<IPage<ProdTagReference>> getProdTagReferencePage(PageParam page, ProdTagReference prodTagReference) {
return ServerResponseEntity.success(prodTagReferenceService.page(page, new LambdaQueryWrapper<ProdTagReference>())); return ServerResponseEntity.success(prodTagReferenceService.page(page, new LambdaQueryWrapper<ProdTagReference>()));
} }
/** /**
* id * ID
* @param referenceId id * ID
* @return * ServerResponseEntity 便
*
* @param referenceId ID
* @return ServerResponseEntity
*/ */
@GetMapping("/info/{referenceId}" ) @GetMapping("/info/{referenceId}")
public ServerResponseEntity<ProdTagReference> getById(@PathVariable("referenceId" ) Long referenceId) { public ServerResponseEntity<ProdTagReference> getById(@PathVariable("referenceId") Long referenceId) {
return ServerResponseEntity.success(prodTagReferenceService.getById(referenceId)); return ServerResponseEntity.success(prodTagReferenceService.getById(referenceId));
} }
/** /**
* *
* @param prodTagReference * ProdTagReference
* @return * @SysLog
* 'generator:prodTagReference:save'
* ServerResponseEntity
*
* @param prodTagReference
* @return ServerResponseEntity
*/ */
@SysLog("新增分组标签引用" ) @SysLog("新增分组标签引用")
@PostMapping @PostMapping
@PreAuthorize("@pms.hasPermission('generator:prodTagReference:save')" ) @PreAuthorize("@pms.hasPermission('generator:prodTagReference:save')")
public ServerResponseEntity<Boolean> save(@RequestBody @Valid ProdTagReference prodTagReference) { public ServerResponseEntity<Boolean> save(@RequestBody @Valid ProdTagReference prodTagReference) {
return ServerResponseEntity.success(prodTagReferenceService.save(prodTagReference)); return ServerResponseEntity.success(prodTagReferenceService.save(prodTagReference));
} }
/** /**
* *
* @param prodTagReference * ProdTagReference
* @return * @SysLog
* 'generator:prodTagReference:update'
* ServerResponseEntity
*
* @param prodTagReference
* @return ServerResponseEntity
*/ */
@SysLog("修改分组标签引用" ) @SysLog("修改分组标签引用")
@PutMapping @PutMapping
@PreAuthorize("@pms.hasPermission('generator:prodTagReference:update')" ) @PreAuthorize("@pms.hasPermission('generator:prodTagReference:update')")
public ServerResponseEntity<Boolean> updateById(@RequestBody @Valid ProdTagReference prodTagReference) { public ServerResponseEntity<Boolean> updateById(@RequestBody @Valid ProdTagReference prodTagReference) {
return ServerResponseEntity.success(prodTagReferenceService.updateById(prodTagReference)); return ServerResponseEntity.success(prodTagReferenceService.updateById(prodTagReference));
} }
/** /**
* id * ID
* @param referenceId id * ID
* @return * @SysLog 'generator:prodTagReference:delete'
* ServerResponseEntity
*
* @param referenceId ID
* @return ServerResponseEntity
*/ */
@SysLog("删除分组标签引用" ) @SysLog("删除分组标签引用")
@DeleteMapping("/{referenceId}" ) @DeleteMapping("/{referenceId}")
@PreAuthorize("@pms.hasPermission('generator:prodTagReference:delete')" ) @PreAuthorize("@pms.hasPermission('generator:prodTagReference:delete')")
public ServerResponseEntity<Boolean> removeById(@PathVariable Long referenceId) { public ServerResponseEntity<Boolean> removeById(@PathVariable Long referenceId) {
return ServerResponseEntity.success(prodTagReferenceService.removeById(referenceId)); return ServerResponseEntity.success(prodTagReferenceService.removeById(referenceId));
} }
}
}

@ -36,9 +36,9 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
/** /**
* controller *
*
* *
* @author lgh * @author lgh
*/ */
@ -46,36 +46,54 @@ import java.util.Objects;
@RequestMapping("/prod/prod") @RequestMapping("/prod/prod")
public class ProductController { public class ProductController {
// 注入ProductService用于处理与商品相关的核心业务逻辑例如商品信息的查询、保存、更新、删除等操作。
@Autowired @Autowired
private ProductService productService; private ProductService productService;
// 注入SkuService用于处理商品规格Sku相关的业务逻辑比如根据商品ID获取商品规格列表等操作在商品相关操作中常涉及规格信息的处理。
@Autowired @Autowired
private SkuService skuService; private SkuService skuService;
// 注入ProdTagReferenceService用于获取商品与标签关联相关的业务逻辑例如获取某个商品关联的标签ID列表等操作用于处理商品的分组标签信息。
@Autowired @Autowired
private ProdTagReferenceService prodTagReferenceService; private ProdTagReferenceService prodTagReferenceService;
// 注入BasketService用于处理购物车相关的业务逻辑在商品操作涉及影响购物车缓存的场景下如商品修改、删除等会调用其方法来清除相关缓存保证数据一致性。
@Autowired @Autowired
private BasketService basketService; private BasketService basketService;
/** /**
* * ProductParamPageParam
* ProductServiceID
*
* @PreAuthorize"prod:prod:page"访
*
* @param product ProductParam
* @param page
* @return ServerResponseEntity
*/ */
@GetMapping("/page") @GetMapping("/page")
@PreAuthorize("@pms.hasPermission('prod:prod:page')") @PreAuthorize("@pms.hasPermission('prod:prod:page')")
public ServerResponseEntity<IPage<Product>> page(ProductParam product, PageParam<Product> page) { public ServerResponseEntity<IPage<Product>> page(ProductParam product, PageParam<Product> page) {
// 使用ProductService的page方法进行分页查询传入分页参数和LambdaQueryWrapper构建的查询条件。
// 通过StrUtil.isNotBlank判断商品名称是否不为空若不为空则添加模糊查询条件按照商品名称进行模糊匹配同时根据店铺ID和商品状态如果不为空进行筛选最后按照上架时间倒序排序。
IPage<Product> products = productService.page(page, IPage<Product> products = productService.page(page,
new LambdaQueryWrapper<Product>() new LambdaQueryWrapper<Product>()
.like(StrUtil.isNotBlank(product.getProdName()), Product::getProdName, product.getProdName()) .like(StrUtil.isNotBlank(product.getProdName()), Product::getProdName, product.getProdName())
.eq(Product::getShopId, SecurityUtils.getSysUser().getShopId()) .eq(Product::getShopId, SecurityUtils.getSysUser().getShopId())
.eq(product.getStatus() != null, Product::getStatus, product.getStatus()) .eq(product.getStatus()!= null, Product::getStatus, product.getStatus())
.orderByDesc(Product::getPutawayTime)); .orderByDesc(Product::getPutawayTime));
return ServerResponseEntity.success(products); return ServerResponseEntity.success(products);
} }
/** /**
* * IDIDProductServicegetProductByProdId
* IDIDSkuService
* IDProdTagReferenceService
* @PreAuthorize"prod:prod:info"访
*
* @param prodId
* @return ServerResponseEntityID
*/ */
@GetMapping("/info/{prodId}") @GetMapping("/info/{prodId}")
@PreAuthorize("@pms.hasPermission('prod:prod:info')") @PreAuthorize("@pms.hasPermission('prod:prod:info')")
@ -87,14 +105,21 @@ public class ProductController {
List<Sku> skuList = skuService.listByProdId(prodId); List<Sku> skuList = skuService.listByProdId(prodId);
prod.setSkuList(skuList); prod.setSkuList(skuList);
//获取分组标签 // 获取分组标签通过ProdTagReferenceService的listTagIdByProdId方法获取该商品关联的标签ID列表并设置到商品对象中。
List<Long> listTagId = prodTagReferenceService.listTagIdByProdId(prodId); List<Long> listTagId = prodTagReferenceService.listTagIdByProdId(prodId);
prod.setTagList(listTagId); prod.setTagList(listTagId);
return ServerResponseEntity.success(prod); return ServerResponseEntity.success(prod);
} }
/** /**
* * @ValidProductParam
* checkParamProductParamProductBeanUtil
* JSONIDID
* 1ProductServicesaveProduct
* @PreAuthorize"prod:prod:save"访
*
* @param productParam
* @return ServerResponseEntity
*/ */
@PostMapping @PostMapping
@PreAuthorize("@pms.hasPermission('prod:prod:save')") @PreAuthorize("@pms.hasPermission('prod:prod:save')")
@ -114,7 +139,16 @@ public class ProductController {
} }
/** /**
* * @ValidProductParam
* checkParamProductServicegetProductByProdIdID
* IDID
* ProductParamProductBeanUtilJSON
* ProductServiceupdateProduct
* IDBasketService
* @PreAuthorize"prod:prod:update"访
*
* @param productParam
* @return ServerResponseEntity
*/ */
@PutMapping @PutMapping
@PreAuthorize("@pms.hasPermission('prod:prod:update')") @PreAuthorize("@pms.hasPermission('prod:prod:update')")
@ -151,7 +185,14 @@ public class ProductController {
} }
/** /**
* * IDProductServicegetProductByProdId
* IDID
* ProductServiceremoveProductByProdId
* IDBasketService
*
*
* @param prodId
* @return ServerResponseEntity
*/ */
public ServerResponseEntity<Void> delete(Long prodId) { public ServerResponseEntity<Void> delete(Long prodId) {
Product dbProduct = productService.getProductByProdId(prodId); Product dbProduct = productService.getProductByProdId(prodId);
@ -159,7 +200,7 @@ public class ProductController {
throw new YamiShopBindException("无法获取非本店铺商品信息"); throw new YamiShopBindException("无法获取非本店铺商品信息");
} }
List<Sku> dbSkus = skuService.listByProdId(dbProduct.getProdId()); List<Sku> dbSkus = skuService.listByProdId(dbProduct.getProdId());
// 删除商品 // 删除商品通过ProductService的removeProductByProdId方法根据商品ID删除商品信息。
productService.removeProductByProdId(prodId); productService.removeProductByProdId(prodId);
for (Sku sku : dbSkus) { for (Sku sku : dbSkus) {
@ -177,7 +218,11 @@ public class ProductController {
} }
/** /**
* * IDIDdelete
* @PreAuthorize"prod:prod:delete"访
*
* @param prodIds ID
* @return ServerResponseEntity
*/ */
@DeleteMapping @DeleteMapping
@PreAuthorize("@pms.hasPermission('prod:prod:delete')") @PreAuthorize("@pms.hasPermission('prod:prod:delete')")
@ -189,7 +234,14 @@ public class ProductController {
} }
/** /**
* * IDProductID
* 1ProductServiceupdateById
* ProductServiceIDBasketService
* @PreAuthorize"prod:prod:status"访
*
* @param prodId
* @param prodStatus 10
* @return ServerResponseEntity
*/ */
@PutMapping("/prodStatus") @PutMapping("/prodStatus")
@PreAuthorize("@pms.hasPermission('prod:prod:status')") @PreAuthorize("@pms.hasPermission('prod:prod:status')")
@ -210,26 +262,8 @@ public class ProductController {
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
private void checkParam(ProductParam productParam) { /**
if (CollectionUtil.isEmpty(productParam.getTagList())) { * ProductParam
throw new YamiShopBindException("请选择产品分组"); * 1.
} * 2.
* 3.
Product.DeliveryModeVO deliveryMode = productParam.getDeliveryModeVo();
boolean hasDeliverMode = deliveryMode != null
&& (deliveryMode.getHasShopDelivery() || deliveryMode.getHasUserPickUp());
if (!hasDeliverMode) {
throw new YamiShopBindException("请选择配送方式");
}
List<Sku> skuList = productParam.getSkuList();
boolean isAllUnUse = true;
for (Sku sku : skuList) {
if (sku.getStatus() == 1) {
isAllUnUse = false;
}
}
if (isAllUnUse) {
throw new YamiShopBindException("至少要启用一种商品规格");
}
}
}

@ -8,6 +8,7 @@
* *
*/ */
// 该类所属的包名,表明其位于管理端控制器相关的包下,用于对店铺详情相关操作进行控制和处理
package com.yami.shop.admin.controller; package com.yami.shop.admin.controller;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
@ -30,131 +31,183 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* ShopDetailControllerSpring RESTfulShopDetail
*
* *
* @author lgh on 2018/08/29. * @author lgh on 2018/08/29.
*/ */
@RestController @RestController
// 定义该控制器类的基础请求路径,所有该类中的接口请求路径都将以此为前缀,表明是与店铺详情相关的操作接口
@RequestMapping("/shop/shopDetail") @RequestMapping("/shop/shopDetail")
public class ShopDetailController { public class ShopDetailController {
// 通过Spring的依赖注入机制自动注入ShopDetailService的实例以便调用其提供的与店铺详情相关的业务逻辑方法
@Autowired @Autowired
private ShopDetailService shopDetailService; private ShopDetailService shopDetailService;
/**
/** *
* * isDistributionShopDetailIDSecurityUtilsID
*/ * ShopDetailServiceupdateByIdremoveShopDetailCacheByShopIdID
@PutMapping("/isDistribution") * ServerResponseEntityVoid
public ServerResponseEntity<Void> updateIsDistribution(@RequestParam Integer isDistribution){ *
ShopDetail shopDetail=new ShopDetail(); * @param isDistribution 01
shopDetail.setShopId(SecurityUtils.getSysUser().getShopId()); * @return ServerResponseEntityVoid
shopDetail.setIsDistribution(isDistribution); */
shopDetailService.updateById(shopDetail); @PutMapping("/isDistribution")
// 更新完成后删除缓存 public ServerResponseEntity<Void> updateIsDistribution(@RequestParam Integer isDistribution) {
shopDetailService.removeShopDetailCacheByShopId(shopDetail.getShopId()); ShopDetail shopDetail = new ShopDetail();
return ServerResponseEntity.success(); shopDetail.setShopId(SecurityUtils.getSysUser().getShopId());
} shopDetail.setIsDistribution(isDistribution);
/** shopDetailService.updateById(shopDetail);
* // 更新完成后删除缓存
*/ shopDetailService.removeShopDetailCacheByShopId(shopDetail.getShopId());
@GetMapping("/info") return ServerResponseEntity.success();
@PreAuthorize("@pms.hasPermission('shop:shopDetail:info')") }
public ServerResponseEntity<ShopDetail> info(){
ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(SecurityUtils.getSysUser().getShopId()); /**
return ServerResponseEntity.success(shopDetail); *
} * SecurityUtilsIDShopDetailServicegetShopDetailByShopId
* ServerResponseEntity
*
/** * @return ServerResponseEntityShopDetail
* */
*/ @GetMapping("/info")
@PreAuthorize("@pms.hasPermission('shop:shopDetail:info')")
public ServerResponseEntity<ShopDetail> info() {
ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(SecurityUtils.getSysUser().getShopId());
return ServerResponseEntity.success(shopDetail);
}
/**
*
* ShopDetailPageParam
* 使ShopDetailServicepageLambdaQueryWrapper
* IDServerResponseEntity
*
* @param shopDetail
* @param page
* @return ServerResponseEntityIPage<ShopDetail>
*/
@GetMapping("/page") @GetMapping("/page")
@PreAuthorize("@pms.hasPermission('shop:shopDetail:page')") @PreAuthorize("@pms.hasPermission('shop:shopDetail:page')")
public ServerResponseEntity<IPage<ShopDetail>> page(ShopDetail shopDetail,PageParam<ShopDetail> page){ public ServerResponseEntity<IPage<ShopDetail>> page(ShopDetail shopDetail, PageParam<ShopDetail> page) {
IPage<ShopDetail> shopDetails = shopDetailService.page(page, IPage<ShopDetail> shopDetails = shopDetailService.page(page,
new LambdaQueryWrapper<ShopDetail>() new LambdaQueryWrapper<ShopDetail>()
.like(StrUtil.isNotBlank(shopDetail.getShopName()),ShopDetail::getShopName,shopDetail.getShopName()) .like(StrUtil.isNotBlank(shopDetail.getShopName()), ShopDetail::getShopName, shopDetail.getShopName())
.orderByDesc(ShopDetail::getShopId)); .orderByDesc(ShopDetail::getShopId));
return ServerResponseEntity.success(shopDetails); return ServerResponseEntity.success(shopDetails);
} }
/** /**
* * ID
*/ * IDShopDetailServicegetShopDetailByShopId
@GetMapping("/info/{shopId}") * ServerResponseEntity
@PreAuthorize("@pms.hasPermission('shop:shopDetail:info')") *
public ServerResponseEntity<ShopDetail> info(@PathVariable("shopId") Long shopId){ * @param shopId ID
ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(shopId); * @return ServerResponseEntityShopDetail
// 店铺图片 */
return ServerResponseEntity.success(shopDetail); @GetMapping("/info/{shopId}")
} @PreAuthorize("@pms.hasPermission('shop:shopDetail:info')")
public ServerResponseEntity<ShopDetail> info(@PathVariable("shopId") Long shopId) {
/** ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(shopId);
* // 店铺图片(此处可根据实际业务需求进一步处理店铺图片相关逻辑,目前只是简单返回店铺详情对象)
*/ return ServerResponseEntity.success(shopDetail);
@PostMapping }
@PreAuthorize("@pms.hasPermission('shop:shopDetail:save')")
public ServerResponseEntity<Void> save(@Valid ShopDetailParam shopDetailParam){ /**
ShopDetail shopDetail = BeanUtil.copyProperties(shopDetailParam, ShopDetail.class); *
shopDetail.setCreateTime(new Date()); * ShopDetailParamBeanUtilShopDetail
shopDetail.setShopStatus(1); * new Date()1
shopDetailService.save(shopDetail); * ShopDetailServicesaveServerResponseEntityVoid
return ServerResponseEntity.success(); *
} * @param shopDetailParam
* @return ServerResponseEntityVoid
/** */
* @PostMapping
*/ @PreAuthorize("@pms.hasPermission('shop:shopDetail:save')")
@PutMapping public ServerResponseEntity<Void> save(@Valid ShopDetailParam shopDetailParam) {
@PreAuthorize("@pms.hasPermission('shop:shopDetail:update')") ShopDetail shopDetail = BeanUtil.copyProperties(shopDetailParam, ShopDetail.class);
public ServerResponseEntity<Void> update(@Valid ShopDetailParam shopDetailParam){ shopDetail.setCreateTime(new Date());
ShopDetail daShopDetail = shopDetailService.getShopDetailByShopId(shopDetailParam.getShopId()); shopDetail.setShopStatus(1);
ShopDetail shopDetail = BeanUtil.copyProperties(shopDetailParam, ShopDetail.class); shopDetailService.save(shopDetail);
shopDetail.setUpdateTime(new Date()); return ServerResponseEntity.success();
shopDetailService.updateShopDetail(shopDetail,daShopDetail); }
return ServerResponseEntity.success();
} /**
*
/** * ShopDetailServicegetShopDetailByShopIdShopDetailParamIDdaShopDetail
* * ShopDetailParamBeanUtilShopDetailnew Date()
*/ * ShopDetailServiceupdateShopDetail
@DeleteMapping("/{id}") * ServerResponseEntityVoid
@PreAuthorize("@pms.hasPermission('shop:shopDetail:delete')") *
public ServerResponseEntity<Void> delete(@PathVariable Long id){ * @param shopDetailParam
shopDetailService.deleteShopDetailByShopId(id); * @return ServerResponseEntityVoid
return ServerResponseEntity.success(); */
} @PutMapping
@PreAuthorize("@pms.hasPermission('shop:shopDetail:update')")
/** public ServerResponseEntity<Void> update(@Valid ShopDetailParam shopDetailParam) {
* ShopDetail daShopDetail = shopDetailService.getShopDetailByShopId(shopDetailParam.getShopId());
*/ ShopDetail shopDetail = BeanUtil.copyProperties(shopDetailParam, ShopDetail.class);
@PutMapping("/shopStatus") shopDetail.setUpdateTime(new Date());
@PreAuthorize("@pms.hasPermission('shop:shopDetail:shopStatus')") shopDetailService.updateShopDetail(shopDetail, daShopDetail);
public ServerResponseEntity<Void> shopStatus(@RequestParam Long shopId,@RequestParam Integer shopStatus){ return ServerResponseEntity.success();
ShopDetail shopDetail = new ShopDetail(); }
shopDetail.setShopId(shopId);
shopDetail.setShopStatus(shopStatus); /**
shopDetailService.updateById(shopDetail); * ID
// 更新完成后删除缓存 * IDShopDetailServicedeleteShopDetailByShopId
shopDetailService.removeShopDetailCacheByShopId(shopDetail.getShopId()); * ServerResponseEntityVoid
return ServerResponseEntity.success(); *
} * @param id ID
* @return ServerResponseEntityVoid
*/
/** @DeleteMapping("/{id}")
* @PreAuthorize("@pms.hasPermission('shop:shopDetail:delete')")
*/ public ServerResponseEntity<Void> delete(@PathVariable Long id) {
shopDetailService.deleteShopDetailByShopId(id);
return ServerResponseEntity.success();
}
/**
*
* IDshopIdshopStatusShopDetailID
* ShopDetailServiceupdateByIdremoveShopDetailCacheByShopIdID
* ServerResponseEntityVoid
*
* @param shopId ID
* @param shopStatus
* @return ServerResponseEntityVoid
*/
@PutMapping("/shopStatus")
@PreAuthorize("@pms.hasPermission('shop:shopDetail:shopStatus')")
public ServerResponseEntity<Void> shopStatus(@RequestParam Long shopId, @RequestParam Integer shopStatus) {
ShopDetail shopDetail = new ShopDetail();
shopDetail.setShopId(shopId);
shopDetail.setShopStatus(shopStatus);
shopDetailService.updateById(shopDetail);
// 更新完成后删除缓存
shopDetailService.removeShopDetailCacheByShopId(shopDetail.getShopId());
return ServerResponseEntity.success();
}
/**
*
* ShopDetailServicelistJava 8Stream
* IDShopDetail
* ServerResponseEntity
*
* @return ServerResponseEntityList<ShopDetail>ShopDetailID
*/
@GetMapping("/listShopName") @GetMapping("/listShopName")
public ServerResponseEntity<List<ShopDetail>> listShopName(){ public ServerResponseEntity<List<ShopDetail>> listShopName() {
List<ShopDetail> list = shopDetailService.list().stream().map((dbShopDetail) ->{ List<ShopDetail> list = shopDetailService.list().stream().map((dbShopDetail) -> {
ShopDetail shopDetail = new ShopDetail(); ShopDetail shopDetail = new ShopDetail();
shopDetail.setShopId(dbShopDetail.getShopId()); shopDetail.setShopId(dbShopDetail.getShopId());
shopDetail.setShopName(dbShopDetail.getShopName()); shopDetail.setShopName(dbShopDetail.getShopName());
return shopDetail; return shopDetail;
}).collect(Collectors.toList()); }).collect(Collectors.toList());
return ServerResponseEntity.success(list); return ServerResponseEntity.success(list);
} }
} }

@ -31,95 +31,137 @@ import java.util.Objects;
/** /**
* *
* * idid
* @author lgh * @author lgh
*/ */
@RestController @RestController
@RequestMapping("/prod/spec") @RequestMapping("/prod/spec")
public class SpecController { public class SpecController {
// 通过Spring的自动注入机制注入ProdPropService对象用于调用业务层处理商品属性相关的业务逻辑
@Autowired @Autowired
private ProdPropService prodPropService; private ProdPropService prodPropService;
// 通过Spring的自动注入机制注入ProdPropValueService对象用于调用业务层处理商品属性值相关的业务逻辑
@Autowired @Autowired
private ProdPropValueService prodPropValueService; private ProdPropValueService prodPropValueService;
/** /**
* *
*
* @param prodProp
* @param page
* @return ServerResponseEntity<IPage<ProdProp>>
*/ */
@GetMapping("/page") @GetMapping("/page")
@PreAuthorize("@pms.hasPermission('prod:spec:page')") @PreAuthorize("@pms.hasPermission('prod:spec:page')")
public ServerResponseEntity<IPage<ProdProp>> page(ProdProp prodProp,PageParam<ProdProp> page) { // 使用 @PreAuthorize 注解进行权限校验,只有拥有'prod:spec:page'权限的用户才能访问该接口
public ServerResponseEntity<IPage<ProdProp>> page(ProdProp prodProp, PageParam<ProdProp> page) {
// 设置规格的规则为指定的规格类型SPEC这里通过枚举值来表示
prodProp.setRule(ProdPropRule.SPEC.value()); prodProp.setRule(ProdPropRule.SPEC.value());
// 设置规格所属店铺的id从当前登录用户信息中获取
prodProp.setShopId(SecurityUtils.getSysUser().getShopId()); prodProp.setShopId(SecurityUtils.getSysUser().getShopId());
// 调用业务层方法获取规格以及对应规格值的分页数据具体逻辑由业务层的pagePropAndValue方法决定
IPage<ProdProp> list = prodPropService.pagePropAndValue(prodProp, page); IPage<ProdProp> list = prodPropService.pagePropAndValue(prodProp, page);
return ServerResponseEntity.success(list); return ServerResponseEntity.success(list);
} }
/** /**
* *
*
* @return ServerResponseEntity<List<ProdProp>>
*/ */
@GetMapping("/list") @GetMapping("/list")
public ServerResponseEntity<List<ProdProp>> list() { public ServerResponseEntity<List<ProdProp>> list() {
List<ProdProp> list = prodPropService.list(new LambdaQueryWrapper<ProdProp>().eq(ProdProp::getRule, ProdPropRule.SPEC.value()).eq(ProdProp::getShopId, SecurityUtils.getSysUser().getShopId())); // 使用MyBatis Plus的LambdaQueryWrapper构建查询条件筛选出规则为指定规格类型SPEC且属于当前登录用户店铺的规格信息
List<ProdProp> list = prodPropService.list(new LambdaQueryWrapper<ProdProp>()
.eq(ProdProp::getRule, ProdPropRule.SPEC.value())
.eq(ProdProp::getShopId, SecurityUtils.getSysUser().getShopId()));
return ServerResponseEntity.success(list); return ServerResponseEntity.success(list);
} }
/** /**
* id * id
* id
* @param specId id
* @return ServerResponseEntity<List<ProdPropValue>>id
*/ */
@GetMapping("/listSpecValue/{specId}") @GetMapping("/listSpecValue/{specId}")
public ServerResponseEntity<List<ProdPropValue>> listSpecValue(@PathVariable("specId") Long specId) { public ServerResponseEntity<List<ProdPropValue>> listSpecValue(@PathVariable("specId") Long specId) {
List<ProdPropValue> list = prodPropValueService.list(new LambdaQueryWrapper<ProdPropValue>().eq(ProdPropValue::getPropId, specId)); // 使用MyBatis Plus的LambdaQueryWrapper构建查询条件筛选出属性id即规格id为传入specId的规格值信息
List<ProdPropValue> list = prodPropValueService.list(new LambdaQueryWrapper<ProdPropValue>()
.eq(ProdPropValue::getPropId, specId));
return ServerResponseEntity.success(list); return ServerResponseEntity.success(list);
} }
/** /**
* *
*
* @param prodProp
* @return ServerResponseEntity<Void>
*/ */
@PostMapping @PostMapping
@PreAuthorize("@pms.hasPermission('prod:spec:save')") @PreAuthorize("@pms.hasPermission('prod:spec:save')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'prod:spec:save'权限的用户才能访问该接口
public ServerResponseEntity<Void> save(@Valid @RequestBody ProdProp prodProp) { public ServerResponseEntity<Void> save(@Valid @RequestBody ProdProp prodProp) {
// 设置规格的规则为指定的规格类型SPEC这里通过枚举值来表示
prodProp.setRule(ProdPropRule.SPEC.value()); prodProp.setRule(ProdPropRule.SPEC.value());
// 设置规格所属店铺的id从当前登录用户信息中获取
prodProp.setShopId(SecurityUtils.getSysUser().getShopId()); prodProp.setShopId(SecurityUtils.getSysUser().getShopId());
// 调用业务层方法保存规格以及对应的规格值信息具体逻辑由业务层的saveProdPropAndValues方法决定
prodPropService.saveProdPropAndValues(prodProp); prodPropService.saveProdPropAndValues(prodProp);
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
/** /**
* *
*
* @param prodProp
* @return ServerResponseEntity<Void>
*/ */
@PutMapping @PutMapping
@PreAuthorize("@pms.hasPermission('prod:spec:update')") @PreAuthorize("@pms.hasPermission('prod:spec:update')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'prod:spec:update'权限的用户才能访问该接口
public ServerResponseEntity<Void> update(@Valid @RequestBody ProdProp prodProp) { public ServerResponseEntity<Void> update(@Valid @RequestBody ProdProp prodProp) {
// 根据传入的规格id从数据库中获取对应的规格对象用于后续权限校验等操作
ProdProp dbProdProp = prodPropService.getById(prodProp.getPropId()); ProdProp dbProdProp = prodPropService.getById(prodProp.getPropId());
if (!Objects.equals(dbProdProp.getShopId(), SecurityUtils.getSysUser().getShopId())) { if (!Objects.equals(dbProdProp.getShopId(), SecurityUtils.getSysUser().getShopId())) {
// 如果当前登录用户所属店铺id与要修改的规格所属店铺id不一致则抛出异常提示没有权限获取该商品规格信息
throw new YamiShopBindException("没有权限获取该商品规格信息"); throw new YamiShopBindException("没有权限获取该商品规格信息");
} }
// 设置规格的规则为指定的规格类型SPEC这里通过枚举值来表示
prodProp.setRule(ProdPropRule.SPEC.value()); prodProp.setRule(ProdPropRule.SPEC.value());
// 设置规格所属店铺的id从当前登录用户信息中获取
prodProp.setShopId(SecurityUtils.getSysUser().getShopId()); prodProp.setShopId(SecurityUtils.getSysUser().getShopId());
// 调用业务层方法更新规格以及对应的规格值信息具体逻辑由业务层的updateProdPropAndValues方法决定
prodPropService.updateProdPropAndValues(prodProp); prodPropService.updateProdPropAndValues(prodProp);
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
/** /**
* *
* id
* @param id id
* @return ServerResponseEntity<Void>
*/ */
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('prod:spec:delete')") @PreAuthorize("@pms.hasPermission('prod:spec:delete')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'prod:spec:delete'权限的用户才能访问该接口
public ServerResponseEntity<Void> delete(@PathVariable Long id) { public ServerResponseEntity<Void> delete(@PathVariable Long id) {
// 调用业务层方法删除规格以及对应的规格值信息传入规格id、规格规则类型以及当前登录用户所属店铺id等参数具体逻辑由业务层的deleteProdPropAndValues方法决定
prodPropService.deleteProdPropAndValues(id, ProdPropRule.SPEC.value(), SecurityUtils.getSysUser().getShopId()); prodPropService.deleteProdPropAndValues(id, ProdPropRule.SPEC.value(), SecurityUtils.getSysUser().getShopId());
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
/** /**
* id * id
* id
* @return ServerResponseEntity<Long>id0L
*/ */
@GetMapping("/listSpecMaxValueId") @GetMapping("/listSpecMaxValueId")
public ServerResponseEntity<Long> listSpecMaxValueId() { public ServerResponseEntity<Long> listSpecMaxValueId() {
// 使用MyBatis Plus的LambdaQueryWrapper构建查询条件按照规格值的自增id降序排列并通过last方法限制只取第一条记录即获取最大的自增id对应的规格值对象
ProdPropValue propValue = prodPropValueService.getOne(new LambdaQueryWrapper<ProdPropValue>() ProdPropValue propValue = prodPropValueService.getOne(new LambdaQueryWrapper<ProdPropValue>()
.orderByDesc(ProdPropValue::getValueId).last("limit 1")); .orderByDesc(ProdPropValue::getValueId).last("limit 1"));
return ServerResponseEntity.success(Objects.isNull(propValue) ? 0L : propValue.getValueId()); return ServerResponseEntity.success(Objects.isNull(propValue)? 0L : propValue.getValueId());
} }
}
}

@ -26,32 +26,50 @@ import java.util.Date;
import java.util.List; import java.util.List;
/** /**
* Transport
*
*
* @author lgh on 2018/11/16. * @author lgh on 2018/11/16.
*/ */
@RestController @RestController
@RequestMapping("/shop/transport") @RequestMapping("/shop/transport")
public class TransportController { public class TransportController {
// 自动注入运费模板服务层接口,通过该接口调用具体的业务逻辑方法来处理运费模板相关业务
@Autowired @Autowired
private TransportService transportService; private TransportService transportService;
/** /**
* *
* TransportPageParam
* 'shop:transport:page' 访
* ID
*
* @param transport
* @param page
* @return ServerResponseEntity 便
*/ */
@GetMapping("/page") @GetMapping("/page")
@PreAuthorize("@pms.hasPermission('shop:transport:page')") @PreAuthorize("@pms.hasPermission('shop:transport:page')")
public ServerResponseEntity<IPage<Transport>> page(Transport transport,PageParam<Transport> page) { public ServerResponseEntity<IPage<Transport>> page(Transport transport, PageParam<Transport> page) {
// 获取当前登录用户所属店铺的 ID用于筛选当前店铺的运费模板数据
Long shopId = SecurityUtils.getSysUser().getShopId(); Long shopId = SecurityUtils.getSysUser().getShopId();
IPage<Transport> transports = transportService.page(page, IPage<Transport> transports = transportService.page(page,
new LambdaQueryWrapper<Transport>() new LambdaQueryWrapper<Transport>()
.eq(Transport::getShopId, shopId) // 根据店铺 ID 精确匹配,筛选出属于当前店铺的运费模板记录
.like(StringUtils.isNotBlank(transport.getTransName()), Transport::getTransName, transport.getTransName())); .eq(Transport::getShopId, shopId)
// 如果传入的运费模板名称不为空,则进行模糊匹配名称字段进行查询,实现根据名称筛选的功能
.like(StringUtils.isNotBlank(transport.getTransName()), Transport::getTransName, transport.getTransName()));
return ServerResponseEntity.success(transports); return ServerResponseEntity.success(transports);
} }
/** /**
* * ID
* ID
* ServerResponseEntity 'shop:transport:info' 访
*
* @param id ID
* @return ServerResponseEntity
*/ */
@GetMapping("/info/{id}") @GetMapping("/info/{id}")
@PreAuthorize("@pms.hasPermission('shop:transport:info')") @PreAuthorize("@pms.hasPermission('shop:transport:info')")
@ -61,13 +79,20 @@ public class TransportController {
} }
/** /**
* *
* Transport ID
* 'shop:transport:save'
*
* @param transport
* @return ServerResponseEntity
*/ */
@PostMapping @PostMapping
@PreAuthorize("@pms.hasPermission('shop:transport:save')") @PreAuthorize("@pms.hasPermission('shop:transport:save')")
public ServerResponseEntity<Void> save(@RequestBody Transport transport) { public ServerResponseEntity<Void> save(@RequestBody Transport transport) {
// 获取当前登录用户所属店铺的 ID设置到运费模板对象中明确该运费模板所属的店铺
Long shopId = SecurityUtils.getSysUser().getShopId(); Long shopId = SecurityUtils.getSysUser().getShopId();
transport.setShopId(shopId); transport.setShopId(shopId);
// 创建当前时间对象,作为运费模板的创建时间
Date createTime = new Date(); Date createTime = new Date();
transport.setCreateTime(createTime); transport.setCreateTime(createTime);
transportService.insertTransportAndTransfee(transport); transportService.insertTransportAndTransfee(transport);
@ -75,7 +100,12 @@ public class TransportController {
} }
/** /**
* *
* Transport
* 'shop:transport:update' ServerResponseEntity
*
* @param transport
* @return ServerResponseEntity
*/ */
@PutMapping @PutMapping
@PreAuthorize("@pms.hasPermission('shop:transport:update')") @PreAuthorize("@pms.hasPermission('shop:transport:update')")
@ -85,28 +115,37 @@ public class TransportController {
} }
/** /**
* *
* ID
* ID 'shop:transport:delete'
* ServerResponseEntity
*
* @param ids ID
* @return ServerResponseEntity
*/ */
@DeleteMapping @DeleteMapping
@PreAuthorize("@pms.hasPermission('shop:transport:delete')") @PreAuthorize("@pms.hasPermission('shop:transport:delete')")
public ServerResponseEntity<Void> delete(@RequestBody Long[] ids) { public ServerResponseEntity<Void> delete(@RequestBody Long[] ids) {
transportService.deleteTransportAndTransfeeAndTranscity(ids); transportService.deleteTransportAndTransfeeAndTranscity(ids);
// 删除运费模板的缓存 // 删除运费模板的缓存,确保缓存数据与数据库最新数据一致,避免缓存数据造成的不一致问题
for (Long id : ids) { for (Long id : ids) {
transportService.removeTransportAndAllItemsCache(id); transportService.removeTransportAndAllItemsCache(id);
} }
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
/** /**
* *
* ID
* ServerResponseEntity 便使
*
* @return ServerResponseEntity
*/ */
@GetMapping("/list") @GetMapping("/list")
public ServerResponseEntity<List<Transport>> list() { public ServerResponseEntity<List<Transport>> list() {
// 获取当前登录用户所属店铺的 ID用于筛选当前店铺的运费模板数据
Long shopId = SecurityUtils.getSysUser().getShopId(); Long shopId = SecurityUtils.getSysUser().getShopId();
List<Transport> list = transportService.list(new LambdaQueryWrapper<Transport>().eq(Transport::getShopId, shopId)); List<Transport> list = transportService.list(new LambdaQueryWrapper<Transport>().eq(Transport::getShopId, shopId));
return ServerResponseEntity.success(list); return ServerResponseEntity.success(list);
} }
}
}

@ -24,7 +24,9 @@ import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid; import jakarta.validation.Valid;
/** /**
* *
* ID
* 便
* *
* @author hzm * @author hzm
* @date 2019-04-15 10:49:40 * @date 2019-04-15 10:49:40
@ -34,26 +36,31 @@ import jakarta.validation.Valid;
@RequestMapping("/user/addr") @RequestMapping("/user/addr")
public class UserAddrController { public class UserAddrController {
// 通过lombok的@AllArgsConstructor注解注入UserAddrService用于处理与用户地址相关的业务逻辑
// 例如地址信息的查询、保存、更新、删除等操作。
private final UserAddrService userAddrService; private final UserAddrService userAddrService;
/** /**
* * PageParamUserAddr
* UserAddrServiceLambdaQueryWrapper
*
* *
* @param page * @param page
* @param userAddr * @param userAddr 使
* @return * @return ServerResponseEntity
*/ */
@GetMapping("/page") @GetMapping("/page")
public ServerResponseEntity<IPage<UserAddr>> getUserAddrPage(PageParam page, UserAddr userAddr) { public ServerResponseEntity<IPage<UserAddr>> getUserAddrPage(PageParam page, UserAddr userAddr) {
// 使用UserAddrService的page方法进行分页查询传入分页参数和一个空的LambdaQueryWrapper目前无额外筛选条件获取分页结果。
return ServerResponseEntity.success(userAddrService.page(page, new LambdaQueryWrapper<UserAddr>())); return ServerResponseEntity.success(userAddrService.page(page, new LambdaQueryWrapper<UserAddr>()));
} }
/** /**
* id * IDIDUserAddrServicegetById
*
* *
* @param addrId id * @param addrId
* @return * @return ServerResponseEntity
*/ */
@GetMapping("/info/{addrId}") @GetMapping("/info/{addrId}")
public ServerResponseEntity<UserAddr> getById(@PathVariable("addrId") Long addrId) { public ServerResponseEntity<UserAddr> getById(@PathVariable("addrId") Long addrId) {
@ -61,10 +68,12 @@ public class UserAddrController {
} }
/** /**
* * @ValidUserAddr
* UserAddrServicesave
* @PreAuthorize"user:addr:save"访使@SysLog便
* *
* @param userAddr * @param userAddr
* @return * @return ServerResponseEntitytruefalse
*/ */
@SysLog("新增用户地址管理") @SysLog("新增用户地址管理")
@PostMapping @PostMapping
@ -74,10 +83,12 @@ public class UserAddrController {
} }
/** /**
* * @ValidUserAddr
* UserAddrServiceupdateById
* @PreAuthorize"user:addr:update"访使@SysLog便
* *
* @param userAddr * @param userAddr
* @return * @return ServerResponseEntitytruefalse
*/ */
@SysLog("修改用户地址管理") @SysLog("修改用户地址管理")
@PutMapping @PutMapping
@ -87,10 +98,12 @@ public class UserAddrController {
} }
/** /**
* id * IDIDUserAddrServiceremoveById
*
* @PreAuthorize"user:addr:delete"访使@SysLog便
* *
* @param addrId id * @param addrId
* @return * @return ServerResponseEntitytruefalse
*/ */
@SysLog("删除用户地址管理") @SysLog("删除用户地址管理")
@DeleteMapping("/{addrId}") @DeleteMapping("/{addrId}")
@ -99,4 +112,4 @@ public class UserAddrController {
return ServerResponseEntity.success(userAddrService.removeById(addrId)); return ServerResponseEntity.success(userAddrService.removeById(addrId));
} }
} }

@ -8,6 +8,7 @@
* *
*/ */
// 该类所属的包名,表明此控制器类位于管理端相关的包下,用于处理用户相关的后台管理操作
package com.yami.shop.admin.controller; package com.yami.shop.admin.controller;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
@ -24,57 +25,88 @@ import org.springframework.web.bind.annotation.*;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
/** /**
* UserControllerSpring RESTfulUser
*
* @author lgh on 2018/10/16. * @author lgh on 2018/10/16.
*/ */
@RestController @RestController
// 定义该控制器类的基础请求路径,所有该类中的接口请求路径都将以此为前缀,表明是针对用户的管理操作接口
@RequestMapping("/admin/user") @RequestMapping("/admin/user")
public class UserController { public class UserController {
// 通过Spring的依赖注入机制自动注入UserService的实例以便调用其提供的与用户相关的业务逻辑方法
@Autowired @Autowired
private UserService userService; private UserService userService;
/** /**
* *
* UserPageParam
* 使UserServicepageLambdaQueryWrapper
*
*
* ServerResponseEntity
*
* @param user
* @param page
* @return ServerResponseEntityIPage<User>
*/ */
@GetMapping("/page") @GetMapping("/page")
@PreAuthorize("@pms.hasPermission('admin:user:page')") @PreAuthorize("@pms.hasPermission('admin:user:page')")
public ServerResponseEntity<IPage<User>> page(User user,PageParam<User> page) { public ServerResponseEntity<IPage<User>> page(User user, PageParam<User> page) {
IPage<User> userPage = userService.page(page, new LambdaQueryWrapper<User>() IPage<User> userPage = userService.page(page, new LambdaQueryWrapper<User>()
.like(StrUtil.isNotBlank(user.getNickName()), User::getNickName, user.getNickName()) .like(StrUtil.isNotBlank(user.getNickName()), User::getNickName, user.getNickName())
.eq(user.getStatus() != null, User::getStatus, user.getStatus())); .eq(user.getStatus()!= null, User::getStatus, user.getStatus()));
for (User userResult : userPage.getRecords()) { for (User userResult : userPage.getRecords()) {
userResult.setNickName(StrUtil.isBlank(userResult.getNickName()) ? "" : userResult.getNickName()); userResult.setNickName(StrUtil.isBlank(userResult.getNickName())? "" : userResult.getNickName());
} }
return ServerResponseEntity.success(userPage); return ServerResponseEntity.success(userPage);
} }
/** /**
* *
* ID
* UserServicegetById
* ServerResponseEntity
*
* @param userId ID
* @return ServerResponseEntityUser
*/ */
@GetMapping("/info/{userId}") @GetMapping("/info/{userId}")
@PreAuthorize("@pms.hasPermission('admin:user:info')") @PreAuthorize("@pms.hasPermission('admin:user:info')")
public ServerResponseEntity<User> info(@PathVariable("userId") String userId) { public ServerResponseEntity<User> info(@PathVariable("userId") String userId) {
User user = userService.getById(userId); User user = userService.getById(userId);
user.setNickName(StrUtil.isBlank(user.getNickName()) ? "" : user.getNickName()); user.setNickName(StrUtil.isBlank(user.getNickName())? "" : user.getNickName());
return ServerResponseEntity.success(user); return ServerResponseEntity.success(user);
} }
/** /**
* *
*
* new Date()
* UserServiceupdateByIdID
* ServerResponseEntityVoid
*
* @param user
* @return ServerResponseEntityVoid
*/ */
@PutMapping @PutMapping
@PreAuthorize("@pms.hasPermission('admin:user:update')") @PreAuthorize("@pms.hasPermission('admin:user:update')")
public ServerResponseEntity<Void> update(@RequestBody User user) { public ServerResponseEntity<Void> update(@RequestBody User user) {
user.setModifyTime(new Date()); user.setModifyTime(new Date());
user.setNickName(StrUtil.isBlank(user.getNickName()) ? "" : user.getNickName()); user.setNickName(StrUtil.isBlank(user.getNickName())? "" : user.getNickName());
userService.updateById(user); userService.updateById(user);
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
/** /**
* *
* IDList
* UserServiceremoveByIdsID
* ServerResponseEntityVoid
*
* @param userIds ID
* @return ServerResponseEntityVoid
*/ */
@DeleteMapping @DeleteMapping
@PreAuthorize("@pms.hasPermission('admin:user:delete')") @PreAuthorize("@pms.hasPermission('admin:user:delete')")
@ -82,4 +114,4 @@ public class UserController {
userService.removeByIds(Arrays.asList(userIds)); userService.removeByIds(Arrays.asList(userIds));
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
} }

@ -27,31 +27,47 @@ import org.springframework.stereotype.Component;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
/** /**
* @author FrozenWatermelon *
* xxl-jobjava * xxl-jobjava
* @see com.yami.shop.admin.config.XxlJobConfig * @see com.yami.shop.admin.config.XxlJobConfig
*
* @author FrozenWatermelon
*/ */
@Component("orderTask") @Component("orderTask")
public class OrderTask { public class OrderTask {
// 创建一个日志记录器,用于记录定时任务执行过程中的相关信息,方便后续查看任务执行情况以及排查问题。
private static final Logger logger = LoggerFactory.getLogger(OrderTask.class); private static final Logger logger = LoggerFactory.getLogger(OrderTask.class);
// 注入OrderService用于处理与订单相关的业务逻辑例如查询订单、取消订单、确认订单等操作。
@Autowired @Autowired
private OrderService orderService; private OrderService orderService;
// 注入ProductService用于处理与商品相关的业务逻辑在订单操作涉及影响商品缓存的场景下如订单取消、确认收货等会调用其方法来清除相关商品缓存保证数据一致性。
@Autowired @Autowired
private ProductService productService; private ProductService productService;
// 注入SkuService用于处理与商品规格Sku相关的业务逻辑同样在订单操作涉及影响商品规格缓存的场景下会调用其方法来清除相关规格缓存保证数据一致性。
@Autowired @Autowired
private SkuService skuService; private SkuService skuService;
/**
* @XxlJobXXL-JOB"cancelOrder"
*
* 1.
* 2. OrderStatus.UNPAY.value()30
* 3.
* 4. OrderServicecancelOrders
* 5. OrderItemProductServiceSkuService
*
*/
@XxlJob("cancelOrder") @XxlJob("cancelOrder")
public void cancelOrder(){ public void cancelOrder() {
Date now = new Date(); Date now = new Date();
logger.info("取消超时未支付订单。。。"); logger.info("取消超时未支付订单。。。");
// 获取30分钟之前未支付的订单 // 获取30分钟之前未支付的订单通过OrderService的listOrderAndOrderItems方法传入未支付状态值和30分钟之前的时间作为筛选条件进行查询。
List<Order> orders = orderService.listOrderAndOrderItems(OrderStatus.UNPAY.value(),DateUtil.offsetMinute(now, -30)); List<Order> orders = orderService.listOrderAndOrderItems(OrderStatus.UNPAY.value(), DateUtil.offsetMinute(now, -30));
if (CollectionUtil.isEmpty(orders)) { if (CollectionUtil.isEmpty(orders)) {
return; return;
} }
@ -60,20 +76,27 @@ public class OrderTask {
List<OrderItem> orderItems = order.getOrderItems(); List<OrderItem> orderItems = order.getOrderItems();
for (OrderItem orderItem : orderItems) { for (OrderItem orderItem : orderItems) {
productService.removeProductCacheByProdId(orderItem.getProdId()); productService.removeProductCacheByProdId(orderItem.getProdId());
skuService.removeSkuCacheBySkuId(orderItem.getSkuId(),orderItem.getProdId()); skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId());
} }
} }
} }
/** /**
* * @XxlJobXXL-JOB"confirmOrder"
*
* 1.
* 2. OrderStatus.CONSIGNMENT.value()15
* 3.
* 4. OrderServiceconfirmOrder
* 5. OrderItemProductServiceSkuService
*
*/ */
@XxlJob("confirmOrder") @XxlJob("confirmOrder")
public void confirmOrder(){ public void confirmOrder() {
Date now = new Date(); Date now = new Date();
logger.info("系统自动确认收货订单。。。"); logger.info("系统自动确认收货订单。。。");
// 获取15天之前未支付的订单 // 获取15天之前已发货的订单通过OrderService的listOrderAndOrderItems方法传入已发货状态值和15天之前的时间作为筛选条件进行查询。
List<Order> orders = orderService.listOrderAndOrderItems(OrderStatus.CONSIGNMENT.value(),DateUtil.offsetDay(now, -15)); List<Order> orders = orderService.listOrderAndOrderItems(OrderStatus.CONSIGNMENT.value(), DateUtil.offsetDay(now, -15));
if (CollectionUtil.isEmpty(orders)) { if (CollectionUtil.isEmpty(orders)) {
return; return;
} }
@ -82,9 +105,9 @@ public class OrderTask {
List<OrderItem> orderItems = order.getOrderItems(); List<OrderItem> orderItems = order.getOrderItems();
for (OrderItem orderItem : orderItems) { for (OrderItem orderItem : orderItems) {
productService.removeProductCacheByProdId(orderItem.getProdId()); productService.removeProductCacheByProdId(orderItem.getProdId());
skuService.removeSkuCacheBySkuId(orderItem.getSkuId(),orderItem.getProdId()); skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId());
} }
} }
} }
} }

@ -8,28 +8,43 @@
* *
*/ */
// 声明该类所在的包名在Java中包用于对类进行组织和分类管理方便代码的模块化以及避免类名冲突等问题。
// 此处表明该类属于com.yami.shop这个包结构下后续相关的类可以按照包的层次结构进行合理组织和查找。
package com.yami.shop.api; package com.yami.shop.api;
// 引入Spring Boot相关的核心注解用于标记这是一个Spring Boot应用程序它会开启Spring Boot的自动配置等一系列功能简化Spring应用的搭建和部署流程。
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
// 用于以编程方式构建Spring Boot应用程序特别是在需要对应用的启动配置等进行更多定制化操作时会用到比如配置应用的启动类、配置文件等信息。
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder; // Spring Boot提供的一个用于支持将Spring Boot应用部署为Web应用例如部署到Servlet容器中的基础类通常需要继承它来做相关的适配和初始化工作。
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
// 用于指定Spring需要扫描的组件所在的基础包路径Spring会在这些包及其子包下查找带有相关注解如 @Component、@Service等的类并将它们注册为Spring容器中的组件便于进行依赖注入等操作。
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
/** /**
* @author lgh * @author lgh
* APISpring BootServlet
*/ */
// @SpringBootApplication是一个复合注解它整合了多个Spring相关的注解例如 @Configuration表示这是一个配置类、@EnableAutoConfiguration开启Spring Boot的自动配置功能以及 @ComponentScan默认扫描当前类所在的包及其子包下的组件等。
// 在这里使用它来标记这个类是Spring Boot应用的主配置类Spring Boot会基于这个类进行一系列的启动和配置操作。
@SpringBootApplication @SpringBootApplication
// 通过 @ComponentScan 注解指定Spring要扫描的基础包路径为 "com.yami.shop"意味着Spring会在这个包及其子包下查找并注册各种Spring组件如控制器、服务类、数据访问层等到Spring容器中以便进行后续的依赖注入和业务逻辑处理。
// 这样可以确保我们自定义的各个业务组件能够被Spring正确识别并管理起来方便在不同的组件之间进行依赖注入实现各种业务功能。
@ComponentScan(basePackages = {"com.yami.shop"}) @ComponentScan(basePackages = {"com.yami.shop"})
public class ApiApplication extends SpringBootServletInitializer{ public class ApiApplication extends SpringBootServletInitializer {
public static void main(String[] args) { // 应用的主入口方法Java应用程序从这个方法开始执行。当运行这个类的 main 方法时会启动Spring Boot应用加载配置、初始化Spring容器并启动相关的Web服务等如果是Web应用的话
// 它是整个应用启动的起始点类似于传统Java应用中程序开始执行的地方只不过在这里借助Spring Boot的功能来完成更复杂的应用启动流程。
public static void main(String[] args) {
// 使用SpringApplication类的静态方法 run 来启动Spring Boot应用传入当前启动类ApiApplication.class以及命令行参数 argsSpring Boot会根据配置自动完成一系列的初始化和启动操作。
// 这个方法内部会执行诸如加载配置文件application.properties或application.yml等、创建Spring容器ApplicationContext、扫描并注册组件、启动嵌入式Web服务器如果是Web应用等一系列操作使得整个应用能够正常运行起来。
SpringApplication.run(ApiApplication.class, args); SpringApplication.run(ApiApplication.class, args);
} }
@Override // 重写SpringBootServletInitializer类中的configure方法用于配置Spring Boot应用在Servlet容器中的启动方式这里返回一个SpringApplicationBuilder对象并指定应用的启动类为ApiApplication.class以便在Servlet容器中正确地启动Spring Boot应用。
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { // 当将Spring Boot应用部署到外部的Servlet容器如Tomcat、Jetty等容器会调用这个方法来构建和配置Spring Boot应用确保应用能够在该容器环境下正常启动和运行实现与Servlet容器的良好集成。
return builder.sources(ApiApplication.class); @Override
} protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
} return builder.sources(ApiApplication.class);
}
}

@ -16,17 +16,43 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
/** /**
* ApiBeanConfigSpringSpringBean
* Spring
* 使 @Configuration SpringSpring
* Bean
* @AllArgsConstructor Lombok便
* SnowflakeBeanSnowflakeID
* ID
* @author lanhai * @author lanhai
*/ */
@Configuration @Configuration
@AllArgsConstructor @AllArgsConstructor
public class ApiBeanConfig { public class ApiBeanConfig {
// 通过Lombok生成的构造函数注入ApiConfig实例ApiConfig实例应该是用于承载和提供配置相关信息的类
// 在这里主要是为了获取其中的workerId和datacenterId两个参数这两个参数对于初始化Snowflake实例至关重要
// 它们在分布式环境下帮助确定Snowflake生成唯一ID的工作节点和数据中心标识从而保证生成的ID在整个分布式系统中是唯一的。
private final ApiConfig apiConfig; private final ApiConfig apiConfig;
/**
* snowflake @Bean Spring @Bean SpringBean
* SnowflakeSpringBean便Spring
* ID便使
* SnowflakeworkerIddatacenterIdapiConfig
* SnowflakeID
* @return SnowflakeSpring
* ID使
*/
@Bean @Bean
public Snowflake snowflake() { public Snowflake snowflake() {
// 使用从ApiConfig中获取的workerId和datacenterId来实例化Snowflake对象确保其在分布式环境下能正确生成唯一ID。
return new Snowflake(apiConfig.getWorkerId(), apiConfig.getDatacenterId()); return new Snowflake(apiConfig.getWorkerId(), apiConfig.getDatacenterId());
} }
} }

@ -8,6 +8,7 @@
* *
*/ */
// 该类所属的包名表明其位于商城API相关的配置包下主要负责处理商城API相关的配置信息加载与管理。
package com.yami.shop.api.config; package com.yami.shop.api.config;
import lombok.Data; import lombok.Data;
@ -15,30 +16,52 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* * ApiConfig
* Spring
* 使Spring便
*
* @author lgh * @author lgh
*/ */
@Data
@Component @Component
// @Component注解是Spring框架中用于标记一个类为Spring组件的基础注解。当Spring容器进行组件扫描时通常基于@ComponentScan配置的扫描路径
// 一旦发现被@Component注解标记的类就会将其实例化并纳入Spring容器进行管理。这使得其他需要使用该类的地方可以通过依赖注入如@Autowired注解轻松获取其实例
// 从而实现了类之间的解耦以及配置信息的统一管理和共享。
@Component
// @PropertySource注解用于指定配置属性的来源文件这里的参数"classpath:api.properties"表示会从类路径下查找名为api.properties的文件作为配置信息的数据源。
// 在实际的项目构建和部署过程中这个文件会随着项目一起打包并且在应用启动时Spring会读取其中的配置内容按照后续的配置绑定规则进行属性赋值操作。
@PropertySource("classpath:api.properties") @PropertySource("classpath:api.properties")
// @ConfigurationProperties注解在Spring Boot应用中扮演着重要角色它用于将配置文件中的属性值绑定到对应的Java类的成员变量上。
// 这里的参数"prefix = "api""指定了配置文件中属性的前缀意味着Spring会查找以"api"开头的属性,并将其对应的值自动注入到这个类中名称相同(忽略大小写)的成员变量中。
// 例如,如果配置文件中有"api.datacenterId = 1"这样的配置项那么就会自动将值1赋给这个类中的datacenterId成员变量实现了配置文件与Java代码之间的无缝对接方便配置管理。
@ConfigurationProperties(prefix = "api") @ConfigurationProperties(prefix = "api")
// 使用lombok的@Data注解这是一个便捷的代码生成注解它会为类自动生成一系列常用的方法包括成员变量的Getter和Setter方法、toString方法、equals方法以及hashCode方法等。
// 通过提供这些自动生成的方法,使得在其他类中对该类的成员变量进行访问、赋值以及在对象比较、哈希计算等操作时更加方便,减少了手动编写这些重复代码的工作量,提高了代码的简洁性和可读性。
@Data
public class ApiConfig { public class ApiConfig {
/** /**
* ID * ID
*/ *
private Integer datacenterId; * IDIDSnowflakeIDID
*
*/
private Integer datacenterId;
/** /**
* ID * ID
*/ *
private Integer workerId; * ID使
* IDSnowflakeIDID便
*/
private Integer workerId;
/** /**
* *
*/ * 使访
private String domainName; *
* 便使访
*/
private String domainName;
} }

@ -18,26 +18,42 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
/** /**
* Swagger使 * Swagger
* Swagger
*
* @author LGH * @author LGH
*/ */
@Configuration @Configuration
public class SwaggerConfiguration { public class SwaggerConfiguration {
@Bean /**
public GroupedOpenApi createRestApi() { * OpenAPI
return GroupedOpenApi.builder() * @return GroupedOpenApi
.group("接口文档") */
.packagesToScan("com.yami.shop.api").build(); @Bean
} public GroupedOpenApi createRestApi() {
return GroupedOpenApi.builder()
// 设置接口文档的分组名称方便在Swagger界面中对接口进行分类查看等操作
.group("接口文档")
// 指定要扫描的包路径Swagger会根据这个路径去查找接口相关的类进而生成对应的接口文档信息
.packagesToScan("com.yami.shop.api").build();
}
/**
@Bean * OpenAPI
public OpenAPI springShopOpenApi() { * @return OpenAPI
return new OpenAPI() */
.info(new Info().title("Mall4j接口文档") @Bean
.description("Mall4j接口文档openapi3.0 接口,用于前端对接") public OpenAPI springShopOpenApi() {
.version("v0.0.1") return new OpenAPI()
.license(new License().name("使用请遵守AGPL3.0授权协议").url("https://www.mall4j.com"))); .info(new Info()
} // 设置接口文档的标题会显示在Swagger界面的顶部等显著位置
} .title("Mall4j接口文档")
// 设置接口文档的描述信息,用于简要说明文档的用途、涵盖范围等内容
.description("Mall4j接口文档openapi3.0 接口,用于前端对接")
// 设置接口文档的版本号,方便对不同版本的接口文档进行区分和管理
.version("v0.0.1")
// 设置接口文档的授权协议相关信息,包括协议名称和协议对应的网址
.license(new License().name("使用请遵守AGPL3.0授权协议").url("https://www.mall4j.com")));
}
}

@ -31,6 +31,8 @@ import java.util.Date;
import java.util.List; import java.util.List;
/** /**
*
*
* @author lanhai * @author lanhai
*/ */
@RestController @RestController
@ -39,120 +41,195 @@ import java.util.List;
@AllArgsConstructor @AllArgsConstructor
public class AddrController { public class AddrController {
// 自动注入用户地址服务层接口,通过该接口调用具体的业务逻辑方法来处理用户地址相关业务
@Autowired @Autowired
private UserAddrService userAddrService; private UserAddrService userAddrService;
/** /**
* *
* DTO
*
* @return ServerResponseEntity UserAddrDto 便使
*/ */
@GetMapping("/list") @GetMapping("/list")
@Operation(summary = "用户地址列表" , description = "获取用户的所有地址信息") @Operation(summary = "用户地址列表", description = "获取用户的所有地址信息")
public ServerResponseEntity<List<UserAddrDto>> dvyList() { public ServerResponseEntity<List<UserAddrDto>> dvyList() {
// 获取当前用户的 ID用于筛选属于该用户的地址记录
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
List<UserAddr> userAddrs = userAddrService.list(new LambdaQueryWrapper<UserAddr>().eq(UserAddr::getUserId, userId).orderByDesc(UserAddr::getCommonAddr).orderByDesc(UserAddr::getUpdateTime)); // 使用 MyBatis Plus 的 LambdaQueryWrapper 构建查询条件,查询当前用户的所有地址信息,并按照常用地址和更新时间降序排序
List<UserAddr> userAddrs = userAddrService.list(new LambdaQueryWrapper<UserAddr>()
.eq(UserAddr::getUserId, userId)
.orderByDesc(UserAddr::getCommonAddr)
.orderByDesc(UserAddr::getUpdateTime));
// 使用 Hutool 的 BeanUtil 将查询到的 UserAddr 类型的地址列表转换为 UserAddrDto 类型列表,方便返回给前端展示
return ServerResponseEntity.success(BeanUtil.copyToList(userAddrs, UserAddrDto.class)); return ServerResponseEntity.success(BeanUtil.copyToList(userAddrs, UserAddrDto.class));
} }
/**
* AddrParam
* ID 0
*
*
* @param addrParam @Valid
* @return ServerResponseEntity "添加地址成功" ID
*/
@PostMapping("/addAddr") @PostMapping("/addAddr")
@Operation(summary = "新增用户地址" , description = "新增用户地址") @Operation(summary = "新增用户地址", description = "新增用户地址")
public ServerResponseEntity<String> addAddr(@Valid @RequestBody AddrParam addrParam) { public ServerResponseEntity<String> addAddr(@Valid @RequestBody AddrParam addrParam) {
// 获取当前用户的 ID用于关联新地址到该用户
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
if (addrParam.getAddrId() != null && addrParam.getAddrId() != 0) { // 如果传入的地址 ID 不为空且不等于 0说明该地址可能已存在返回相应的错误提示信息
if (addrParam.getAddrId()!= null && addrParam.getAddrId()!= 0) {
return ServerResponseEntity.showFailMsg("该地址已存在"); return ServerResponseEntity.showFailMsg("该地址已存在");
} }
// 统计当前用户已有的地址数量,用于判断是否需要将新地址设置为默认地址
long addrCount = userAddrService.count(new LambdaQueryWrapper<UserAddr>().eq(UserAddr::getUserId, userId)); long addrCount = userAddrService.count(new LambdaQueryWrapper<UserAddr>().eq(UserAddr::getUserId, userId));
// 使用 Hutool 的 BeanUtil 将 AddrParam 类型的参数对象转换为 UserAddr 类型,方便保存到数据库
UserAddr userAddr = BeanUtil.copyProperties(addrParam, UserAddr.class); UserAddr userAddr = BeanUtil.copyProperties(addrParam, UserAddr.class);
// 如果当前用户没有地址记录则将新地址设置为默认地址常用地址commonAddr 设为 1
if (addrCount == 0) { if (addrCount == 0) {
userAddr.setCommonAddr(1); userAddr.setCommonAddr(1);
} else { } else {
// 否则设置为非默认地址commonAddr 设为 0
userAddr.setCommonAddr(0); userAddr.setCommonAddr(0);
} }
// 设置用户 ID明确该地址所属的用户
userAddr.setUserId(userId); userAddr.setUserId(userId);
// 设置地址状态为有效(这里假设 1 表示有效状态)
userAddr.setStatus(1); userAddr.setStatus(1);
// 设置地址的创建时间为当前时间
userAddr.setCreateTime(new Date()); userAddr.setCreateTime(new Date());
// 设置地址的更新时间为当前时间
userAddr.setUpdateTime(new Date()); userAddr.setUpdateTime(new Date());
// 调用用户地址服务层的保存方法,将新地址信息保存到数据库中
userAddrService.save(userAddr); userAddrService.save(userAddr);
// 如果新地址被设置为默认地址commonAddr 为 1则清除默认地址缓存确保缓存数据的一致性
if (userAddr.getCommonAddr() == 1) { if (userAddr.getCommonAddr() == 1) {
// 清除默认地址缓存
userAddrService.removeUserAddrByUserId(0L, userId); userAddrService.removeUserAddrByUserId(0L, userId);
} }
return ServerResponseEntity.success("添加地址成功"); return ServerResponseEntity.success("添加地址成功");
} }
/** /**
* * AddrParam
*
*
* @param addrParam @Valid
* @return ServerResponseEntity "修改地址成功"
*/ */
@PutMapping("/updateAddr") @PutMapping("/updateAddr")
@Operation(summary = "修改订单用户地址" , description = "修改用户地址") @Operation(summary = "修改订单用户地址", description = "修改用户地址")
public ServerResponseEntity<String> updateAddr(@Valid @RequestBody AddrParam addrParam) { public ServerResponseEntity<String> updateAddr(@Valid @RequestBody AddrParam addrParam) {
// 获取当前用户的 ID用于验证地址是否属于该用户以及后续更新操作
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
// 根据传入的地址 ID 和用户 ID从数据库中查询对应的用户地址记录若不存在则返回相应的错误提示信息
UserAddr dbUserAddr = userAddrService.getUserAddrByUserId(addrParam.getAddrId(), userId); UserAddr dbUserAddr = userAddrService.getUserAddrByUserId(addrParam.getAddrId(), userId);
if (dbUserAddr == null) { if (dbUserAddr == null) {
return ServerResponseEntity.showFailMsg("该地址已被删除"); return ServerResponseEntity.showFailMsg("该地址已被删除");
} }
// 使用 Hutool 的 BeanUtil 将 AddrParam 类型的参数对象转换为 UserAddr 类型,准备更新到数据库
UserAddr userAddr = BeanUtil.copyProperties(addrParam, UserAddr.class); UserAddr userAddr = BeanUtil.copyProperties(addrParam, UserAddr.class);
// 设置用户 ID确保更新的是正确用户的地址记录
userAddr.setUserId(userId); userAddr.setUserId(userId);
// 设置地址的更新时间为当前时间
userAddr.setUpdateTime(new Date()); userAddr.setUpdateTime(new Date());
// 调用用户地址服务层的更新方法,将更新后的地址信息保存到数据库中
userAddrService.updateById(userAddr); userAddrService.updateById(userAddr);
// 清除当前地址缓存
// 清除当前地址的缓存,保证缓存数据与数据库最新数据一致
userAddrService.removeUserAddrByUserId(addrParam.getAddrId(), userId); userAddrService.removeUserAddrByUserId(addrParam.getAddrId(), userId);
// 清除默认地址缓存 // 清除默认地址缓存,确保默认地址相关数据的准确性
userAddrService.removeUserAddrByUserId(0L, userId); userAddrService.removeUserAddrByUserId(0L, userId);
return ServerResponseEntity.success("修改地址成功"); return ServerResponseEntity.success("修改地址成功");
} }
/** /**
* * ID
*
*
* @param addrId ID
* @return ServerResponseEntity "删除地址成功"
*/ */
@DeleteMapping("/deleteAddr/{addrId}") @DeleteMapping("/deleteAddr/{addrId}")
@Operation(summary = "删除订单用户地址" , description = "根据地址id删除用户地址") @Operation(summary = "删除订单用户地址", description = "根据地址id删除用户地址")
@Parameter(name = "addrId", description = "地址ID" , required = true) @Parameter(name = "addrId", description = "地址ID", required = true)
public ServerResponseEntity<String> deleteDvy(@PathVariable("addrId") Long addrId) { public ServerResponseEntity<String> deleteDvy(@PathVariable("addrId") Long addrId) {
// 获取当前用户的 ID用于验证地址是否属于该用户以及后续删除操作
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
// 根据传入的地址 ID 和用户 ID从数据库中查询对应的用户地址记录若不存在则返回相应的错误提示信息
UserAddr userAddr = userAddrService.getUserAddrByUserId(addrId, userId); UserAddr userAddr = userAddrService.getUserAddrByUserId(addrId, userId);
if (userAddr == null) { if (userAddr == null) {
return ServerResponseEntity.showFailMsg("该地址已被删除"); return ServerResponseEntity.showFailMsg("该地址已被删除");
} }
// 如果该地址是默认地址commonAddr 为 1则不允许删除返回相应的错误提示信息
if (userAddr.getCommonAddr() == 1) { if (userAddr.getCommonAddr() == 1) {
return ServerResponseEntity.showFailMsg("默认地址无法删除"); return ServerResponseEntity.showFailMsg("默认地址无法删除");
} }
// 调用用户地址服务层的删除方法,从数据库中删除该地址记录
userAddrService.removeById(addrId); userAddrService.removeById(addrId);
// 清除对应地址的缓存,保证缓存数据与数据库数据的一致性
userAddrService.removeUserAddrByUserId(addrId, userId); userAddrService.removeUserAddrByUserId(addrId, userId);
return ServerResponseEntity.success("删除地址成功"); return ServerResponseEntity.success("删除地址成功");
} }
/** /**
* * ID
*
*
* @param addrId ID
* @return ServerResponseEntity "修改地址成功"
*/ */
@PutMapping("/defaultAddr/{addrId}") @PutMapping("/defaultAddr/{addrId}")
@Operation(summary = "设置默认地址" , description = "根据地址id设置默认地址") @Operation(summary = "设置默认地址", description = "根据地址id设置默认地址")
public ServerResponseEntity<String> defaultAddr(@PathVariable("addrId") Long addrId) { public ServerResponseEntity<String> defaultAddr(@PathVariable("addrId") Long addrId) {
// 获取当前用户的 ID用于关联默认地址到该用户
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
// 调用用户地址服务层的方法,将指定地址 ID 的地址设置为当前用户的默认地址
userAddrService.updateDefaultUserAddr(addrId, userId); userAddrService.updateDefaultUserAddr(addrId, userId);
// 清除默认地址的缓存,保证缓存数据与数据库最新数据一致
userAddrService.removeUserAddrByUserId(0L, userId); userAddrService.removeUserAddrByUserId(0L, userId);
// 清除当前设置为默认地址的地址缓存,确保相关数据准确性
userAddrService.removeUserAddrByUserId(addrId, userId); userAddrService.removeUserAddrByUserId(addrId, userId);
return ServerResponseEntity.success("修改地址成功"); return ServerResponseEntity.success("修改地址成功");
} }
/** /**
* * ID UserAddrDto
*
*
* @param addrId ID
* @return ServerResponseEntity UserAddrDto
*/ */
@GetMapping("/addrInfo/{addrId}") @GetMapping("/addrInfo/{addrId}")
@Operation(summary = "获取地址信息" , description = "根据地址id获取地址信息") @Operation(summary = "获取地址信息", description = "根据地址id获取地址信息")
@Parameter(name = "addrId", description = "地址ID" , required = true) @Parameter(name = "addrId", description = "地址ID", required = true)
public ServerResponseEntity<UserAddrDto> addrInfo(@PathVariable("addrId") Long addrId) { public ServerResponseEntity<UserAddrDto> addrInfo(@PathVariable("addrId") Long addrId) {
// 获取当前用户的 ID用于验证地址是否属于该用户以及查询操作
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
// 根据传入的地址 ID 和用户 ID从数据库中查询对应的用户地址记录若不存在则抛出相应的异常
UserAddr userAddr = userAddrService.getUserAddrByUserId(addrId, userId); UserAddr userAddr = userAddrService.getUserAddrByUserId(addrId, userId);
if (userAddr == null) { if (userAddr == null) {
throw new YamiShopBindException("该地址已被删除"); throw new YamiShopBindException("该地址已被删除");
} }
// 使用 Hutool 的 BeanUtil 将查询到的 UserAddr 类型的地址信息转换为 UserAddrDto 类型,方便返回给前端展示
return ServerResponseEntity.success(BeanUtil.copyProperties(userAddr, UserAddrDto.class)); return ServerResponseEntity.success(BeanUtil.copyProperties(userAddr, UserAddrDto.class));
} }
}
}

@ -24,23 +24,36 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List; import java.util.List;
/** /**
*
*
*
* @author lanhai * @author lanhai
*/ */
@RestController @RestController
// 使用 @Tag 注解为该控制器类添加标签说明,用于在 API 文档(如 Swagger 生成的文档)中对该类下的接口进行分类展示,这里表明是“首页轮播图接口”相关的一组接口。
@Tag(name = "首页轮播图接口") @Tag(name = "首页轮播图接口")
public class IndexImgController { public class IndexImgController {
// 注入IndexImgService用于与首页轮播图相关的业务逻辑处理例如从数据库中查询轮播图列表等操作。
@Autowired @Autowired
private IndexImgService indexImgService; private IndexImgService indexImgService;
/** /**
* *
* @GetMapping HTTP GET /indexImgs访
* @Operation API summarydescription便使
*
* @return ServerResponseEntityIndexImgDto
*/ */
@GetMapping("/indexImgs") @GetMapping("/indexImgs")
@Operation(summary = "首页轮播图" , description = "获取首页轮播图列表信息") @Operation(summary = "首页轮播图", description = "获取首页轮播图列表信息")
public ServerResponseEntity<List<IndexImgDto>> indexImgs() { public ServerResponseEntity<List<IndexImgDto>> indexImgs() {
// 调用IndexImgService的listIndexImg方法从数据库或其他数据源获取首页轮播图的原始数据列表以IndexImg对象列表形式返回
List<IndexImg> indexImgList = indexImgService.listIndexImg(); List<IndexImg> indexImgList = indexImgService.listIndexImg();
// 使用hutool的BeanUtil工具类将IndexImg对象列表转换为IndexImgDto对象列表IndexImgDto可能是用于对外展示的、经过筛选或格式调整后的视图对象
// 这样可以避免直接将内部的业务实体对象暴露给客户端,更好地控制数据的展示格式和安全性。
List<IndexImgDto> indexImgDtos = BeanUtil.copyToList(indexImgList, IndexImgDto.class); List<IndexImgDto> indexImgDtos = BeanUtil.copyToList(indexImgList, IndexImgDto.class);
// 将转换后的IndexImgDto列表封装在表示成功的ServerResponseEntity对象中返回给客户端遵循统一的接口响应格式规范。
return ServerResponseEntity.success(indexImgDtos); return ServerResponseEntity.success(indexImgDtos);
} }
} }

@ -8,6 +8,7 @@
* *
*/ */
// 该类所属的包名表明其位于商城API的控制器包下主要用于处理与“我的订单”相关的接口请求及业务逻辑。
package com.yami.shop.api.controller; package com.yami.shop.api.controller;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
@ -36,58 +37,85 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
/** /**
* MyOrderControllerSpring RESTful
*
*
* @author lanhai * @author lanhai
*/ */
@RestController @RestController
// 定义该控制器类的基础请求路径,所有该类中的接口请求路径都将以此为前缀,表明是与“我的订单”相关的操作接口。
@RequestMapping("/p/myOrder") @RequestMapping("/p/myOrder")
// 使用Swagger的@Tag注解对该控制器类进行标记用于在API文档中生成对应的分类标签方便接口文档的分类展示和阅读这里表示该类下的接口都属于“我的订单接口”这一分类。
@Tag(name = "我的订单接口") @Tag(name = "我的订单接口")
// 使用lombok的@AllArgsConstructor注解自动生成包含所有final字段的构造函数方便依赖注入这里会为所有注入的服务类生成对应的构造函数参数。
@AllArgsConstructor @AllArgsConstructor
public class MyOrderController { public class MyOrderController {
// 通过构造函数注入OrderService实例用于调用与订单相关的业务逻辑方法比如获取订单、取消订单、确认收货等操作。
private final OrderService orderService; private final OrderService orderService;
// 通过构造函数注入UserAddrOrderService实例用于处理用户地址与订单相关的业务逻辑可能涉及获取订单对应的用户地址等操作。
private final UserAddrOrderService userAddrOrderService; private final UserAddrOrderService userAddrOrderService;
// 通过构造函数注入ProductService实例用于操作产品相关的业务逻辑例如可能在某些操作后需要清除产品缓存等情况会用到该服务。
private final ProductService productService; private final ProductService productService;
// 通过构造函数注入SkuService实例用于处理商品库存单元SKU相关的业务逻辑比如根据SKU ID进行缓存清除等操作。
private final SkuService skuService; private final SkuService skuService;
// 通过构造函数注入MyOrderService实例用于获取与“我的订单”相关的分页数据等业务逻辑例如根据用户ID和订单状态查询订单列表等操作。
private final MyOrderService myOrderService; private final MyOrderService myOrderService;
// 通过构造函数注入ShopDetailService实例用于获取店铺详情信息比如在获取订单详情时需要展示店铺名称等信息时会调用该服务获取相应数据。
private final ShopDetailService shopDetailService; private final ShopDetailService shopDetailService;
// 通过构造函数注入OrderItemService实例用于获取订单商品项相关的业务逻辑例如根据订单号获取订单中包含的具体商品项信息等操作。
private final OrderItemService orderItemService; private final OrderItemService orderItemService;
/** /**
* *
* orderNumber
* ServerResponseEntity
*
* @param orderNumber
* @return ServerResponseEntityOrderShopDto
*/ */
@GetMapping("/orderDetail") @GetMapping("/orderDetail")
// 使用Swagger的@Operation注解对该接口方法进行描述用于在API文档中生成对应的接口说明信息这里简要说明了该接口的功能是获取订单详情信息。
@Operation(summary = "订单详情信息", description = "根据订单号获取订单详情信息") @Operation(summary = "订单详情信息", description = "根据订单号获取订单详情信息")
// 使用Swagger的@Parameter注解对接口方法的参数进行描述这里指定了orderNumber参数的名称、描述以及其为必填项等信息方便在API文档中展示参数详情。
@Parameter(name = "orderNumber", description = "订单号", required = true) @Parameter(name = "orderNumber", description = "订单号", required = true)
public ServerResponseEntity<OrderShopDto> orderDetail(@RequestParam(value = "orderNumber") String orderNumber) { public ServerResponseEntity<OrderShopDto> orderDetail(@RequestParam(value = "orderNumber") String orderNumber) {
// 通过SecurityUtils获取当前登录用户的ID用于后续验证用户是否有权限获取该订单信息等操作。
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
OrderShopDto orderShopDto = new OrderShopDto(); OrderShopDto orderShopDto = new OrderShopDto();
// 调用OrderService的getOrderByOrderNumber方法根据订单号获取对应的订单对象如果订单不存在则抛出异常。
Order order = orderService.getOrderByOrderNumber(orderNumber); Order order = orderService.getOrderByOrderNumber(orderNumber);
if (order == null) { if (order == null) {
throw new RuntimeException("该订单不存在"); throw new RuntimeException("该订单不存在");
} }
// 验证当前登录用户是否有权限获取该订单信息通过比较订单中的用户ID和当前登录用户的ID是否一致来判断如果不一致则抛出异常。
if (!Objects.equals(order.getUserId(), userId)) { if (!Objects.equals(order.getUserId(), userId)) {
throw new RuntimeException("你没有权限获取该订单信息"); throw new RuntimeException("你没有权限获取该订单信息");
} }
// 调用ShopDetailService的getShopDetailByShopId方法根据订单所属店铺的ID获取店铺详情信息用于填充订单详情中的店铺相关信息。
ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(order.getShopId()); ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(order.getShopId());
// 调用UserAddrOrderService的getById方法根据订单关联的地址订单ID获取对应的用户地址订单对象用于获取用户地址信息。
UserAddrOrder userAddrOrder = userAddrOrderService.getById(order.getAddrOrderId()); UserAddrOrder userAddrOrder = userAddrOrderService.getById(order.getAddrOrderId());
// 使用BeanUtil将UserAddrOrder对象的属性复制到UserAddrDto对象中进行数据类型转换方便后续统一处理和返回数据。
UserAddrDto userAddrDto = BeanUtil.copyProperties(userAddrOrder, UserAddrDto.class); UserAddrDto userAddrDto = BeanUtil.copyProperties(userAddrOrder, UserAddrDto.class);
// 调用OrderItemService的getOrderItemsByOrderNumber方法根据订单号获取该订单下包含的所有商品项信息列表。
List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber); List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber);
// 使用BeanUtil将List<OrderItem>中的每个OrderItem对象的属性复制到OrderItemDto对象中组成新的列表用于后续统一处理和返回数据。
List<OrderItemDto> orderItemList = BeanUtil.copyToList(orderItems, OrderItemDto.class); List<OrderItemDto> orderItemList = BeanUtil.copyToList(orderItems, OrderItemDto.class);
// 将获取到的店铺相关信息设置到OrderShopDto对象中如店铺ID、店铺名称等。
orderShopDto.setShopId(shopDetail.getShopId()); orderShopDto.setShopId(shopDetail.getShopId());
orderShopDto.setShopName(shopDetail.getShopName()); orderShopDto.setShopName(shopDetail.getShopName());
// 设置订单的实际总价、用户地址信息、订单商品项信息、运费、优惠金额、创建时间、备注以及订单状态等信息到OrderShopDto对象中。
orderShopDto.setActualTotal(order.getActualTotal()); orderShopDto.setActualTotal(order.getActualTotal());
orderShopDto.setUserAddrDto(userAddrDto); orderShopDto.setUserAddrDto(userAddrDto);
orderShopDto.setOrderItemDtos(orderItemList); orderShopDto.setOrderItemDtos(orderItemList);
@ -97,21 +125,28 @@ public class MyOrderController {
orderShopDto.setRemarks(order.getRemarks()); orderShopDto.setRemarks(order.getRemarks());
orderShopDto.setStatus(order.getStatus()); orderShopDto.setStatus(order.getStatus());
// 计算订单中商品的总金额和商品总数量,通过遍历订单商品项列表,累加每个商品项的商品总金额和商品数量来实现。
double total = 0.0; double total = 0.0;
Integer totalNum = 0; Integer totalNum = 0;
for (OrderItemDto orderItem : orderShopDto.getOrderItemDtos()) { for (OrderItemDto orderItem : orderShopDto.getOrderItemDtos()) {
total = Arith.add(total, orderItem.getProductTotalAmount()); total = Arith.add(total, orderItem.getProductTotalAmount());
totalNum += orderItem.getProdCount(); totalNum += orderItem.getProdCount();
} }
// 将计算得到的商品总金额和商品总数量设置到OrderShopDto对象中。
orderShopDto.setTotal(total); orderShopDto.setTotal(total);
orderShopDto.setTotalNum(totalNum); orderShopDto.setTotalNum(totalNum);
return ServerResponseEntity.success(orderShopDto); return ServerResponseEntity.success(orderShopDto);
} }
/** /**
* *
* statuspage
* 0ServerResponseEntity
*
* @param status 12
* @param page
* @return ServerResponseEntityIPage<MyOrderDto>
*/ */
@GetMapping("/myOrder") @GetMapping("/myOrder")
@Operation(summary = "订单列表信息", description = "根据订单状态获取订单列表信息状态为0时获取所有订单") @Operation(summary = "订单列表信息", description = "根据订单状态获取订单列表信息状态为0时获取所有订单")
@ -119,19 +154,29 @@ public class MyOrderController {
@Parameter(name = "status", description = "订单状态 1:待付款 2:待发货 3:待收货 4:待评价 5:成功 6:失败") @Parameter(name = "status", description = "订单状态 1:待付款 2:待发货 3:待收货 4:待评价 5:成功 6:失败")
}) })
public ServerResponseEntity<IPage<MyOrderDto>> myOrder(@RequestParam(value = "status") Integer status, PageParam<MyOrderDto> page) { public ServerResponseEntity<IPage<MyOrderDto>> myOrder(@RequestParam(value = "status") Integer status, PageParam<MyOrderDto> page) {
// 通过SecurityUtils获取当前登录用户的ID用于查询该用户对应的订单列表信息。
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
// 调用MyOrderService的pageMyOrderByUserIdAndStatus方法根据用户ID和订单状态进行分页查询订单列表信息获取到IPage<MyOrderDto>类型的分页数据对象。
IPage<MyOrderDto> myOrderDtoIpage = myOrderService.pageMyOrderByUserIdAndStatus(page, userId, status); IPage<MyOrderDto> myOrderDtoIpage = myOrderService.pageMyOrderByUserIdAndStatus(page, userId, status);
return ServerResponseEntity.success(myOrderDtoIpage); return ServerResponseEntity.success(myOrderDtoIpage);
} }
/** /**
* *
* orderNumber
* OrderServicecancelOrders
* SKUServerResponseEntity
*
* @param orderNumber
* @return ServerResponseEntityString
*/ */
@PutMapping("/cancel/{orderNumber}") @PutMapping("/cancel/{orderNumber}")
@Operation(summary = "根据订单号取消订单", description = "根据订单号取消订单") @Operation(summary = "根据订单号取消订单", description = "根据订单号取消订单")
@Parameter(name = "orderNumber", description = "订单号", required = true) @Parameter(name = "orderNumber", description = "订单号", required = true)
public ServerResponseEntity<String> cancel(@PathVariable("orderNumber") String orderNumber) { public ServerResponseEntity<String> cancel(@PathVariable("orderNumber") String orderNumber) {
// 通过SecurityUtils获取当前登录用户的ID用于验证用户是否有权限操作该订单。
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
// 调用OrderService的getOrderByOrderNumber方法根据订单号获取对应的订单对象。
Order order = orderService.getOrderByOrderNumber(orderNumber); Order order = orderService.getOrderByOrderNumber(orderNumber);
if (!Objects.equals(order.getUserId(), userId)) { if (!Objects.equals(order.getUserId(), userId)) {
throw new YamiShopBindException("你没有权限获取该订单信息"); throw new YamiShopBindException("你没有权限获取该订单信息");
@ -139,12 +184,14 @@ public class MyOrderController {
if (!Objects.equals(order.getStatus(), OrderStatus.UNPAY.value())) { if (!Objects.equals(order.getStatus(), OrderStatus.UNPAY.value())) {
throw new YamiShopBindException("订单已支付,无法取消订单"); throw new YamiShopBindException("订单已支付,无法取消订单");
} }
// 调用OrderItemService的getOrderItemsByOrderNumber方法根据订单号获取该订单下包含的所有商品项信息列表。
List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber); List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber);
order.setOrderItems(orderItems); order.setOrderItems(orderItems);
// 取消订单 // 调用OrderService的cancelOrders方法传入包含该订单的列表这里使用Collections.singletonList将单个订单包装成列表取消订单操作。
orderService.cancelOrders(Collections.singletonList(order)); orderService.cancelOrders(Collections.singletonList(order));
// 清除缓存 // 循环遍历订单商品项信息列表针对每个商品项调用ProductService的removeProductCacheByProdId方法清除对应产品的缓存
// 以及调用SkuService的removeSkuCacheBySkuId方法清除对应SKU的缓存以保证数据的一致性和缓存的有效性。
for (OrderItem orderItem : orderItems) { for (OrderItem orderItem : orderItems) {
productService.removeProductCacheByProdId(orderItem.getProdId()); productService.removeProductCacheByProdId(orderItem.getProdId());
skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId()); skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId());
@ -152,14 +199,21 @@ public class MyOrderController {
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
/** /**
* *
* orderNumber
* OrderServiceconfirmOrder
* SKUServerResponseEntity
*
* @param orderNumber
* @return ServerResponseEntityString
*/ */
@PutMapping("/receipt/{orderNumber}") @PutMapping("/receipt/{orderNumber}")
@Operation(summary = "根据订单号确认收货", description = "根据订单号确认收货") @Operation(summary = "根据订单号确认收货", description = "根据订单号确认收货")
public ServerResponseEntity<String> receipt(@PathVariable("orderNumber") String orderNumber) { public ServerResponseEntity<String> receipt(@PathVariable("orderNumber") String orderNumber) {
// 通过SecurityUtils获取当前登录用户的ID用于验证用户是否有权限操作该订单。
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
// 调用OrderService的getOrderByOrderNumber方法根据订单号获取对应的订单对象。
Order order = orderService.getOrderByOrderNumber(orderNumber); Order order = orderService.getOrderByOrderNumber(orderNumber);
if (!Objects.equals(order.getUserId(), userId)) { if (!Objects.equals(order.getUserId(), userId)) {
throw new YamiShopBindException("你没有权限获取该订单信息"); throw new YamiShopBindException("你没有权限获取该订单信息");
@ -167,9 +221,10 @@ public class MyOrderController {
if (!Objects.equals(order.getStatus(), OrderStatus.CONSIGNMENT.value())) { if (!Objects.equals(order.getStatus(), OrderStatus.CONSIGNMENT.value())) {
throw new YamiShopBindException("订单未发货,无法确认收货"); throw new YamiShopBindException("订单未发货,无法确认收货");
} }
// 调用OrderItemService的getOrderItemsByOrderNumber方法根据订单号获取该订单下包含的所有商品项信息列表。
List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber); List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber);
order.setOrderItems(orderItems); order.setOrderItems(orderItems);
// 确认收货 // 调用OrderService的confirmOrder方法传入包含该订单的列表这里使用Collections.singletonList将单个订单包装成列表进行确认收货操作。
orderService.confirmOrder(Collections.singletonList(order)); orderService.confirmOrder(Collections.singletonList(order));
for (OrderItem orderItem : orderItems) { for (OrderItem orderItem : orderItems) {
@ -180,41 +235,10 @@ public class MyOrderController {
} }
/** /**
* *
*/ * orderNumber
@DeleteMapping("/{orderNumber}") * OrderServicedeleteOrdersServerResponseEntity
@Operation(summary = "根据订单号删除订单", description = "根据订单号删除订单") *
@Parameter(name = "orderNumber", description = "订单号", required = true) *
public ServerResponseEntity<String> delete(@PathVariable("orderNumber") String orderNumber) { * @param orderNumber
String userId = SecurityUtils.getUser().getUserId(); * @return ServerResponseEntity
Order order = orderService.getOrderByOrderNumber(orderNumber);
if (order == null) {
throw new YamiShopBindException("该订单不存在");
}
if (!Objects.equals(order.getUserId(), userId)) {
throw new YamiShopBindException("你没有权限获取该订单信息");
}
if (!Objects.equals(order.getStatus(), OrderStatus.SUCCESS.value()) && !Objects.equals(order.getStatus(), OrderStatus.CLOSE.value())) {
throw new YamiShopBindException("订单未完成或未关闭,无法删除订单");
}
// 删除订单
orderService.deleteOrders(Collections.singletonList(order));
return ServerResponseEntity.success("删除成功");
}
/**
*
*/
@GetMapping("/orderCount")
@Operation(summary = "获取我的订单订单数量", description = "获取我的订单订单数量")
public ServerResponseEntity<OrderCountData> getOrderCount() {
String userId = SecurityUtils.getUser().getUserId();
OrderCountData orderCountMap = orderService.getOrderCount(userId);
return ServerResponseEntity.success(orderCountMap);
}
}

@ -29,49 +29,70 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List; import java.util.List;
/** /**
*
*
* `Swagger`便使
*
* @author lanhai * @author lanhai
*/ */
@RestController @RestController
@RequestMapping("/shop/notice") @RequestMapping("/shop/notice")
@Tag(name = "公告管理接口") @Tag(name = "公告管理接口")
// 使用 @AllArgsConstructor 注解,由 lombok 自动生成包含所有成员变量的构造函数,用于依赖注入
@AllArgsConstructor @AllArgsConstructor
public class NoticeController { public class NoticeController {
// 通过构造函数注入NoticeService用于调用业务层方法来获取公告相关的数据
private NoticeService noticeService; private NoticeService noticeService;
/** /**
* *
* DTO
*
* @return ServerResponseEntity<List<NoticeDto>>DTO
*/ */
@GetMapping("/topNoticeList") @GetMapping("/topNoticeList")
@Operation(summary = "置顶公告列表信息" , description = "获取所有置顶公告列表信息") @Operation(summary = "置顶公告列表信息", description = "获取所有置顶公告列表信息")
// 使用 @Operation 注解在Swagger文档中对该接口进行描述包括接口的简要总结和详细描述信息
public ServerResponseEntity<List<NoticeDto>> getTopNoticeList() { public ServerResponseEntity<List<NoticeDto>> getTopNoticeList() {
// 调用服务层方法获取公告列表数据(这里获取的是所有的公告列表,后续可根据业务逻辑判断哪些是置顶的)
List<Notice> noticeList = noticeService.listNotice(); List<Notice> noticeList = noticeService.listNotice();
// 使用 hutool 工具类的方法将Notice类型的列表转换为NoticeDto类型的列表用于传输给前端展示的数据格式转换
List<NoticeDto> noticeDtoList = BeanUtil.copyToList(noticeList, NoticeDto.class); List<NoticeDto> noticeDtoList = BeanUtil.copyToList(noticeList, NoticeDto.class);
return ServerResponseEntity.success(noticeDtoList); return ServerResponseEntity.success(noticeDtoList);
} }
/** /**
* *
* IDDTO
*
* @param id ID @PathVariable
* @return ServerResponseEntity<NoticeDto>IDDTO
*/ */
@GetMapping("/info/{id}") @GetMapping("/info/{id}")
@Operation(summary = "公告详情" , description = "获取公告id公告详情") @Operation(summary = "公告详情", description = "获取公告id公告详情")
public ServerResponseEntity<NoticeDto> getNoticeById(@PathVariable("id") Long id) { public ServerResponseEntity<NoticeDto> getNoticeById(@PathVariable("id") Long id) {
// 调用服务层方法根据ID获取公告详情数据
Notice notice = noticeService.getNoticeById(id); Notice notice = noticeService.getNoticeById(id);
// 使用 hutool 工具类的方法将Notice对象转换为NoticeDto对象用于传输给前端展示的数据格式转换
NoticeDto noticeDto = BeanUtil.copyProperties(notice, NoticeDto.class); NoticeDto noticeDto = BeanUtil.copyProperties(notice, NoticeDto.class);
return ServerResponseEntity.success(noticeDto); return ServerResponseEntity.success(noticeDto);
} }
/** /**
* *
*
*
* @param page
* @return ServerResponseEntity<IPage<NoticeDto>>DTO
*/ */
@GetMapping("/noticeList") @GetMapping("/noticeList")
@Operation(summary = "公告列表信息" , description = "获取所有公告列表信息") @Operation(summary = "公告列表信息", description = "获取所有公告列表信息")
@Parameters({ @Parameters({
}) })
// 使用 @Parameters 注解在Swagger文档中可以对接口的参数进行详细描述这里目前没有添加具体参数描述内容
public ServerResponseEntity<IPage<NoticeDto>> pageNotice(PageParam<NoticeDto> page) { public ServerResponseEntity<IPage<NoticeDto>> pageNotice(PageParam<NoticeDto> page) {
return ServerResponseEntity.success(noticeService.pageNotice(page)); return ServerResponseEntity.success(noticeService.pageNotice(page));
} }
} }

@ -39,6 +39,9 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
/** /**
*
*
*
* @author lanhai * @author lanhai
*/ */
@RestController @RestController
@ -46,112 +49,148 @@ import java.util.Objects;
@Tag(name = "订单接口") @Tag(name = "订单接口")
public class OrderController { public class OrderController {
// 自动注入订单服务层接口,用于处理订单相关的核心业务逻辑,如保存订单、查询订单等操作
@Autowired @Autowired
private OrderService orderService; private OrderService orderService;
// 自动注入库存单元SKU服务层接口可能用于处理商品库存相关操作比如库存扣减、缓存清除等
@Autowired @Autowired
private SkuService skuService; private SkuService skuService;
// 自动注入商品服务层接口,可用于商品相关的业务操作,例如获取商品信息、清除商品缓存等
@Autowired @Autowired
private ProductService productService; private ProductService productService;
// 自动注入用户地址服务层接口,用于获取用户地址相关信息,比如根据用户 ID 和地址 ID 查询具体地址详情
@Autowired @Autowired
private UserAddrService userAddrService; private UserAddrService userAddrService;
// 自动注入购物车服务层接口,用于处理购物车相关业务,如获取购物车商品项、根据购物车商品组装店铺相关信息等
@Autowired @Autowired
private BasketService basketService; private BasketService basketService;
// 自动注入 Spring 的应用上下文对象,用于发布事件,实现基于事件驱动的业务逻辑解耦,例如发布确认订单事件
@Autowired @Autowired
private ApplicationContext applicationContext; private ApplicationContext applicationContext;
/** /**
* *
* orderService
*
* @param orderParam @Valid ID ID
* @return ServerResponseEntity ShopCartOrderMergerDto 便
*/ */
@PostMapping("/confirm") @PostMapping("/confirm")
@Operation(summary = "结算,生成订单信息" , description = "传入下单所需要的参数进行下单") @Operation(summary = "结算,生成订单信息", description = "传入下单所需要的参数进行下单")
public ServerResponseEntity<ShopCartOrderMergerDto> confirm(@Valid @RequestBody OrderParam orderParam) { public ServerResponseEntity<ShopCartOrderMergerDto> confirm(@Valid @RequestBody OrderParam orderParam) {
// 获取当前用户的 ID用于后续关联订单、地址等信息到该用户
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
// 订单地址信息 // 根据传入的地址 ID 和用户 ID从数据库中获取用户的订单地址信息,并转换为 DTO 类型方便后续返回给前端展示
UserAddr userAddr = userAddrService.getUserAddrByUserId(orderParam.getAddrId(), userId); UserAddr userAddr = userAddrService.getUserAddrByUserId(orderParam.getAddrId(), userId);
UserAddrDto userAddrDto = BeanUtil.copyProperties(userAddr, UserAddrDto.class); UserAddrDto userAddrDto = BeanUtil.copyProperties(userAddr, UserAddrDto.class);
// 组装获取用户提交的购物车商品项,根据传入的购物车商品项 ID 和用户 ID 等信息,调用购物车服务层方法获取具体的商品项信息列表,
// 组装获取用户提交的购物车商品项 // 如果购物车中没有选择商品(即列表为空),则抛出异常提示用户选择商品加入购物车
List<ShopCartItemDto> shopCartItems = basketService.getShopCartItemsByOrderItems(orderParam.getBasketIds(),orderParam.getOrderItem(),userId); List<ShopCartItemDto> shopCartItems = basketService.getShopCartItemsByOrderItems(orderParam.getBasketIds(), orderParam.getOrderItem(), userId);
if (CollectionUtil.isEmpty(shopCartItems)) { if (CollectionUtil.isEmpty(shopCartItems)) {
throw new YamiShopBindException("请选择您需要的商品加入购物车"); throw new YamiShopBindException("请选择您需要的商品加入购物车");
} }
// 根据店铺组装购车中的商品信息,返回每个店铺中的购物车商品信息 // 根据店铺组装购车中的商品信息,将购物车中的商品项按照店铺进行分类整理,返回每个店铺中的购物车商品信息列表,方便后续计算每个店铺的订单详情
List<ShopCartDto> shopCarts = basketService.getShopCarts(shopCartItems); List<ShopCartDto> shopCarts = basketService.getShopCarts(shopCartItems);
// 将要返回给前端的完整的订单信息 // 创建一个将要返回给前端的完整的订单信息对象,用于逐步组装并填充订单相关的各种信息
ShopCartOrderMergerDto shopCartOrderMergerDto = new ShopCartOrderMergerDto(); ShopCartOrderMergerDto shopCartOrderMergerDto = new ShopCartOrderMergerDto();
// 将用户地址信息设置到订单信息对象中,作为订单的收货地址相关信息
shopCartOrderMergerDto.setUserAddr(userAddrDto); shopCartOrderMergerDto.setUserAddr(userAddrDto);
// 所有店铺的订单信息 // 创建一个用于存放所有店铺的订单信息的列表,后续将每个店铺的订单信息对象添加到该列表中
List<ShopCartOrderDto> shopCartOrders = new ArrayList<>(); List<ShopCartOrderDto> shopCartOrders = new ArrayList<>();
// 初始化订单相关的金额和数量变量,用于累加计算订单的总金额、实际支付金额、商品总数量以及优惠金额等信息
double actualTotal = 0.0; double actualTotal = 0.0;
double total = 0.0; double total = 0.0;
int totalCount = 0; int totalCount = 0;
double orderReduce = 0.0; double orderReduce = 0.0;
// 遍历每个店铺的购物车信息,进行每个店铺订单信息的组装和金额等相关计算
for (ShopCartDto shopCart : shopCarts) { for (ShopCartDto shopCart : shopCarts) {
// 每个店铺的订单信息 // 创建一个每个店铺的订单信息对象,用于填充该店铺相关的订单详细信息
ShopCartOrderDto shopCartOrder = new ShopCartOrderDto(); ShopCartOrderDto shopCartOrder = new ShopCartOrderDto();
// 设置店铺 ID明确该订单信息所属的店铺
shopCartOrder.setShopId(shopCart.getShopId()); shopCartOrder.setShopId(shopCart.getShopId());
// 设置店铺名称,方便前端展示等使用
shopCartOrder.setShopName(shopCart.getShopName()); shopCartOrder.setShopName(shopCart.getShopName());
// 获取该店铺中的商品项折扣信息列表,包含了商品的折扣相关情况以及对应的商品项列表等信息
List<ShopCartItemDiscountDto> shopCartItemDiscounts = shopCart.getShopCartItemDiscounts(); List<ShopCartItemDiscountDto> shopCartItemDiscounts = shopCart.getShopCartItemDiscounts();
// 店铺中所有商品项信息 // 创建一个用于存放该店铺中所有商品项信息的列表,后续将从商品项折扣信息中提取出所有商品项并添加到该列表中
List<ShopCartItemDto> shopAllShopCartItems = new ArrayList<>(); List<ShopCartItemDto> shopAllShopCartItems = new ArrayList<>();
// 遍历商品项折扣信息列表,将每个折扣信息中的商品项添加到店铺所有商品项列表中,实现商品项的整合
for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartItemDiscounts) { for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartItemDiscounts) {
List<ShopCartItemDto> discountShopCartItems = shopCartItemDiscount.getShopCartItems(); List<ShopCartItemDto> discountShopCartItems = shopCartItemDiscount.getShopCartItems();
shopAllShopCartItems.addAll(discountShopCartItems); shopAllShopCartItems.addAll(discountShopCartItems);
} }
// 将商品项折扣信息设置到店铺订单信息对象中,作为该店铺订单的商品项折扣相关内容
shopCartOrder.setShopCartItemDiscounts(shopCartItemDiscounts); shopCartOrder.setShopCartItemDiscounts(shopCartItemDiscounts);
applicationContext.publishEvent(new ConfirmOrderEvent(shopCartOrder,orderParam,shopAllShopCartItems)); // 发布确认订单事件,将店铺订单信息、下单参数以及店铺所有商品项信息作为事件参数传递出去,
// 可以通过事件监听器实现一些额外的业务逻辑,例如基于事件驱动的业务流程扩展、通知等功能,实现业务逻辑的解耦
applicationContext.publishEvent(new ConfirmOrderEvent(shopCartOrder, orderParam, shopAllShopCartItems));
actualTotal = Arith.add(actualTotal,shopCartOrder.getActualTotal()); // 累加计算订单的实际支付金额,将当前店铺订单的实际支付金额累加到总实际支付金额中
total = Arith.add(total,shopCartOrder.getTotal()); actualTotal = Arith.add(actualTotal, shopCartOrder.getActualTotal());
// 累加计算订单的总金额,将当前店铺订单的总金额累加到总金额中
total = Arith.add(total, shopCartOrder.getTotal());
// 累加计算商品的总数量,将当前店铺订单的商品数量累加到总数量中
totalCount = totalCount + shopCartOrder.getTotalCount(); totalCount = totalCount + shopCartOrder.getTotalCount();
orderReduce = Arith.add(orderReduce,shopCartOrder.getShopReduce()); // 累加计算订单的优惠金额,将当前店铺订单的优惠金额累加到总优惠金额中
shopCartOrders.add(shopCartOrder); orderReduce = Arith.add(orderReduce, shopCartOrder.getShopReduce());
// 将当前店铺的订单信息对象添加到所有店铺订单信息列表中,完成一个店铺订单信息的组装
shopCartOrders.add(shopCartOrder);
} }
// 将计算好的订单总金额、实际支付金额、商品总数量以及优惠金额等信息设置到完整的订单信息对象中
shopCartOrderMergerDto.setActualTotal(actualTotal); shopCartOrderMergerDto.setActualTotal(actualTotal);
shopCartOrderMergerDto.setTotal(total); shopCartOrderMergerDto.setTotal(total);
shopCartOrderMergerDto.setTotalCount(totalCount); shopCartOrderMergerDto.setTotalCount(totalCount);
shopCartOrderMergerDto.setShopCartOrders(shopCartOrders); shopCartOrderMergerDto.setShopCartOrders(shopCartOrders);
shopCartOrderMergerDto.setOrderReduce(orderReduce); shopCartOrderMergerDto.setOrderReduce(orderReduce);
// 将组装好的完整订单信息存入缓存中,并返回缓存后的订单信息对象,方便后续提交订单等操作使用缓存数据,提高性能并保证数据一致性
shopCartOrderMergerDto = orderService.putConfirmOrderCache(userId, shopCartOrderMergerDto); shopCartOrderMergerDto = orderService.putConfirmOrderCache(userId, shopCartOrderMergerDto);
return ServerResponseEntity.success(shopCartOrderMergerDto); return ServerResponseEntity.success(shopCartOrderMergerDto);
} }
/** /**
* / , * /
*
*
*
*
* @param submitOrderParam @Valid
* @return ServerResponseEntity OrderNumbersDto 便
*/ */
@PostMapping("/submit") @PostMapping("/submit")
@Operation(summary = "提交订单,返回支付流水号" , description = "根据传入的参数判断是否为购物车提交订单,同时对购物车进行删除,用户开始进行支付") @Operation(summary = "提交订单,返回支付流水号", description = "根据传入的参数判断是否为购物车提交订单,同时对购物车进行删除,用户开始进行支付")
public ServerResponseEntity<OrderNumbersDto> submitOrders(@Valid @RequestBody SubmitOrderParam submitOrderParam) { public ServerResponseEntity<OrderNumbersDto> submitOrders(@Valid @RequestBody SubmitOrderParam submitOrderParam) {
// 获取当前用户的 ID用于后续从缓存中获取订单信息、关联订单等操作到该用户
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
// 从缓存中获取之前生成并缓存的确认订单信息,如果缓存中不存在则抛出异常提示订单已过期,需要重新下单
ShopCartOrderMergerDto mergerOrder = orderService.getConfirmOrderCache(userId); ShopCartOrderMergerDto mergerOrder = orderService.getConfirmOrderCache(userId);
if (mergerOrder == null) { if (mergerOrder == null) {
throw new YamiShopBindException("订单已过期,请重新下单"); throw new YamiShopBindException("订单已过期,请重新下单");
} }
// 获取提交订单参数中的店铺参数列表,用于后续设置订单的备注信息等操作
List<OrderShopParam> orderShopParams = submitOrderParam.getOrderShopParam(); List<OrderShopParam> orderShopParams = submitOrderParam.getOrderShopParam();
// 获取缓存中订单信息里的所有店铺订单信息列表,用于遍历设置备注信息以及后续的缓存清除等相关操作
List<ShopCartOrderDto> shopCartOrders = mergerOrder.getShopCartOrders(); List<ShopCartOrderDto> shopCartOrders = mergerOrder.getShopCartOrders();
// 设置备注
// 设置订单备注信息,如果传入的店铺参数列表不为空,则遍历店铺订单信息和店铺参数,根据店铺 ID 匹配,将对应的备注信息设置到店铺订单对象中
if (CollectionUtil.isNotEmpty(orderShopParams)) { if (CollectionUtil.isNotEmpty(orderShopParams)) {
for (ShopCartOrderDto shopCartOrder : shopCartOrders) { for (ShopCartOrderDto shopCartOrder : shopCartOrders) {
for (OrderShopParam orderShopParam : orderShopParams) { for (OrderShopParam orderShopParam : orderShopParams) {
@ -162,36 +201,47 @@ public class OrderController {
} }
} }
List<Order> orders = orderService.submit(userId,mergerOrder); // 调用订单服务层的提交订单方法,传入用户 ID 和缓存中的订单信息对象,提交订单并获取生成的订单列表,该方法内部会进行复杂的业务逻辑处理,
// 例如根据店铺拆单、保存订单信息到数据库、处理库存扣减等相关操作
List<Order> orders = orderService.submit(userId, mergerOrder);
// 创建一个字符串构建器,用于拼接订单编号,方便后续返回给前端一个用逗号分隔的订单编号字符串
StringBuilder orderNumbers = new StringBuilder(); StringBuilder orderNumbers = new StringBuilder();
// 遍历生成的订单列表,将每个订单的编号添加到字符串构建器中,并在每个编号后添加逗号进行分隔
for (Order order : orders) { for (Order order : orders) {
orderNumbers.append(order.getOrderNumber()).append(","); orderNumbers.append(order.getOrderNumber()).append(",");
} }
// 删除最后一个多余的逗号,得到正确格式的订单编号字符串
orderNumbers.deleteCharAt(orderNumbers.length() - 1); orderNumbers.deleteCharAt(orderNumbers.length() - 1);
// 标记是否为购物车提交订单,初始化为 false后续根据购物车商品项的 ID 判断是否为购物车提交订单情况
boolean isShopCartOrder = false; boolean isShopCartOrder = false;
// 移除缓存
// 移除相关缓存,遍历每个店铺的订单信息、商品项折扣信息以及商品项信息,进行以下操作:
// 1. 判断商品项是否有购物车 ID如果有则说明是购物车提交订单并相应地设置标记变量。
// 2. 清除商品对应的库存单元SKU缓存通过调用 SKU 服务层的方法,传入商品的 SKU ID 和商品 ID。
// 3. 清除商品缓存,通过调用商品服务层的方法,传入商品 ID确保缓存数据与数据库最新状态一致避免数据不一致问题影响后续业务操作。
for (ShopCartOrderDto shopCartOrder : shopCartOrders) { for (ShopCartOrderDto shopCartOrder : shopCartOrders) {
for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartOrder.getShopCartItemDiscounts()) { for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartOrder.getShopCartItemDiscounts()) {
for (ShopCartItemDto shopCartItem : shopCartItemDiscount.getShopCartItems()) { for (ShopCartItemDto shopCartItem : shopCartItemDiscount.getShopCartItems()) {
Long basketId = shopCartItem.getBasketId(); Long basketId = shopCartItem.getBasketId();
if (basketId != null && basketId != 0) { if (basketId!= null && basketId!= 0) {
isShopCartOrder = true; isShopCartOrder = true;
} }
skuService.removeSkuCacheBySkuId(shopCartItem.getSkuId(),shopCartItem.getProdId()); skuService.removeSkuCacheBySkuId(shopCartItem.getSkuId(), shopCartItem.getProdId());
productService.removeProductCacheByProdId(shopCartItem.getProdId()); productService.removeProductCacheByProdId(shopCartItem.getProdId());
} }
} }
} }
// 购物车提交订单时(即有购物车ID时)
// 如果是购物车提交订单(即标记变量为 true则调用购物车服务层的方法根据用户 ID 清除该用户的购物车商品项缓存,保证购物车数据的准确性
if (isShopCartOrder) { if (isShopCartOrder) {
basketService.removeShopCartItemsCacheByUserId(userId); basketService.removeShopCartItemsCacheByUserId(userId);
} }
// 清除确认订单的缓存,通过调用订单服务层的方法,传入用户 ID确保缓存数据与实际业务状态一致避免缓存数据干扰下次下单操作
orderService.removeConfirmOrderCache(userId); orderService.removeConfirmOrderCache(userId);
return ServerResponseEntity.success(new OrderNumbersDto(orderNumbers.toString())); return ServerResponseEntity.success(new OrderNumbersDto(orderNumbers.toString()));
} }
}
}

@ -26,45 +26,69 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
/** /**
*
* PayService
*
* @author lanhai * @author lanhai
*/ */
@RestController @RestController
// 设置该控制器类对应的请求映射路径,后续类中的接口方法路径会基于此进行拼接,这里表明是与订单支付相关的接口所在的基础路径。
@RequestMapping("/p/order") @RequestMapping("/p/order")
// 使用 @Tag 注解为该控制器类添加标签说明,用于在 API 文档(如 Swagger 生成的文档)中对该类下的接口进行分类展示,这里表明是“订单接口”相关的一组接口。
@Tag(name = "订单接口") @Tag(name = "订单接口")
// 通过Lombok的 @AllArgsConstructor 注解生成包含所有参数的构造函数用于依赖注入PayService实例方便后续调用支付相关的业务方法。
@AllArgsConstructor @AllArgsConstructor
public class PayController { public class PayController {
// 注入PayService用于处理与支付相关的核心业务逻辑例如发起支付、处理支付成功后的业务逻辑等操作。
private final PayService payService; private final PayService payService;
/** /**
* * PayParam
* @PostMapping HTTP POST /payPOST
* @Operation API summarydescription便使
*
* @param payParam PayParam
* @return ServerResponseEntityVoid
*/ */
@PostMapping("/pay") @PostMapping("/pay")
@Operation(summary = "根据订单号进行支付" , description = "根据订单号进行支付") @Operation(summary = "根据订单号进行支付", description = "根据订单号进行支付")
public ServerResponseEntity<Void> pay(@RequestBody PayParam payParam) { public ServerResponseEntity<Void> pay(@RequestBody PayParam payParam) {
// 通过SecurityUtils工具类获取当前登录用户信息封装在YamiUser对象中后续可能用于验证用户身份、关联支付记录与用户等操作。
YamiUser user = SecurityUtils.getUser(); YamiUser user = SecurityUtils.getUser();
// 从获取到的用户对象中提取用户ID作为支付操作中关联用户的关键标识传递给后续的支付业务逻辑处理方法。
String userId = user.getUserId(); String userId = user.getUserId();
// 调用PayService的pay方法传入用户ID和支付参数发起支付操作该方法会根据传入的参数进行具体的支付流程处理
// 例如与支付平台交互、生成支付订单等并返回包含支付相关信息的PayInfoDto对象如支付单号等信息。
PayInfoDto payInfo = payService.pay(userId, payParam); PayInfoDto payInfo = payService.pay(userId, payParam);
// 调用PayService的paySuccess方法传入支付单号等信息用于处理支付成功后的相关业务逻辑比如更新订单状态、记录支付记录等操作
// 第二个参数为空字符串可能表示一些额外的备注信息等具体含义取决于paySuccess方法的实现逻辑。
payService.paySuccess(payInfo.getPayNo(), ""); payService.paySuccess(payInfo.getPayNo(), "");
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
/** /**
* * PayParam
* ServerResponseEntitytrue
* @PostMapping HTTP POST /normalPayPOST
* @Operation API
*
* @param payParam PayParam
* @return ServerResponseEntitytrue
*/ */
@PostMapping("/normalPay") @PostMapping("/normalPay")
@Operation(summary = "根据订单号进行支付" , description = "根据订单号进行支付") @Operation(summary = "根据订单号进行支付", description = "根据订单号进行支付")
public ServerResponseEntity<Boolean> normalPay(@RequestBody PayParam payParam) { public ServerResponseEntity<Boolean> normalPay(@RequestBody PayParam payParam) {
YamiUser user = SecurityUtils.getUser(); YamiUser user = SecurityUtils.getUser();
String userId = user.getUserId(); String userId = user.getUserId();
PayInfoDto pay = payService.pay(userId, payParam); PayInfoDto pay = payService.pay(userId, payParam);
// 根据内部订单号更新order settlement // 根据内部订单号更新order settlement调用PayService的paySuccess方法处理支付成功后的业务逻辑传入支付单号等信息
// 这里的操作与上面的支付接口中的paySuccess调用类似都是为了保证支付成功后相关业务数据的更新和一致性。
payService.paySuccess(pay.getPayNo(), ""); payService.paySuccess(pay.getPayNo(), "");
return ServerResponseEntity.success(true); return ServerResponseEntity.success(true);
} }
} }

@ -8,6 +8,7 @@
* *
*/ */
// 该类所属的包名表明其位于商城API的控制器包下从类名推测可能是用于处理支付通知相关的接口逻辑。
package com.yami.shop.api.controller; package com.yami.shop.api.controller;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -16,33 +17,44 @@ import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Hidden;
/** /**
* PayNoticeControllerSpring RESTful@HiddenAPI使Swagger
*
*
* @author lanhai * @author lanhai
*/ */
@Hidden @Hidden
// 使用@Hidden注解将该控制器标记为隐藏状态使其不在API文档中显示可能是因为该接口目前不需要对外暴露或者处于开发调试阶段等原因。
@RestController @RestController
// 表明该类是一个RESTful风格的控制器Spring会自动将其方法返回的对象转换为合适的响应格式如JSON等返回给客户端用于处理HTTP请求并返回响应。
@RequestMapping("/notice/pay") @RequestMapping("/notice/pay")
// 定义该控制器类的基础请求路径,所有该类中的接口请求路径都将以此为前缀,表明是与支付通知相关的操作接口。
@AllArgsConstructor @AllArgsConstructor
// 使用lombok的@AllArgsConstructor注解会自动生成包含所有final字段的构造函数方便依赖注入。虽然目前部分依赖如wxMiniPayService和payService被注释掉了但如果后续启用相关功能这个构造函数可以方便地注入对应的服务实例。
public class PayNoticeController { public class PayNoticeController {
//模拟支付不需要回调
// /** // 以下两个成员变量对应的服务原本可能用于处理支付相关的业务逻辑,目前被注释掉了,可能是因为模拟支付不需要回调等情况暂时不用这些服务。
// * 小程序支付 // 模拟支付不需要回调
// */ // /**
// private final WxPayService wxMiniPayService; // * 小程序支付
// // */
// private final PayService payService; // private final WxPayService wxMiniPayService;
// //
// // private final PayService payService;
// @RequestMapping("/order")
// public ServerResponseEntity<Void> submit(@RequestBody String xmlData) throws WxPayException { // 以下是被注释掉的方法从方法名和代码逻辑来看它原本可能是用于接收支付结果的回调通知解析通知中的XML数据微信支付等可能会以XML格式返回支付结果信息
// WxPayOrderNotifyResult parseOrderNotifyResult = wxMiniPayService.parseOrderNotifyResult(xmlData); // 获取相关支付单号等信息然后根据内部订单号调用payService的paySuccess方法更新支付相关的业务数据比如订单的支付状态等最后返回表示操作成功的响应信息。
// // 目前由于模拟支付不需要回调,所以该方法处于注释状态。
// String payNo = parseOrderNotifyResult.getOutTradeNo(); // @RequestMapping("/order")
// String bizPayNo = parseOrderNotifyResult.getTransactionId(); // public ServerResponseEntity<Void> submit(@RequestBody String xmlData) throws WxPayException {
// // WxPayOrderNotifyResult parseOrderNotifyResult = wxMiniPayService.parseOrderNotifyResult(xmlData);
// // 根据内部订单号更新order settlement //
// payService.paySuccess(payNo, bizPayNo); // String payNo = parseOrderNotifyResult.getOutTradeNo();
// // String bizPayNo = parseOrderNotifyResult.getTransactionId();
// //
// return ServerResponseEntity.success(); // // 根据内部订单号更新order settlement
// } // payService.paySuccess(payNo, bizPayNo);
} //
//
// return ServerResponseEntity.success();
// }
}

@ -30,39 +30,76 @@ import org.springframework.web.bind.annotation.*;
import java.util.Date; import java.util.Date;
/** /**
*
*
* `Swagger`便使
*
* @author lanhai * @author lanhai
*/ */
@RestController @RestController
@RequestMapping("/prodComm") @RequestMapping("/prodComm")
@Tag(name = "评论接口") @Tag(name = "评论接口")
// 使用 @AllArgsConstructor 注解,由 lombok 自动生成包含所有成员变量的构造函数,用于依赖注入
@AllArgsConstructor @AllArgsConstructor
public class ProdCommController { public class ProdCommController {
// 通过构造函数注入ProdCommService用于调用业务层方法来处理商品评论相关的业务逻辑
private final ProdCommService prodCommService; private final ProdCommService prodCommService;
/**
*
* ID
*
* @param prodId ID
* @return ServerResponseEntity<ProdCommDataDto>DTO
*/
@GetMapping("/prodCommData") @GetMapping("/prodCommData")
@Operation(summary = "返回商品评论数据(好评率 好评数量 中评数 差评数)" , description = "根据商品id获取") @Operation(summary = "返回商品评论数据(好评率 好评数量 中评数 差评数)", description = "根据商品id获取")
public ServerResponseEntity<ProdCommDataDto> getProdCommData(Long prodId) { public ServerResponseEntity<ProdCommDataDto> getProdCommData(Long prodId) {
return ServerResponseEntity.success(prodCommService.getProdCommDataByProdId(prodId)); return ServerResponseEntity.success(prodCommService.getProdCommDataByProdId(prodId));
} }
/**
*
*
*
* @param page
* @return ServerResponseEntity<IPage<ProdCommDto>>DTO
*/
@GetMapping("/prodCommPageByUser") @GetMapping("/prodCommPageByUser")
@Operation(summary = "根据用户返回评论分页数据" , description = "传入页码") @Operation(summary = "根据用户返回评论分页数据", description = "传入页码")
public ServerResponseEntity<IPage<ProdCommDto>> getProdCommPage(PageParam page) { public ServerResponseEntity<IPage<ProdCommDto>> getProdCommPage(PageParam page) {
return ServerResponseEntity.success(prodCommService.getProdCommDtoPageByUserId(page, SecurityUtils.getUser().getUserId())); return ServerResponseEntity.success(prodCommService.getProdCommDtoPageByUserId(page, SecurityUtils.getUser().getUserId()));
} }
@GetMapping("/prodCommPageByProd") /**
@Operation(summary = "根据商品返回评论分页数据" , description = "传入商品id和页码") *
* ID
*
*
* @param page
* @param prodId ID
* @param evaluate -1 null 0 1 2 3
* @return ServerResponseEntity<IPage<ProdCommDto>>DTO
*/
@GetMapping("/prodCommPageByProdId")
@Operation(summary = "根据商品返回评论分页数据", description = "传入商品id和页码")
@Parameters({ @Parameters({
@Parameter(name = "prodId", description = "商品id" , required = true), @Parameter(name = "prodId", description = "商品id", required = true),
@Parameter(name = "evaluate", description = "-1或null 全部0好评 1中评 2差评 3有图" , required = true), @Parameter(name = "evaluate", description = "-1或null 全部0好评 1中评 2差评 3有图", required = true),
}) })
public ServerResponseEntity<IPage<ProdCommDto>> getProdCommPageByProdId(PageParam page, Long prodId, Integer evaluate) { public ServerResponseEntity<IPage<ProdCommDto>> getProdCommPageByProdId(PageParam page, Long prodId, Integer evaluate) {
return ServerResponseEntity.success(prodCommService.getProdCommDtoPageByProdId(page, prodId, evaluate)); return ServerResponseEntity.success(prodCommService.getProdCommDtoPageByProdId(page, prodId, evaluate));
} }
/**
*
* IDID
*
*
* @param prodCommParam IDIDID
* @return ServerResponseEntity<Void>
*/
@PostMapping @PostMapping
@Operation(summary = "添加评论") @Operation(summary = "添加评论")
public ServerResponseEntity<Void> saveProdCommPage(ProdCommParam prodCommParam) { public ServerResponseEntity<Void> saveProdCommPage(ProdCommParam prodCommParam) {
@ -81,10 +118,17 @@ public class ProdCommController {
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
/**
*
* ID
*
* @param prodCommId ID
* @return ServerResponseEntity<Void>
*/
@DeleteMapping @DeleteMapping
@Operation(summary = "删除评论" , description = "根据id删除") @Operation(summary = "删除评论", description = "根据id删除")
public ServerResponseEntity<Void> deleteProdComm(Long prodCommId) { public ServerResponseEntity<Void> deleteProdComm(Long prodCommId) {
prodCommService.removeById(prodCommId); prodCommService.removeById(prodCommId);
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
} }

@ -37,6 +37,9 @@ import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
*
* 使
*
* @author lgh on 2018/11/26. * @author lgh on 2018/11/26.
*/ */
@RestController @RestController
@ -44,49 +47,72 @@ import java.util.stream.Collectors;
@Tag(name = "商品接口") @Tag(name = "商品接口")
public class ProdController { public class ProdController {
// 自动注入商品服务层接口,用于调用与商品相关的核心业务逻辑方法,如根据分类 ID 查询商品列表、获取商品详情等操作
@Autowired @Autowired
private ProductService prodService; private ProductService prodService;
// 自动注入库存单元SKU服务层接口用于获取商品对应的 SKU 相关信息,例如根据商品 ID 查询其所有的 SKU 列表等操作
@Autowired @Autowired
private SkuService skuService; private SkuService skuService;
// 自动注入运费模板服务层接口,用于获取商品相关的运费模板信息,比如根据运费模板 ID 获取详细的运费模板及关联信息
@Autowired @Autowired
private TransportService transportService; private TransportService transportService;
/**
* ID ID ID
* 便
*
* @param categoryId ID
* @param page
* @return ServerResponseEntity ProductDto 便使
*/
@GetMapping("/pageProd") @GetMapping("/pageProd")
@Operation(summary = "通过分类id商品列表信息" , description = "根据分类ID获取该分类下所有的商品列表信息") @Operation(summary = "通过分类id商品列表信息", description = "根据分类ID获取该分类下所有的商品列表信息")
@Parameters({ @Parameters({
@Parameter(name = "categoryId", description = "分类ID" , required = true), @Parameter(name = "categoryId", description = "分类ID", required = true),
}) })
public ServerResponseEntity<IPage<ProductDto>> prodList( public ServerResponseEntity<IPage<ProductDto>> prodList(
@RequestParam(value = "categoryId") Long categoryId,PageParam<ProductDto> page) { @RequestParam(value = "categoryId") Long categoryId, PageParam<ProductDto> page) {
IPage<ProductDto> productPage = prodService.pageByCategoryId(page, categoryId); IPage<ProductDto> productPage = prodService.pageByCategoryId(page, categoryId);
return ServerResponseEntity.success(productPage); return ServerResponseEntity.success(productPage);
} }
/**
* ID ID
* SKU ID
* 便
*
* @param prodId ID
* @return ServerResponseEntity ProductDto
*/
@GetMapping("/prodInfo") @GetMapping("/prodInfo")
@Operation(summary = "商品详情信息" , description = "根据商品IDprodId获取商品信息") @Operation(summary = "商品详情信息", description = "根据商品IDprodId获取商品信息")
@Parameter(name = "prodId", description = "商品ID" , required = true) @Parameter(name = "prodId", description = "商品ID", required = true)
public ServerResponseEntity<ProductDto> prodInfo(Long prodId) { public ServerResponseEntity<ProductDto> prodInfo(Long prodId) {
// 通过商品服务层的方法,根据商品 ID 从数据库中获取商品的基本信息,若商品不存在则直接返回空的成功响应(可根据实际需求优化此处逻辑,比如返回特定的错误提示)
Product product = prodService.getProductByProdId(prodId); Product product = prodService.getProductByProdId(prodId);
if (product == null) { if (product == null) {
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
// 通过库存单元服务层的方法,根据商品 ID 查询该商品对应的所有 SKU 列表
List<Sku> skuList = skuService.listByProdId(prodId); List<Sku> skuList = skuService.listByProdId(prodId);
// 启用的sku列表 // 使用 Java 8 的 Stream API 过滤出状态为启用(这里假设状态为 1 表示启用)的 SKU 列表,方便前端展示可用的商品库存单元信息
List<Sku> useSkuList = skuList.stream().filter(sku -> sku.getStatus() == 1).collect(Collectors.toList()); List<Sku> useSkuList = skuList.stream().filter(sku -> sku.getStatus() == 1).collect(Collectors.toList());
// 将启用的 SKU 列表设置到商品对象中,完善商品的详细信息
product.setSkuList(useSkuList); product.setSkuList(useSkuList);
ProductDto productDto = BeanUtil.copyProperties(product, ProductDto.class);
// 使用 Hutool 的 BeanUtil 将商品对象转换为对应的 DTO 类型,方便按照前端所需的格式返回数据,避免直接暴露数据库实体对象带来的潜在风险
ProductDto productDto = BeanUtil.copyProperties(product, ProductDto.class);
// 商品的配送方式 // 商品的配送方式字符串解析为对应的对象(这里假设使用了自定义的 JSON 解析方法,具体根据实际情况而定),方便后续判断和获取相关的配送信息
Product.DeliveryModeVO deliveryModeVO = Json.parseObject(product.getDeliveryMode(), Product.DeliveryModeVO.class); Product.DeliveryModeVO deliveryModeVO = Json.parseObject(product.getDeliveryMode(), Product.DeliveryModeVO.class);
// 有店铺配送的方式, 且存在运费模板,才返回运费模板的信息,供前端查阅
if (deliveryModeVO.getHasShopDelivery() && product.getDeliveryTemplateId() != null) { // 判断如果商品有店铺配送的方式,并且商品关联的运费模板 ID 不为空(即存在运费模板),则通过运费模板服务层的方法,
// 根据运费模板 ID 获取详细的运费模板及关联信息,并设置到商品详情的 DTO 对象中,供前端查阅运费相关情况,方便用户下单时参考运费成本
if (deliveryModeVO.getHasShopDelivery() && product.getDeliveryTemplateId()!= null) {
Transport transportAndAllItems = transportService.getTransportAndAllItems(product.getDeliveryTemplateId()); Transport transportAndAllItems = transportService.getTransportAndAllItems(product.getDeliveryTemplateId());
productDto.setTransport(transportAndAllItems); productDto.setTransport(transportAndAllItems);
} }
@ -94,38 +120,65 @@ public class ProdController {
return ServerResponseEntity.success(productDto); return ServerResponseEntity.success(productDto);
} }
/**
*
* 便
*
* @param page
* @return ServerResponseEntity ProductDto 便使
*/
@GetMapping("/lastedProdPage") @GetMapping("/lastedProdPage")
@Operation(summary = "新品推荐" , description = "获取新品推荐商品列表") @Operation(summary = "新品推荐", description = "获取新品推荐商品列表")
@Parameters({ @Parameters({})
})
public ServerResponseEntity<IPage<ProductDto>> lastedProdPage(PageParam<ProductDto> page) { public ServerResponseEntity<IPage<ProductDto>> lastedProdPage(PageParam<ProductDto> page) {
IPage<ProductDto> productPage = prodService.pageByPutAwayTime(page); IPage<ProductDto> productPage = prodService.pageByPutAwayTime(page);
return ServerResponseEntity.success(productPage); return ServerResponseEntity.success(productPage);
} }
/**
* ID ID ID
* 便
*
* @param tagId ID
* @param page
* @return ServerResponseEntity ProductDto 便使
*/
@GetMapping("/prodListByTagId") @GetMapping("/prodListByTagId")
@Operation(summary = "通过分组标签获取商品列表" , description = "通过分组标签idtagId获取商品列表") @Operation(summary = "通过分组标签获取商品列表", description = "通过分组标签idtagId获取商品列表")
@Parameters({ @Parameters({
@Parameter(name = "tagId", description = "当前页默认为1" , required = true), @Parameter(name = "tagId", description = "当前页默认为1", required = true),
}) })
public ServerResponseEntity<IPage<ProductDto>> prodListByTagId( public ServerResponseEntity<IPage<ProductDto>> prodListByTagId(
@RequestParam(value = "tagId") Long tagId,PageParam<ProductDto> page) { @RequestParam(value = "tagId") Long tagId, PageParam<ProductDto> page) {
IPage<ProductDto> productPage = prodService.pageByTagId(page, tagId); IPage<ProductDto> productPage = prodService.pageByTagId(page, tagId);
return ServerResponseEntity.success(productPage); return ServerResponseEntity.success(productPage);
} }
/**
*
*
*
* @param page
* @return ServerResponseEntity ProductDto 便使
*/
@GetMapping("/moreBuyProdList") @GetMapping("/moreBuyProdList")
@Operation(summary = "每日疯抢" , description = "获取销量最多的商品列表") @Operation(summary = "每日疯抢", description = "获取销量最多的商品列表")
@Parameters({}) @Parameters({})
public ServerResponseEntity<IPage<ProductDto>> moreBuyProdList(PageParam<ProductDto> page) { public ServerResponseEntity<IPage<ProductDto>> moreBuyProdList(PageParam<ProductDto> page) {
IPage<ProductDto> productPage = prodService.moreBuyProdList(page); IPage<ProductDto> productPage = prodService.moreBuyProdList(page);
return ServerResponseEntity.success(productPage); return ServerResponseEntity.success(productPage);
} }
/**
*
* 便
*
* @return ServerResponseEntity TagProductDto 便使
*/
@GetMapping("/tagProdList") @GetMapping("/tagProdList")
@Operation(summary = "首页所有标签商品接口" , description = "获取首页所有标签商品接口") @Operation(summary = "首页所有标签商品接口", description = "获取首页所有标签商品接口")
public ServerResponseEntity<List<TagProductDto>> getTagProdList() { public ServerResponseEntity<List<TagProductDto>> getTagProdList() {
List<TagProductDto> productDtoList = prodService.tagProdList(); List<TagProductDto> productDtoList = prodService.tagProdList();
return ServerResponseEntity.success(productDtoList); return ServerResponseEntity.success(productDtoList);
} }
} }

@ -25,27 +25,39 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List; import java.util.List;
/** /**
*
* ProdTagService
*
* @author lanhai * @author lanhai
*/ */
@RestController @RestController
// 设置该控制器类对应的请求映射路径,后续类中的接口方法路径会基于此进行拼接,这里表明是与商品分组标签相关接口所在的基础路径。
@RequestMapping("/prod/tag") @RequestMapping("/prod/tag")
// 使用 @Tag 注解为该控制器类添加标签说明,用于在 API 文档(如 Swagger 生成的文档)中对该类下的接口进行分类展示,这里表明是“商品分组标签接口”相关的一组接口。
@Tag(name = "商品分组标签接口") @Tag(name = "商品分组标签接口")
// 通过Lombok的 @AllArgsConstructor 注解生成包含所有参数的构造函数用于依赖注入ProdTagService实例方便后续调用其业务方法获取标签数据。
@AllArgsConstructor @AllArgsConstructor
public class ProdTagController { public class ProdTagController {
// 注入ProdTagService用于处理与商品分组标签相关的业务逻辑例如从数据库中查询所有商品分组标签列表等操作。
private ProdTagService prodTagService; private ProdTagService prodTagService;
/** /**
* *
* @GetMapping HTTP GET /prodTagListGET
* @Operation API summarydescription便使
*
* @return ServerResponseEntityProdTagDto
*/ */
@GetMapping("/prodTagList") @GetMapping("/prodTagList")
@Operation(summary = "商品分组标签列表" , description = "获取所有的商品分组列表") @Operation(summary = "商品分组标签列表", description = "获取所有的商品分组列表")
public ServerResponseEntity<List<ProdTagDto>> getProdTagList() { public ServerResponseEntity<List<ProdTagDto>> getProdTagList() {
// 调用ProdTagService的listProdTag方法从数据库或其他数据源获取商品分组标签的原始数据列表以ProdTag对象形式返回
List<ProdTag> prodTagList = prodTagService.listProdTag(); List<ProdTag> prodTagList = prodTagService.listProdTag();
// 使用hutool的BeanUtil工具类将ProdTag对象列表转换为ProdTagDto对象列表ProdTagDto可能是用于对外展示的、经过筛选或格式调整后的视图对象
// 这样可以避免直接将内部的业务实体对象暴露给客户端,更好地控制数据的展示格式和安全性。
List<ProdTagDto> prodTagDtoList = BeanUtil.copyToList(prodTagList, ProdTagDto.class); List<ProdTagDto> prodTagDtoList = BeanUtil.copyToList(prodTagList, ProdTagDto.class);
// 将转换后的ProdTagDto列表封装在表示成功的ServerResponseEntity对象中返回给客户端遵循统一的接口响应格式规范。
return ServerResponseEntity.success(prodTagDtoList); return ServerResponseEntity.success(prodTagDtoList);
} }
}
}

@ -8,9 +8,9 @@
* *
*/ */
// 该类所属的包名表明其位于商城API的控制器包下主要用于处理与搜索相关的各种接口请求及对应的业务逻辑例如热搜查询、商品搜索等功能。
package com.yami.shop.api.controller; package com.yami.shop.api.controller;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yami.shop.common.util.PageParam; import com.yami.shop.common.util.PageParam;
@ -32,66 +32,115 @@ import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
* SearchControllerSpring RESTful
* HotSearchServiceProductServiceSwagger便使
*
* @author lanhai * @author lanhai
*/ */
@RestController @RestController
// 定义该控制器类的基础请求路径,所有该类中的接口请求路径都将以此为前缀,表明是与搜索相关的操作接口。
@RequestMapping("/search") @RequestMapping("/search")
// 使用Swagger的@Tag注解对该控制器类进行标记用于在API文档中生成对应的分类标签方便接口文档的分类展示和阅读这里表示该类下的接口都属于“搜索接口”这一分类。
@Tag(name = "搜索接口") @Tag(name = "搜索接口")
// 使用lombok的@AllArgsConstructor注解自动生成包含所有final字段的构造函数方便依赖注入这里会为HotSearchService和ProductService生成对应的构造函数参数以便在类中使用这两个服务类的实例。
@AllArgsConstructor @AllArgsConstructor
public class SearchController { public class SearchController {
// 通过构造函数注入HotSearchService实例用于调用与热搜相关的业务逻辑方法比如根据店铺ID或获取全局热搜等操作来获取热搜数据。
private final HotSearchService hotSearchService; private final HotSearchService hotSearchService;
// 通过构造函数注入ProductService实例用于调用与商品搜索相关的业务逻辑方法例如根据商品名、排序条件等进行分页搜索商品并获取相应的商品搜索结果数据。
private final ProductService productService; private final ProductService productService;
/**
* ID
* IDshopIdnumbersortHotSearchServicegetHotSearchDtoByShopId
* getListResponseEntityServerResponseEntity
*
* @param shopId
* @param number
* @param sort 01
* @return ServerResponseEntityList<HotSearchDto>HotSearchDto
*/
@GetMapping("/hotSearchByShopId") @GetMapping("/hotSearchByShopId")
@Operation(summary = "查看店铺热搜" , description = "根据店铺id,热搜数量获取热搜") // 使用Swagger的@Operation注解对该接口方法进行描述用于在API文档中生成对应的接口说明信息这里简要说明了该接口的功能是查看店铺热搜。
@Operation(summary = "查看店铺热搜", description = "根据店铺id,热搜数量获取热搜")
// 使用Swagger的@Parameters注解对接口方法的多个参数进行统一描述通过包含多个@Parameter注解来分别详细说明每个参数的名称、描述以及是否必填等信息方便在API文档中展示参数详情。
@Parameters({ @Parameters({
@Parameter(name = "shopId", description = "店铺id" , required = true), @Parameter(name = "shopId", description = "店铺id", required = true),
@Parameter(name = "number", description = "取数" , required = true), @Parameter(name = "number", description = "取数", required = true),
@Parameter(name = "sort", description = "是否按照顺序(0 否 1是)"), @Parameter(name = "sort", description = "是否按照顺序(0 否 1是)")
}) })
public ServerResponseEntity<List<HotSearchDto>> hotSearchByShopId(Long shopId,Integer number,Integer sort) { public ServerResponseEntity<List<HotSearchDto>> hotSearchByShopId(Long shopId, Integer number, Integer sort) {
List<HotSearchDto> list = hotSearchService.getHotSearchDtoByShopId(shopId); List<HotSearchDto> list = hotSearchService.getHotSearchDtoByShopId(shopId);
return getListResponseEntity(number, sort, list); return getListResponseEntity(number, sort, list);
} }
/**
*
* numbersortHotSearchServicegetHotSearchDtoByShopIdID0L
* getListResponseEntityServerResponseEntity
*
* @param number
* @param sort 01
* @return ServerResponseEntityList<HotSearchDto>HotSearchDto
*/
@GetMapping("/hotSearch") @GetMapping("/hotSearch")
@Operation(summary = "查看全局热搜" , description = "根据店铺id,热搜数量获取热搜") @Operation(summary = "查看全局热搜", description = "根据店铺id,热搜数量获取热搜")
@Parameters({ @Parameters({
@Parameter(name = "number", description = "取数" , required = true), @Parameter(name = "number", description = "取数", required = true),
@Parameter(name = "sort", description = "是否按照顺序(0 否 1是)", required = false ), @Parameter(name = "sort", description = "是否按照顺序(0 否 1是)", required = false)
}) })
public ServerResponseEntity<List<HotSearchDto>> hotSearch(Integer number,Integer sort) { public ServerResponseEntity<List<HotSearchDto>> hotSearch(Integer number, Integer sort) {
List<HotSearchDto> list = hotSearchService.getHotSearchDtoByShopId(0L); List<HotSearchDto> list = hotSearchService.getHotSearchDtoByShopId(0L);
return getListResponseEntity(number, sort, list); return getListResponseEntity(number, sort, list);
} }
/**
*
* numbersortlist
* null0使Collections.shuffle
* ServerResponseEntity
* numberServerResponseEntity
*
* @param number
* @param sort
* @param list HotSearchDto
* @return ServerResponseEntityList<HotSearchDto>
*/
private ServerResponseEntity<List<HotSearchDto>> getListResponseEntity(Integer number, Integer sort, List<HotSearchDto> list) { private ServerResponseEntity<List<HotSearchDto>> getListResponseEntity(Integer number, Integer sort, List<HotSearchDto> list) {
if(sort == null || sort == 0){ if (sort == null || sort == 0) {
Collections.shuffle(list); Collections.shuffle(list);
} }
if(!CollectionUtil.isNotEmpty(list) || list.size()< number){ if (!CollectionUtil.isNotEmpty(list) || list.size() < number) {
return ServerResponseEntity.success(list); return ServerResponseEntity.success(list);
} }
return ServerResponseEntity.success(list.subList(0, number)); return ServerResponseEntity.success(list.subList(0, number));
} }
/**
*
* pageprodNamesortorderByIDshopIdProductServicegetSearchProdDtoPageByProdName
* IPage<SearchProdDto>ServerResponseEntity
*
* @param page
* @param prodName
* @param sort 012
* @param orderBy 01sort使
* @param shopId
* @return ServerResponseEntityIPage<SearchProdDto>
*/
@GetMapping("/searchProdPage") @GetMapping("/searchProdPage")
@Operation(summary = "分页排序搜索商品" , description = "根据商品名搜索") @Operation(summary = "分页排序搜索商品", description = "根据商品名搜索")
@Parameters({ @Parameters({
@Parameter(name = "prodName", description = "商品名" , required = true), @Parameter(name = "prodName", description = "商品名", required = true),
@Parameter(name = "sort", description = "排序(0 默认排序 1销量排序 2价格排序)"),
@Parameter(name = "orderBy", description = "排序(0升序 1降序)"), @Parameter(name = "orderBy", description = "排序(0升序 1降序)"),
@Parameter(name = "shopId", description = "店铺id" , required = true), @Parameter(name = "shopId", description = "店铺id", required = true),
@Parameter(name = "sort", description = "排序(0 默认排序 1销量排序 2价格排序)")
}) })
public ServerResponseEntity<IPage<SearchProdDto>> searchProdPage(PageParam page, String prodName, Integer sort, Integer orderBy, Long shopId) { public ServerResponseEntity<IPage<SearchProdDto>> searchProdPage(PageParam page, String prodName, Integer sort, Integer orderBy, Long shopId) {
return ServerResponseEntity.success(productService.getSearchProdDtoPageByProdName(page,prodName,sort,orderBy)); return ServerResponseEntity.success(productService.getSearchProdDtoPageByProdName(page, prodName, sort, orderBy));
} }
}
}

@ -40,136 +40,195 @@ import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
*
* /
* `Swagger`便使
*
* @author lanhai * @author lanhai
*/ */
@RestController @RestController
@RequestMapping("/p/shopCart") @RequestMapping("/p/shopCart")
@Tag(name = "购物车接口") @Tag(name = "购物车接口")
// 使用 @AllArgsConstructor 注解,由 lombok 自动生成包含所有成员变量的构造函数,用于依赖注入
@AllArgsConstructor @AllArgsConstructor
public class ShopCartController { public class ShopCartController {
// 用于处理购物车相关业务逻辑的服务层对象,比如更新购物车、获取购物车商品项等操作
private final BasketService basketService; private final BasketService basketService;
// 用于处理商品相关业务逻辑的服务层对象,比如获取商品信息等操作
private final ProductService productService; private final ProductService productService;
// 用于处理商品库存单元SKU相关业务逻辑的服务层对象比如获取SKU信息等操作
private final SkuService skuService; private final SkuService skuService;
// Spring应用上下文对象用于发布事件等操作例如发布购物车相关事件
private final ApplicationContext applicationContext; private final ApplicationContext applicationContext;
/** /**
* *
* ID
* *
* @param basketIdShopCartParamMap * @param basketIdShopCartParamMap ID
* @return * @return ServerResponseEntity<List<ShopCartDto>>DTO
*/ */
@PostMapping("/info") @PostMapping("/info")
@Operation(summary = "获取用户购物车信息" , description = "获取用户购物车信息,参数为用户选中的活动项数组,以购物车id为key") @Operation(summary = "获取用户购物车信息", description = "获取用户购物车信息,参数为用户选中的活动项数组,以购物车id为key")
public ServerResponseEntity<List<ShopCartDto>> info(@RequestBody Map<Long, ShopCartParam> basketIdShopCartParamMap) { public ServerResponseEntity<List<ShopCartDto>> info(@RequestBody Map<Long, ShopCartParam> basketIdShopCartParamMap) {
// 获取当前登录用户的ID用于关联购物车与用户
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
// 更新购物车信息 // 如果传入的购物车参数映射表不为空,调用购物车服务层方法根据参数更新购物车信息
if (MapUtil.isNotEmpty(basketIdShopCartParamMap)) { if (MapUtil.isNotEmpty(basketIdShopCartParamMap)) {
basketService.updateBasketByShopCartParam(userId, basketIdShopCartParamMap); basketService.updateBasketByShopCartParam(userId, basketIdShopCartParamMap);
} }
// 拿到购物车的所有item // 调用购物车服务层方法获取该用户的购物车商品项列表
List<ShopCartItemDto> shopCartItems = basketService.getShopCartItems(userId); List<ShopCartItemDto> shopCartItems = basketService.getShopCartItems(userId);
return ServerResponseEntity.success(basketService.getShopCarts(shopCartItems)); return ServerResponseEntity.success(basketService.getShopCarts(shopCartItems));
} }
/**
*
* ID
*
* @param basketIds ID
* @return ServerResponseEntity<Void>
*/
@DeleteMapping("/deleteItem") @DeleteMapping("/deleteItem")
@Operation(summary = "删除用户购物车物品" , description = "通过购物车id删除用户购物车物品") @Operation(summary = "删除用户购物车物品", description = "通过购物车id删除用户购物车物品")
public ServerResponseEntity<Void> deleteItem(@RequestBody List<Long> basketIds) { public ServerResponseEntity<Void> deleteItem(@RequestBody List<Long> basketIds) {
// 获取当前登录用户的ID用于关联购物车与用户
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
basketService.deleteShopCartItemsByBasketIds(userId, basketIds); basketService.deleteShopCartItemsByBasketIds(userId, basketIds);
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
/**
*
*
*
* @return ServerResponseEntity<String>"删除成功"
*/
@DeleteMapping("/deleteAll") @DeleteMapping("/deleteAll")
@Operation(summary = "清空用户购物车所有物品" , description = "清空用户购物车所有物品") @Operation(summary = "清空用户购物车所有物品", description = "清空用户购物车所有物品")
public ServerResponseEntity<String> deleteAll() { public ServerResponseEntity<String> deleteAll() {
// 获取当前登录用户的ID用于关联购物车与用户
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
basketService.deleteAllShopCartItems(userId); basketService.deleteAllShopCartItems(userId);
return ServerResponseEntity.success("删除成功"); return ServerResponseEntity.success("删除成功");
} }
/**
*
* 0
*
*
* @param param IDSKU IDID
* @return ServerResponseEntity<String>
*/
@PostMapping("/changeItem") @PostMapping("/changeItem")
@Operation(summary = "添加、修改用户购物车物品", description = "通过商品id(prodId)、skuId、店铺Id(shopId),添加/修改用户购物车商品,并传入改变的商品个数(count)" + @Operation(summary = "添加、修改用户购物车物品", description = "通过商品id(prodId)、skuId、店铺Id(shopId),添加/修改用户购物车商品,并传入改变的商品个数(count)" +
"当count为正值时增加商品数量当count为负值时将减去商品的数量当最终count值小于0时会将商品从购物车里面删除") "当count为正值时增加商品数量当count为负值时将减去商品的数量当最终count值小于0时会将商品从购物车里面删除")
public ServerResponseEntity<String> addItem(@Valid @RequestBody ChangeShopCartParam param) { public ServerResponseEntity<String> addItem(@Valid @RequestBody ChangeShopCartParam param) {
// 如果更改的商品数量为0则返回提示输入更改数量的失败响应
if (param.getCount() == 0) { if (param.getCount() == 0) {
return ServerResponseEntity.showFailMsg("输入更改数量"); return ServerResponseEntity.showFailMsg("输入更改数量");
} }
// 获取当前登录用户的ID用于关联购物车与用户
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
// 获取当前用户的购物车商品项列表,用于后续判断商品是否已在购物车中等操作
List<ShopCartItemDto> shopCartItems = basketService.getShopCartItems(userId); List<ShopCartItemDto> shopCartItems = basketService.getShopCartItems(userId);
// 根据传入的商品ID获取商品信息
Product prodParam = productService.getProductByProdId(param.getProdId()); Product prodParam = productService.getProductByProdId(param.getProdId());
// 根据传入的SKU ID获取SKU信息
Sku skuParam = skuService.getSkuBySkuId(param.getSkuId()); Sku skuParam = skuService.getSkuBySkuId(param.getSkuId());
// 当商品状态不正常时,不能添加到购物车 // 当商品状态不正常下架状态状态值不为1返回提示商品已下架的失败响应
if (prodParam.getStatus() != 1 || skuParam.getStatus() != 1) { if (prodParam.getStatus()!= 1 || skuParam.getStatus()!= 1) {
return ServerResponseEntity.showFailMsg("当前商品已下架"); return ServerResponseEntity.showFailMsg("当前商品已下架");
} }
// 遍历购物车商品项列表,判断要操作的商品是否已在购物车中
for (ShopCartItemDto shopCartItemDto : shopCartItems) { for (ShopCartItemDto shopCartItemDto : shopCartItems) {
if (Objects.equals(param.getSkuId(), shopCartItemDto.getSkuId())) { if (Objects.equals(param.getSkuId(), shopCartItemDto.getSkuId())) {
Basket basket = new Basket(); Basket basket = new Basket();
basket.setUserId(userId); basket.setUserId(userId);
// 更新购物车中该商品的数量,为原数量加上传入的更改数量
basket.setBasketCount(param.getCount() + shopCartItemDto.getProdCount()); basket.setBasketCount(param.getCount() + shopCartItemDto.getProdCount());
basket.setBasketId(shopCartItemDto.getBasketId()); basket.setBasketId(shopCartItemDto.getBasketId());
// 防止购物车变成负数 // 防止购物车商品数量变成负数如果数量小于等于0则删除该购物车商品项
if (basket.getBasketCount() <= 0) { if (basket.getBasketCount() <= 0) {
basketService.deleteShopCartItemsByBasketIds(userId, Collections.singletonList(basket.getBasketId())); basketService.deleteShopCartItemsByBasketIds(userId, Collections.singletonList(basket.getBasketId()));
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
// 当sku实际库存不足时不能添加到购物车 // 当SKU实际库存不足当前库存小于购物车中要设置的数量且原购物车商品数量大于0说明是修改操作返回提示库存不足的失败响应
if (skuParam.getStocks() < basket.getBasketCount() && shopCartItemDto.getProdCount() > 0) { if (skuParam.getStocks() < basket.getBasketCount() && shopCartItemDto.getProdCount() > 0) {
return ServerResponseEntity.showFailMsg("库存不足"); return ServerResponseEntity.showFailMsg("库存不足");
} }
// 调用购物车服务层方法更新购物车商品项信息
basketService.updateShopCartItem(basket); basketService.updateShopCartItem(basket);
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
} }
// 防止购物车已被删除的情况下,添加了负数的商品 // 如果商品不在购物车中,且传入的更改数量为负数,说明商品已被删除,返回相应提示的失败响应
if (param.getCount() < 0) { if (param.getCount() < 0) {
return ServerResponseEntity.showFailMsg("商品已从购物车移除"); return ServerResponseEntity.showFailMsg("商品已从购物车移除");
} }
// 当sku实际库存不足时不能添加到购物车
// 当SKU实际库存不足当前库存小于要添加的数量说明是添加操作返回提示库存不足的失败响应
if (skuParam.getStocks() < param.getCount()) { if (skuParam.getStocks() < param.getCount()) {
return ServerResponseEntity.showFailMsg("库存不足"); return ServerResponseEntity.showFailMsg("库存不足");
} }
// 所有都正常时
basketService.addShopCartItem(param,userId); // 所有验证都通过时,调用购物车服务层方法添加购物车商品项,并返回添加成功的响应
basketService.addShopCartItem(param, userId);
return ServerResponseEntity.success("添加成功"); return ServerResponseEntity.success("添加成功");
} }
/**
*
* 0
*
* @return ServerResponseEntity<Integer>
*/
@GetMapping("/prodCount") @GetMapping("/prodCount")
@Operation(summary = "获取购物车商品数量" , description = "获取所有购物车商品数量") @Operation(summary = "获取购物车商品数量", description = "获取所有购物车商品数量")
public ServerResponseEntity<Integer> prodCount() { public ServerResponseEntity<Integer> prodCount() {
// 获取当前登录用户的ID用于关联购物车与用户
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
// 获取当前用户的购物车商品项列表
List<ShopCartItemDto> shopCartItems = basketService.getShopCartItems(userId); List<ShopCartItemDto> shopCartItems = basketService.getShopCartItems(userId);
if (CollectionUtil.isEmpty(shopCartItems)) { if (CollectionUtil.isEmpty(shopCartItems)) {
return ServerResponseEntity.success(0); return ServerResponseEntity.success(0);
} }
// 使用流操作计算购物车商品项列表中所有商品数量的总和
Integer totalCount = shopCartItems.stream().map(ShopCartItemDto::getProdCount).reduce(0, Integer::sum); Integer totalCount = shopCartItems.stream().map(ShopCartItemDto::getProdCount).reduce(0, Integer::sum);
return ServerResponseEntity.success(totalCount); return ServerResponseEntity.success(totalCount);
} }
/**
*
* ID
*
* @return ServerResponseEntity<List<ShopCartExpiryItemDto>>DTO
*/
@GetMapping("/expiryProdList") @GetMapping("/expiryProdList")
@Operation(summary = "获取购物车失效商品信息" , description = "获取购物车失效商品列表") @Operation(summary = "获取购物车失效商品信息", description = "获取购物车失效商品列表")
public ServerResponseEntity<List<ShopCartExpiryItemDto>> expiryProdList() { public ServerResponseEntity<List<ShopCartExpiryItemDto>> expiryProdList() {
// 获取当前登录用户的ID用于关联购物车与用户
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
// 获取当前用户购物车中的失效商品项列表
List<ShopCartItemDto> shopCartItems = basketService.getShopCartExpiryItems(userId); List<ShopCartItemDto> shopCartItems = basketService.getShopCartExpiryItems(userId);
//根据店铺ID划分item // 根据店铺ID对失效商品项列表进行分组得到以店铺ID为键对应商品项列表为值的映射表
Map<Long, List<ShopCartItemDto>> shopCartItemDtoMap = shopCartItems.stream().collect(Collectors.groupingBy(ShopCartItemDto::getShopId)); Map<Long, List<ShopCartItemDto>> shopCartItemDtoMap = shopCartItems.stream().collect(Collectors.groupingBy(ShopCartItemDto::getShopId));
// 返回一个店铺对应的所有信息 // 用于存储构建好的每个店铺对应的购物车失效商品信息对象列表
List<ShopCartExpiryItemDto> shopcartExpiryitems = Lists.newArrayList(); List<ShopCartExpiryItemDto> shopcartExpiryitems = Lists.newArrayList();
// 遍历分组后的店铺ID集合构建每个店铺对应的购物车失效商品信息对象并添加到列表中
for (Long key : shopCartItemDtoMap.keySet()) { for (Long key : shopCartItemDtoMap.keySet()) {
ShopCartExpiryItemDto shopCartExpiryItemDto = new ShopCartExpiryItemDto(); ShopCartExpiryItemDto shopCartExpiryItemDto = new ShopCartExpiryItemDto();
shopCartExpiryItemDto.setShopId(key); shopCartExpiryItemDto.setShopId(key);
@ -185,66 +244,44 @@ public class ShopCartController {
return ServerResponseEntity.success(shopcartExpiryitems); return ServerResponseEntity.success(shopcartExpiryitems);
} }
/**
*
*
*
* @return ServerResponseEntity<Void>
*/
@DeleteMapping("/cleanExpiryProdList") @DeleteMapping("/cleanExpiryProdList")
@Operation(summary = "清空用户失效商品" , description = "清空用户失效商品") @Operation(summary = "清空用户失效商品", description = "清空用户失效商品")
public ServerResponseEntity<Void> cleanExpiryProdList() { public ServerResponseEntity<Void> cleanExpiryProdList() {
// 获取当前登录用户的ID用于关联购物车与用户
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
basketService.cleanExpiryProdList(userId); basketService.cleanExpiryProdList(userId);
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
/**
*
* IDIDDTO
*
* @param basketIds ID
* @return ServerResponseEntity<ShopCartAmountDto>DTO
*/
@PostMapping("/totalPay") @PostMapping("/totalPay")
@Operation(summary = "获取选中购物项总计、选中的商品数量" , description = "获取选中购物项总计、选中的商品数量,参数为购物车id数组") @Operation(summary = "获取选中购物项总计、选中的商品数量", description = "获取选中购物项总计、选中的商品数量,参数为购物车id数组")
public ServerResponseEntity<ShopCartAmountDto> getTotalPay(@RequestBody List<Long> basketIds) { public ServerResponseEntity<ShopCartAmountDto> getTotalPay(@RequestBody List<Long> basketIds) {
// 拿到购物车的所有item // 拿到购物车的所有item
List<ShopCartItemDto> dbShopCartItems = basketService.getShopCartItems(SecurityUtils.getUser().getUserId()); List<ShopCartItemDto> dbShopCartItems = basketService.getShopCartItems(SecurityUtils.getUser().getUserId());
// 从所有购物车商品项中筛选出传入的购物车ID列表对应的商品项
List<ShopCartItemDto> chooseShopCartItems = dbShopCartItems List<ShopCartItemDto> chooseShopCartItems = dbShopCartItems
.stream() .stream()
.filter(shopCartItemDto -> { .filter(shopCartItemDto -> {
for (Long basketId : basketIds) { for (Long basketId : basketIds) {
if (Objects.equals(basketId,shopCartItemDto.getBasketId())) { if (Objects.equals(basketId, shopCartItemDto.getBasketId())) {
return true; return true;
} }
} }
return false; return false;
}) })
.toList(); .toList();
// 根据店铺ID划分item
Map<Long, List<ShopCartItemDto>> shopCartMap = chooseShopCartItems.stream().collect(Collectors.groupingBy(ShopCartItemDto::getShopId));
double total = 0.0;
int count = 0;
double reduce = 0.0;
for (Long shopId : shopCartMap.keySet()) {
//获取店铺的所有商品项
List<ShopCartItemDto> shopCartItemDtoList = shopCartMap.get(shopId);
// 构建每个店铺的购物车信息
ShopCartDto shopCart = new ShopCartDto();
shopCart.setShopId(shopId);
applicationContext.publishEvent(new ShopCartEvent(shopCart, shopCartItemDtoList));
List<ShopCartItemDiscountDto> shopCartItemDiscounts = shopCart.getShopCartItemDiscounts();
for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartItemDiscounts) {
List<ShopCartItemDto> shopCartItems = shopCartItemDiscount.getShopCartItems();
for (ShopCartItemDto shopCartItem : shopCartItems) {
count = shopCartItem.getProdCount() + count;
total = Arith.add(shopCartItem.getProductTotalAmount(), total);
}
}
}
ShopCartAmountDto shopCartAmountDto = new ShopCartAmountDto();
shopCartAmountDto.setCount(count);
shopCartAmountDto.setTotalMoney(total);
shopCartAmountDto.setSubtractMoney(reduce);
shopCartAmountDto.setFinalMoney(Arith.sub(shopCartAmountDto.getTotalMoney(), shopCartAmountDto.getSubtractMoney()));
return ServerResponseEntity.success(shopCartAmountDto);
}
}

@ -27,6 +27,9 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List; import java.util.List;
/** /**
* SKU ID
* DTO 使
*
* @author lanhai * @author lanhai
*/ */
@RestController @RestController
@ -35,20 +38,29 @@ import java.util.List;
@AllArgsConstructor @AllArgsConstructor
public class SkuController { public class SkuController {
// 通过构造函数注入的方式引入 SKU 服务层接口,方便后续调用其提供的业务逻辑方法来处理与 SKU 相关的操作,比如查询 SKU 列表等。
private final SkuService skuService; private final SkuService skuService;
/**
* ID ID 使 MyBatis Plus LambdaQueryWrapper
* 1 isDelete 0 SKU
* SKU DTO ServerResponseEntity 便使
*
* @param prodId ID SKU Sku prodId
* @return ServerResponseEntity SkuDto SKU SKU
*/
@GetMapping("/getSkuList") @GetMapping("/getSkuList")
@Operation(summary = "通过prodId获取商品全部规格列表" , description = "通过prodId获取商品全部规格列表") @Operation(summary = "通过prodId获取商品全部规格列表", description = "通过prodId获取商品全部规格列表")
@Parameter(name = "prodId", description = "商品id" ) @Parameter(name = "prodId", description = "商品id")
public ServerResponseEntity<List<SkuDto>> getSkuListByProdId(Long prodId) { public ServerResponseEntity<List<SkuDto>> getSkuListByProdId(Long prodId) {
// 使用 MyBatis Plus 的 LambdaQueryWrapper 构建查询条件,设置筛选条件为状态为启用、未被删除且属于指定商品 ID 的 SKU 记录。
List<Sku> skus = skuService.list(new LambdaQueryWrapper<Sku>() List<Sku> skus = skuService.list(new LambdaQueryWrapper<Sku>()
.eq(Sku::getStatus, 1) .eq(Sku::getStatus, 1)
.eq(Sku::getIsDelete, 0) .eq(Sku::getIsDelete, 0)
.eq(Sku::getProdId, prodId) .eq(Sku::getProdId, prodId)
); );
// 使用 Hutool 的 BeanUtil 将查询到的 Sku 实体列表转换为 SkuDto 类型的列表,方便按照前端期望的格式返回数据,避免直接暴露实体对象的一些潜在问题。
List<SkuDto> skuDtoList = BeanUtil.copyToList(skus, SkuDto.class); List<SkuDto> skuDtoList = BeanUtil.copyToList(skus, SkuDto.class);
return ServerResponseEntity.success(skuDtoList); return ServerResponseEntity.success(skuDtoList);
} }
} }

@ -25,24 +25,38 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
/** /**
*
* SmsLogService
*
* @author lanhai * @author lanhai
*/ */
@RestController @RestController
// 设置该控制器类对应的请求映射路径,后续类中的接口方法路径会基于此进行拼接,这里表明是与发送验证码相关接口所在的基础路径。
@RequestMapping("/p/sms") @RequestMapping("/p/sms")
// 使用 @Tag 注解为该控制器类添加标签说明,用于在 API 文档(如 Swagger 生成的文档)中对该类下的接口进行分类展示,这里表明是“发送验证码接口”相关的一组接口。
@Tag(name = "发送验证码接口") @Tag(name = "发送验证码接口")
public class SmsController { public class SmsController {
@Autowired // 注入SmsLogService用于处理与短信验证码相关的业务逻辑例如实际发送短信、记录短信发送日志等操作。
private SmsLogService smsLogService; @Autowired
private SmsLogService smsLogService;
/** /**
* *
* @PostMapping HTTP POST /sendPOST
* @Operation API summarydescription便使
*
* @param sendSmsParam SendSmsParam
* @return ServerResponseEntityVoid
*/ */
@PostMapping("/send") @PostMapping("/send")
@Operation(summary = "发送验证码" , description = "用户的发送验证码") @Operation(summary = "发送验证码", description = "用户的发送验证码")
public ServerResponseEntity<Void> audit(@RequestBody SendSmsParam sendSmsParam) { public ServerResponseEntity<Void> audit(@RequestBody SendSmsParam sendSmsParam) {
String userId = SecurityUtils.getUser().getUserId(); // 通过SecurityUtils工具类获取当前登录用户的ID可能用于记录短信发送与用户的关联关系或者作为短信发送业务逻辑中的一部分验证等操作。
smsLogService.sendSms(SmsType.VALID, userId, sendSmsParam.getMobile(),Maps.newHashMap()); String userId = SecurityUtils.getUser().getUserId();
// 调用SmsLogService的sendSms方法来发送短信验证码传入短信类型这里指定为SmsType.VALID表示验证类型的短信、用户ID、手机号码以及一个空的Map可能用于传递一些额外的短信发送配置参数等目前为空作为参数进行短信发送操作。
return ServerResponseEntity.success(); smsLogService.sendSms(SmsType.VALID, userId, sendSmsParam.getMobile(), Maps.newHashMap());
return ServerResponseEntity.success();
} }
} }

@ -8,13 +8,13 @@
* *
*/ */
// 该类所属的包名表明其位于商城API的控制器包下主要用于处理与用户收藏相关的各种接口请求及对应的业务逻辑比如获取收藏数据、判断是否收藏、添加或取消收藏等操作。
package com.yami.shop.api.controller; package com.yami.shop.api.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yami.shop.bean.app.dto.ProductDto; import com.yami.shop.bean.app.dto.ProductDto;
import com.yami.shop.bean.app.dto.UserCollectionDto; import com.yaami.shop.bean.app.dto.UserCollectionDto;
import com.yami.shop.bean.model.Product; import com.yami.shop.bean.model.Product;
import com.yami.shop.bean.model.UserCollection; import com.yami.shop.bean.model.UserCollection;
import com.yami.shop.common.exception.YamiShopBindException; import com.yami.shop.common.exception.YamiShopBindException;
@ -31,51 +31,87 @@ import org.springframework.web.bind.annotation.*;
import java.util.Date; import java.util.Date;
import java.util.Objects; import java.util.Objects;
/** /**
* UserCollectionControllerSpring RESTful
* UserCollectionServiceProductServiceSwagger便使
*
* @author lanhai * @author lanhai
*/ */
@RestController @RestController
// 定义该控制器类的基础请求路径,所有该类中的接口请求路径都将以此为前缀,表明是与用户收藏相关的操作接口。
@RequestMapping("/p/user/collection") @RequestMapping("/p/user/collection")
// 使用Swagger的@Tag注解对该控制器类进行标记用于在API文档中生成对应的分类标签方便接口文档的分类展示和阅读这里表示该类下的接口都属于“收藏接口”这一分类。
@Tag(name = "收藏接口") @Tag(name = "收藏接口")
// 使用lombok的@AllArgsConstructor注解自动生成包含所有final字段的构造函数方便依赖注入这里会为UserCollectionService和ProductService生成对应的构造函数参数以便在类中使用这两个服务类的实例。
@AllArgsConstructor @AllArgsConstructor
public class UserCollectionController { public class UserCollectionController {
// 通过构造函数注入UserCollectionService实例用于调用与用户收藏记录相关的业务逻辑方法比如查询收藏记录、添加或删除收藏记录等操作。
private final UserCollectionService userCollectionService; private final UserCollectionService userCollectionService;
// 通过构造函数注入ProductService实例用于调用与商品相关的业务逻辑方法例如判断商品是否存在、获取收藏商品列表等操作通常会结合用户收藏记录来实现相关业务功能。
private final ProductService productService; private final ProductService productService;
/**
*
* pageUserCollectionServicegetUserCollectionDtoPageByUserIdIDSecurityUtils
* ServerResponseEntity
*
* @param page
* @return ServerResponseEntityIPage<UserCollectionDto>
*/
@GetMapping("/page") @GetMapping("/page")
@Operation(summary = "分页返回收藏数据" , description = "根据用户id获取") @Operation(summary = "分页返回收藏数据", description = "根据用户id获取")
public ServerResponseEntity<IPage<UserCollectionDto>> getUserCollectionDtoPageByUserId(PageParam page) { public ServerResponseEntity<IPage<UserCollectionDto>> getUserCollectionDtoPageByUserId(PageParam page) {
return ServerResponseEntity.success(userCollectionService.getUserCollectionDtoPageByUserId(page, SecurityUtils.getUser().getUserId())); return ServerResponseEntity.success(userCollectionService.getUserCollectionDtoPageByUserId(page, SecurityUtils.getUser().getUserId()));
} }
/**
* ID
* IDprodIdProductServicecountLambdaQueryWrapper1
* UserCollectionServicecountLambdaQueryWrapperIDIDSecurityUtils
* 0truefalseServerResponseEntity
*
* @param prodId
* @return ServerResponseEntityBooleantruefalse
*/
@GetMapping("isCollection") @GetMapping("isCollection")
@Operation(summary = "根据商品id获取该商品是否在收藏夹中" , description = "传入收藏商品id") @Operation(summary = "根据商品id获取该商品是否在收藏夹中", description = "传入收藏商品id")
public ServerResponseEntity<Boolean> isCollection(Long prodId) { public ServerResponseEntity<Boolean> isCollection(Long prodId) {
if (productService.count(new LambdaQueryWrapper<Product>() if (productService.count(new LambdaQueryWrapper<Product>()
.eq(Product::getProdId, prodId)) < 1) { .eq(Product::getProdId, prodId)) < 1) {
throw new YamiShopBindException("该商品不存在"); throw new YamiShopBindException("该商品不存在");
} }
return ServerResponseEntity.success(userCollectionService.count(new LambdaQueryWrapper<UserCollection>() return ServerResponseEntity.success(userCollectionService.count(new LambdaQueryWrapper<UserCollection>()
.eq(UserCollection::getProdId, prodId) .eq(UserCollection::getProdId, prodId)
.eq(UserCollection::getUserId, SecurityUtils.getUser().getUserId())) > 0); .eq(UserCollection::getUserId, SecurityUtils.getUser().getUserId())) > 0);
} }
/**
* /
* IDprodIdProductServicegetProductByProdIdIDSecurityUtils
* UserCollectionServicecountLambdaQueryWrapper0removeLambdaQueryWrapper
* 0UserCollectionnew Date()IDIDsave
* ServerResponseEntityVoid
*
* @param prodId
* @return ServerResponseEntityVoid
*/
@PostMapping("/addOrCancel") @PostMapping("/addOrCancel")
@Operation(summary = "添加/取消收藏" , description = "传入收藏商品id,如果商品未收藏则收藏商品,已收藏则取消收藏") @Operation(summary = "添加/取消收藏", description = "传入收藏商品id,如果商品未收藏则收藏商品,已收藏则取消收藏")
@Parameter(name = "prodId", description = "商品id" , required = true) @Parameter(name = "prodId", description = "商品id", required = true)
public ServerResponseEntity<Void> addOrCancel(@RequestBody Long prodId) { public ServerResponseEntity<Void> addOrCancel(@RequestBody Long prodId) {
if (Objects.isNull(productService.getProductByProdId(prodId))) { if (Objects.isNull(productService.getProductByProdId(prodId))) {
throw new YamiShopBindException("该商品不存在"); throw new YamiShopBindException("该商品不存在");
} }
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
if (userCollectionService.count(new LambdaQueryWrapper<UserCollection>() if (userCollectionService.count(new LambdaQueryWrapper<UserCollection>()
.eq(UserCollection::getProdId, prodId) .eq(UserCollection::getProdId, prodId)
.eq(UserCollection::getUserId, userId)) > 0) { .eq(UserCollection::getUserId, userId)) > 0) {
userCollectionService.remove(new LambdaQueryWrapper<UserCollection>() userCollectionService.remove(new LambdaQueryWrapper<UserCollection>()
.eq(UserCollection::getProdId, prodId) .eq(UserCollection::getProdId, prodId)
.eq(UserCollection::getUserId, userId)); .eq(UserCollection::getUserId, userId));
} else { } else {
UserCollection userCollection = new UserCollection(); UserCollection userCollection = new UserCollection();
userCollection.setCreateTime(new Date()); userCollection.setCreateTime(new Date());
@ -86,22 +122,36 @@ public class UserCollectionController {
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
/**
*
* SecurityUtilsIDUserCollectionServicecountLambdaQueryWrapperID
* ServerResponseEntity
*
* @return ServerResponseEntityLong
*/
/** /**
* *
*/ */
@GetMapping("count") @GetMapping("count")
@Operation(summary = "查询用户收藏商品数量" , description = "查询用户收藏商品数量") @Operation(summary = "查询用户收藏商品数量", description = "查询用户收藏商品数量")
public ServerResponseEntity<Long> findUserCollectionCount() { public ServerResponseEntity<Long> findUserCollectionCount() {
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
return ServerResponseEntity.success(userCollectionService.count(new LambdaQueryWrapper<UserCollection>().eq(UserCollection::getUserId, userId))); return ServerResponseEntity.success(userCollectionService.count(new LambdaQueryWrapper<UserCollection>().eq(UserCollection::getUserId, userId)));
} }
/**
*
* pageProductServicecollectionProdsIDSecurityUtils
* ServerResponseEntity
*
* @param page
* @return ServerResponseEntityIPage<ProductDto>
*/
@GetMapping("/prods") @GetMapping("/prods")
@Operation(summary = "获取用户收藏商品列表" , description = "获取用户收藏商品列表") @Operation(summary = "获取用户收藏商品列表", description = "获取用户收藏商品列表")
public ServerResponseEntity<IPage<ProductDto>> collectionProds(PageParam page) { public ServerResponseEntity<IPage<ProductDto>> collectionProds(PageParam page) {
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
IPage<ProductDto> productDtoPage = productService.collectionProds(page, userId); IPage<ProductDto> productDtoPage = productService.collectionProds(page, userId);
return ServerResponseEntity.success(productDtoPage); return ServerResponseEntity.success(productDtoPage);
} }
}
}

@ -21,39 +21,58 @@ import lombok.AllArgsConstructor;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import com.yami.shop.common.response.ServerResponseEntity; import com.yami.shop.common.response.ServerResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
/** /**
*
* `Swagger`便使
*
* @author lanhai * @author lanhai
*/ */
@RestController @RestController
@RequestMapping("/p/user") @RequestMapping("/p/user")
@Tag(name = "用户接口") @Tag(name = "用户接口")
// 使用 @AllArgsConstructor 注解,由 lombok 自动生成包含所有成员变量的构造函数,用于依赖注入
@AllArgsConstructor @AllArgsConstructor
public class UserController { public class UserController {
private final UserService userService; // 通过构造函数注入UserService用于调用业务层方法来处理用户相关的业务逻辑
private final UserService userService;
/**
/** *
* * IDDTO
*/ *
@GetMapping("/userInfo") * @return ServerResponseEntity<UserDto>DTO
@Operation(summary = "查看用户信息" , description = "根据用户IDuserId获取用户信息") */
public ServerResponseEntity<UserDto> userInfo() { @GetMapping("/userInfo")
String userId = SecurityUtils.getUser().getUserId(); @Operation(summary = "查看用户信息", description = "根据用户IDuserId获取用户信息")
User user = userService.getById(userId); public ServerResponseEntity<UserDto> userInfo() {
UserDto userDto = BeanUtil.copyProperties(user, UserDto.class); // 获取当前登录用户的ID
return ServerResponseEntity.success(userDto); String userId = SecurityUtils.getUser().getUserId();
} // 调用服务层方法根据用户ID获取用户实体信息
User user = userService.getById(userId);
// 使用 hutool 工具类的方法将User对象转换为UserDto对象用于传输给前端展示的数据格式转换
UserDto userDto = BeanUtil.copyProperties(user, UserDto.class);
return ServerResponseEntity.success(userDto);
}
@PutMapping("/setUserInfo") /**
@Operation(summary = "设置用户信息" , description = "设置用户信息") *
public ServerResponseEntity<Void> setUserInfo(@RequestBody UserInfoParam userInfoParam) { *
String userId = SecurityUtils.getUser().getUserId(); *
User user = new User(); * @param userInfoParam
user.setUserId(userId); * @return ServerResponseEntity<Void>
user.setPic(userInfoParam.getAvatarUrl()); */
user.setNickName(userInfoParam.getNickName()); @PutMapping("/setUserInfo")
userService.updateById(user); @Operation(summary = "设置用户信息", description = "设置用户信息")
return ServerResponseEntity.success(); public ServerResponseEntity<Void> setUserInfo(@RequestBody UserInfoParam userInfoParam) {
} // 获取当前登录用户的ID
} String userId = SecurityUtils.getUser().getUserId();
User user = new User();
user.setUserId(userId);
user.setPic(userInfoParam.getAvatarUrl());
user.setNickName(userInfoParam.getNickName());
userService.updateById(user);
return ServerResponseEntity.success();
}
}

@ -1,6 +1,5 @@
package com.yami.shop.api.controller; package com.yami.shop.api.controller;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -24,7 +23,9 @@ import jakarta.validation.Valid;
import java.util.Date; import java.util.Date;
/** /**
* *
*
* 使
* *
* @author SJL * @author SJL
*/ */
@ -34,68 +35,118 @@ import java.util.Date;
@AllArgsConstructor @AllArgsConstructor
public class UserRegisterController { public class UserRegisterController {
// 通过构造函数注入的方式引入用户服务层接口,用于调用与用户相关的业务逻辑方法,比如保存用户信息、根据条件查询用户等操作。
private final UserService userService; private final UserService userService;
// 通过构造函数注入的方式引入密码编码器,用于对用户密码进行加密处理,保证密码在存储和传输过程中的安全性。
private final PasswordEncoder passwordEncoder; private final PasswordEncoder passwordEncoder;
// 通过构造函数注入的方式引入令牌存储相关的类,用于处理用户登录后令牌的生成、存储以及获取相关信息等操作,实现用户身份认证和授权相关功能。
private final TokenStore tokenStore; private final TokenStore tokenStore;
// 通过构造函数注入的方式引入密码管理相关的类,可能用于对用户输入的密码进行解密(如果有加密传输等情况)或者其他密码相关的管理操作。
private final PasswordManager passwordManager; private final PasswordManager passwordManager;
/**
* 使
*
* 访
*
* @param userRegisterParam @Valid
* @return ServerResponseEntity TokenInfoVO
*/
@PostMapping("/register") @PostMapping("/register")
@Operation(summary = "注册" , description = "用户注册或绑定手机号接口") @Operation(summary = "注册", description = "用户注册或绑定手机号接口")
public ServerResponseEntity<TokenInfoVO> register(@Valid @RequestBody UserRegisterParam userRegisterParam) { public ServerResponseEntity<TokenInfoVO> register(@Valid @RequestBody UserRegisterParam userRegisterParam) {
// 如果用户注册参数中的昵称nickName为空字符串则将用户名userName作为昵称使用保证昵称有值。
if (StrUtil.isBlank(userRegisterParam.getNickName())) { if (StrUtil.isBlank(userRegisterParam.getNickName())) {
userRegisterParam.setNickName(userRegisterParam.getUserName()); userRegisterParam.setNickName(userRegisterParam.getUserName());
} }
// 正在进行申请注册
// 通过用户服务层的方法,使用 MyBatis Plus 的 LambdaQueryWrapper 构建查询条件,统计数据库中昵称与传入的昵称相同的用户数量,
// 如果数量大于 0则表示该用户名昵称已被注册抛出异常提示用户无法重新注册。
if (userService.count(new LambdaQueryWrapper<User>().eq(User::getNickName, userRegisterParam.getNickName())) > 0) { if (userService.count(new LambdaQueryWrapper<User>().eq(User::getNickName, userRegisterParam.getNickName())) > 0) {
// 该用户名已注册,无法重新注册 // 该用户名已注册,无法重新注册
throw new YamiShopBindException("该用户名已注册,无法重新注册"); throw new YamiShopBindException("该用户名已注册,无法重新注册");
} }
// 创建当前时间对象,用于设置用户的修改时间、注册时间等属性,确保记录的时间信息准确。
Date now = new Date(); Date now = new Date();
// 创建一个新的用户对象,用于填充用户注册信息并保存到数据库中。
User user = new User(); User user = new User();
// 设置用户的修改时间为当前时间,表示该用户信息的最近一次修改时间。
user.setModifyTime(now); user.setModifyTime(now);
// 设置用户的注册时间为当前时间,记录用户注册的具体时间点。
user.setUserRegtime(now); user.setUserRegtime(now);
// 设置用户状态为有效(这里假设状态值为 1 表示有效,可根据实际业务定义调整)。
user.setStatus(1); user.setStatus(1);
// 设置用户的昵称,使用经过前面逻辑处理后的昵称值(可能是原昵称或者用户名)。
user.setNickName(userRegisterParam.getNickName()); user.setNickName(userRegisterParam.getNickName());
// 设置用户的邮箱信息,从注册参数中获取并赋值给用户对象。
user.setUserMail(userRegisterParam.getUserMail()); user.setUserMail(userRegisterParam.getUserMail());
// 通过密码管理类的方法,对用户注册参数中传入的密码进行解密操作(可能是之前加密传输过来的情况),获取解密后的密码字符串。
String decryptPassword = passwordManager.decryptPassword(userRegisterParam.getPassWord()); String decryptPassword = passwordManager.decryptPassword(userRegisterParam.getPassWord());
// 使用密码编码器对解密后的密码进行加密处理,将加密后的密码设置到用户对象中,保证密码在数据库中以加密形式存储,提高安全性。
user.setLoginPassword(passwordEncoder.encode(decryptPassword)); user.setLoginPassword(passwordEncoder.encode(decryptPassword));
// 使用 Hutool 工具类生成一个简单的唯一用户 ID这里采用 UUID 的简化形式,可根据实际需求调整生成方式),作为用户的标识。
String userId = IdUtil.simpleUUID(); String userId = IdUtil.simpleUUID();
// 将生成的用户 ID 设置到用户对象中,确保每个用户在系统中有唯一的标识。
user.setUserId(userId); user.setUserId(userId);
// 调用用户服务层的保存方法,将填充好信息的用户对象保存到数据库中,完成用户注册操作。
userService.save(user); userService.save(user);
// 2. 登录
// 模拟用户登录操作,创建一个用于存储在令牌中的用户信息对象,填充用户 ID、用户类型这里设置为普通用户通过枚举值获取、是否为管理员这里设置为否以及是否启用等信息。
UserInfoInTokenBO userInfoInTokenBO = new UserInfoInTokenBO(); UserInfoInTokenBO userInfoInTokenBO = new UserInfoInTokenBO();
userInfoInTokenBO.setUserId(user.getUserId()); userInfoInTokenBO.setUserId(user.getUserId());
userInfoInTokenBO.setSysType(SysTypeEnum.ORDINARY.value()); userInfoInTokenBO.setSysType(SysTypeEnum.ORDINARY.value());
userInfoInTokenBO.setIsAdmin(0); userInfoInTokenBO.setIsAdmin(0);
userInfoInTokenBO.setEnabled(true); userInfoInTokenBO.setEnabled(true);
// 通过令牌存储类的方法,将用户信息存储并生成对应的令牌信息对象,然后返回包含令牌信息的响应结果给前端,方便后续用户进行身份认证和授权访问。
return ServerResponseEntity.success(tokenStore.storeAndGetVo(userInfoInTokenBO)); return ServerResponseEntity.success(tokenStore.storeAndGetVo(userInfoInTokenBO));
} }
/**
*
*
*
*
* @param userPwdUpdateParam @Valid
* @return ServerResponseEntity
*/
@PutMapping("/updatePwd") @PutMapping("/updatePwd")
@Operation(summary = "修改密码" , description = "修改密码") @Operation(summary = "修改密码", description = "修改密码")
public ServerResponseEntity<Void> updatePwd(@Valid @RequestBody UserRegisterParam userPwdUpdateParam) { public ServerResponseEntity<Void> updatePwd(@Valid @RequestBody UserRegisterParam userPwdUpdateParam) {
// 通过用户服务层的方法,使用 MyBatis Plus 的 LambdaQueryWrapper 构建查询条件,根据用户名(昵称)从数据库中查询对应的用户信息,若查询不到则抛出异常提示无法获取用户信息。
User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getNickName, userPwdUpdateParam.getNickName())); User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getNickName, userPwdUpdateParam.getNickName()));
if (user == null) { if (user == null) {
// 无法获取用户信息 // 无法获取用户信息
throw new YamiShopBindException("无法获取用户信息"); throw new YamiShopBindException("无法获取用户信息");
} }
// 通过密码管理类的方法,对用户修改密码参数中传入的新密码进行解密操作(可能是之前加密传输过来的情况),获取解密后的密码字符串。
String decryptPassword = passwordManager.decryptPassword(userPwdUpdateParam.getPassWord()); String decryptPassword = passwordManager.decryptPassword(userPwdUpdateParam.getPassWord());
// 如果解密后的新密码为空字符串,则抛出异常提示新密码不能为空。
if (StrUtil.isBlank(decryptPassword)) { if (StrUtil.isBlank(decryptPassword)) {
// 新密码不能为空 // 新密码不能为空
throw new YamiShopBindException("新密码不能为空"); throw new YamiShopBindException("新密码不能为空");
} }
// 使用密码编码器对解密后的新密码进行加密处理,得到加密后的密码字符串。
String password = passwordEncoder.encode(decryptPassword); String password = passwordEncoder.encode(decryptPassword);
// 如果加密后的新密码与用户原密码相同,则抛出异常提示新密码不能与原密码相同,保证密码修改的合理性。
if (StrUtil.equals(password, user.getLoginPassword())) { if (StrUtil.equals(password, user.getLoginPassword())) {
// 新密码不能与原密码相同 // 新密码不能与原密码相同
throw new YamiShopBindException("新密码不能与原密码相同"); throw new YamiShopBindException("新密码不能与原密码相同");
} }
// 设置用户的修改时间为当前时间,表示此次密码修改操作的时间。
user.setModifyTime(new Date()); user.setModifyTime(new Date());
// 将加密后的新密码设置到用户对象中,更新用户的密码信息到数据库中,完成密码修改操作。
user.setLoginPassword(password); user.setLoginPassword(password);
// 调用用户服务层的更新方法,将更新后的用户对象信息保存到数据库中,确保密码修改生效。
userService.updateById(user); userService.updateById(user);
return ServerResponseEntity.success(); return ServerResponseEntity.success();
} }
} }

@ -31,69 +31,105 @@ import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* * ConfirmOrderEvent
*
*
*
* @author LGH * @author LGH
*/ */
@Component("defaultConfirmOrderListener") @Component("defaultConfirmOrderListener")
@AllArgsConstructor @AllArgsConstructor
public class ConfirmOrderListener { public class ConfirmOrderListener {
// 通过Lombok生成的构造函数注入UserAddrService用于根据用户地址ID和用户ID获取用户地址信息
// 在后续计算运费等操作中会用到该地址信息。
private final UserAddrService userAddrService; private final UserAddrService userAddrService;
// 注入TransportManagerService用于根据购物车商品项和用户地址计算运费是订单金额计算的重要组成部分。
private final TransportManagerService transportManagerService; private final TransportManagerService transportManagerService;
// 注入ProductService用于根据商品ID获取商品详细信息以验证商品状态以及参与订单金额计算等操作。
private final ProductService productService; private final ProductService productService;
// 注入SkuService用于根据商品规格SkuID获取商品规格详细信息同样用于验证商品状态和相关金额计算等操作。
private final SkuService skuService; private final SkuService skuService;
/** /**
* * ConfirmOrderEvent
* @EventListener 使
* @Order
*
* 1. ShopCartOrderDtoOrderParam
* 2. IDIDID
* 3. IDIDUserAddrService
* 4.
* - ProductServiceSkuService
* - 1
* -
* - TransportManagerService
* -
*
* @param event
*/ */
@EventListener(ConfirmOrderEvent.class) @EventListener(ConfirmOrderEvent.class)
@Order(ConfirmOrderOrder.DEFAULT) @Order(ConfirmOrderOrder.DEFAULT)
public void defaultConfirmOrderEvent(ConfirmOrderEvent event) { public void defaultConfirmOrderEvent(ConfirmOrderEvent event) {
// 从确认订单事件对象中获取购物车订单信息,包含了订单整体的一些汇总信息以及后续需要更新的金额等相关属性。
ShopCartOrderDto shopCartOrderDto = event.getShopCartOrderDto(); ShopCartOrderDto shopCartOrderDto = event.getShopCartOrderDto();
// 从确认订单事件对象中获取订单参数其中包含了如用户选择的收货地址ID等关键信息用于后续查询地址等操作。
OrderParam orderParam = event.getOrderParam(); OrderParam orderParam = event.getOrderParam();
// 通过SecurityUtils工具类获取当前用户的ID用于后续根据用户ID来查询与之相关的用户地址等信息。
String userId = SecurityUtils.getUser().getUserId(); String userId = SecurityUtils.getUser().getUserId();
// 订单的地址信息 // 根据订单参数中的地址ID和当前用户ID通过UserAddrService获取用户地址信息该地址信息将用于计算运费等操作若不存在地址则返回null。
UserAddr userAddr = userAddrService.getUserAddrByUserId(orderParam.getAddrId(), userId); UserAddr userAddr = userAddrService.getUserAddrByUserId(orderParam.getAddrId(), userId);
// 初始化订单总金额为0.0,后续会累加每个商品项的金额得到最终的订单总金额。
double total = 0.0; double total = 0.0;
// 初始化商品总数量为0在遍历购物车商品项时会累加每个商品项的数量得到总的商品数量。
int totalCount = 0; int totalCount = 0;
// 初始化运费为0.0,后续会根据每个商品项以及用户地址计算运费并累加得到总的运费。
double transfee = 0.0; double transfee = 0.0;
// 遍历购物车中的每个商品项,对每个商品项进行相关的信息获取、状态验证以及金额计算等操作。
for (ShopCartItemDto shopCartItem : event.getShopCartItems()) { for (ShopCartItemDto shopCartItem : event.getShopCartItems()) {
// 获取商品信息 // 通过ProductService根据商品ID获取商品详细信息,用于后续验证商品状态以及参与金额计算等操作。
Product product = productService.getProductByProdId(shopCartItem.getProdId()); Product product = productService.getProductByProdId(shopCartItem.getProdId());
// 获取sku信息 // 通过SkuService根据商品规格ID获取商品规格详细信息同样用于验证商品状态和参与金额计算等操作。
Sku sku = skuService.getSkuBySkuId(shopCartItem.getSkuId()); Sku sku = skuService.getSkuBySkuId(shopCartItem.getSkuId());
if (product == null || sku == null) { if (product == null || sku == null) {
throw new YamiShopBindException("购物车包含无法识别的商品"); throw new YamiShopBindException("购物车包含无法识别的商品");
} }
if (product.getStatus() != 1 || sku.getStatus() != 1) { if (product.getStatus()!= 1 || sku.getStatus()!= 1) {
throw new YamiShopBindException("商品[" + sku.getProdName() + "]已下架"); throw new YamiShopBindException("商品[" + sku.getProdName() + "]已下架");
} }
// 累加商品数量,将当前商品项的数量累加到总商品数量中。
totalCount = shopCartItem.getProdCount() + totalCount; totalCount = shopCartItem.getProdCount() + totalCount;
// 使用Arith工具类可能是自定义的用于精确计算的工具类将当前商品项的金额累加到总金额中得到订单的商品总金额。
total = Arith.add(shopCartItem.getProductTotalAmount(), total); total = Arith.add(shopCartItem.getProductTotalAmount(), total);
// 用户地址如果为空,则表示该用户从未设置过任何地址相关信息 // 用户地址如果不为空,则表示用户设置了收货地址,需要计算该商品项对应的运费并累加到总运费中。
if (userAddr != null) { if (userAddr!= null) {
// 每个产品的运费相加 // 调用TransportManagerService的方法计算当前商品项对应的运费传入商品项和用户地址信息作为参数
// 并将计算得到的运费累加到总运费中,实现运费的累加计算。
transfee = Arith.add(transfee, transportManagerService.calculateTransfee(shopCartItem, userAddr)); transfee = Arith.add(transfee, transportManagerService.calculateTransfee(shopCartItem, userAddr));
} }
// 设置商品项的实际总价(这里暂设置为商品项原本的金额,可能后续还有其他逻辑调整)。
shopCartItem.setActualTotal(shopCartItem.getProductTotalAmount()); shopCartItem.setActualTotal(shopCartItem.getProductTotalAmount());
// 更新购物车订单信息中的实际总价,将商品总金额和运费相加得到最终的实际总价。
shopCartOrderDto.setActualTotal(Arith.add(total, transfee)); shopCartOrderDto.setActualTotal(Arith.add(total, transfee));
// 更新购物车订单信息中的总金额,即商品总金额(不包含运费)。
shopCartOrderDto.setTotal(total); shopCartOrderDto.setTotal(total);
// 更新购物车订单信息中的商品总数量。
shopCartOrderDto.setTotalCount(totalCount); shopCartOrderDto.setTotalCount(totalCount);
// 更新购物车订单信息中的运费。
shopCartOrderDto.setTransfee(transfee); shopCartOrderDto.setTransfee(transfee);
} }
} }
} }

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

Loading…
Cancel
Save