diff --git a/wangjinhao_branch b/wangjinhao_branch new file mode 100644 index 0000000..66dc905 --- /dev/null +++ b/wangjinhao_branch @@ -0,0 +1 @@ +undefined \ No newline at end of file diff --git a/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/AdminLoginController.java b/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/AdminLoginController.java new file mode 100644 index 0000000..1326f2c --- /dev/null +++ b/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/AdminLoginController.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ +package com.yami.shop.admin.controller; // 定义类所在的包 + +import cn.hutool.core.util.StrUtil; // 引入Hutool工具类库中的StrUtil工具类,用于字符串操作 +import com.anji.captcha.model.common.ResponseModel; // 引入验证码响应模型 +import com.anji.captcha.model.vo.CaptchaVO; // 引入验证码模型 +import com.anji.captcha.service.CaptchaService; // 引入验证码服务 +import com.baomidou.mybatisplus.core.toolkit.Wrappers; // 引入MyBatis-Plus工具类Wrappers +import com.yami.shop.common.exception.YamiShopBindException; // 引入自定义异常类 +import com.yami.shop.common.response.ServerResponseEntity; // 引入服务器响应实体类 +import com.yami.shop.security.admin.dto.CaptchaAuthenticationDTO; // 引入验证码认证DTO +import com.yami.shop.security.common.bo.UserInfoInTokenBO; // 引入用户信息类 +import com.yami.shop.security.common.enums.SysTypeEnum; // 引入系统类型枚举 +import com.yami.shop.security.common.manager.PasswordCheckManager; // 引入密码校验管理类 +import com.yami.shop.security.common.manager.PasswordManager; // 引入密码管理类 +import com.yami.shop.security.common.manager.TokenStore; // 引入Token存储管理类 +import com.yami.shop.security.common.vo.TokenInfoVO; // 引入Token信息VO +import com.yami.shop.sys.constant.Constant; // 引入系统常量 +import com.yami.shop.sys.model.SysMenu; // 引入系统菜单模型 +import com.yami.shop.sys.model.SysUser; // 引入系统用户模型 +import com.yami.shop.sys.service.SysMenuService; // 引入系统菜单服务 +import com.yami.shop.sys.service.SysUserService; // 引入系统用户服务 +import io.swagger.v3.oas.annotations.Operation; // 引入Swagger的Operation注解 +import io.swagger.v3.oas.annotations.tags.Tag; // 引入Swagger的Tag注解 +import org.springframework.beans.factory.annotation.Autowired; // 引入Spring的@Autowired注解 +import org.springframework.web.bind.annotation.PostMapping; // 引入Spring的@PostMapping注解 +import org.springframework.web.bind.annotation.RequestBody; // 引入Spring的@RequestBody注解 +import org.springframework.web.bind.annotation.RestController; // 引入Spring的@RestController注解 + +import jakarta.validation.Valid; // 引入Jakarta Validation的Valid注解 +import java.util.Arrays; // 引入Java的Arrays工具类 +import java.util.List; // 引入Java的List接口 +import java.util.Objects; // 引入Java的Objects工具类 +import java.util.Set; // 引入Java的Set接口 +import java.util.stream.Collectors; // 引入Java的Collectors工具类 + +/** + * AdminLoginController类,用于处理管理员登录逻辑。 + * 该类包含一个登录方法和一个获取用户权限的方法。 + * 通过账号/手机号/用户名密码登录,进行验证码校验和密码校验,生成Token。 + * @作者 FrozenWatermelon + * @日期 2020/6/30 + */ +@RestController // 标注这是一个控制器类,并且其返回结果直接写入HTTP响应体中,而不是视图名称 +@Tag(name = "登录") // 给API文档添加标签,描述这个控制器的功能 +public class AdminLoginController { + + @Autowired + private TokenStore tokenStore; // 自动注入Token存储管理类 + + @Autowired + private SysUserService sysUserService; // 自动注入系统用户服务 + + @Autowired + private SysMenuService sysMenuService; // 自动注入系统菜单服务 + + @Autowired + private PasswordCheckManager passwordCheckManager; // 自动注入密码校验管理类 + + @Autowired + private CaptchaService captchaService; // 自动注入验证码服务 + + @Autowired + private PasswordManager passwordManager; // 自动注入密码管理类 + + /** + * 账号密码 + 验证码登录(用于后台登录) + * 通过账号/手机号/用户名密码登录,并进行验证码校验和密码校验。 + * @param captchaAuthenticationDTO 包含账号、密码和验证码的DTO对象 + * @return 服务器响应实体,包含登录成功后的Token信息 + */ + @PostMapping("/adminLogin") + @Operation(summary = "账号密码 + 验证码登录(用于后台登录)" , description = "通过账号/手机号/用户名密码登录") + public ServerResponseEntity login( + @Valid @RequestBody CaptchaAuthenticationDTO captchaAuthenticationDTO) { + // 登陆后台登录需要再校验一遍验证码 + CaptchaVO captchaVO = new CaptchaVO(); + captchaVO.setCaptchaVerification(captchaAuthenticationDTO.getCaptchaVerification()); + ResponseModel response = captchaService.verification(captchaVO); + if (!response.isSuccess()) { + return ServerResponseEntity.showFailMsg("验证码有误或已过期"); + } + + SysUser sysUser = sysUserService.getByUserName(captchaAuthenticationDTO.getUserName()); + if (sysUser == null) { + throw new YamiShopBindException("账号或密码不正确"); + } + + // 半小时内密码输入错误十次,已限制登录30分钟 + String decryptPassword = passwordManager.decryptPassword(captchaAuthenticationDTO.getPassWord()); + passwordCheckManager.checkPassword(SysTypeEnum.ADMIN, captchaAuthenticationDTO.getUserName(), decryptPassword, sysUser.getPassword()); + + // 不是店铺超级管理员,并且是禁用状态,无法登录 + if (Objects.equals(sysUser.getStatus(), 0)) { + // 未找到此用户信息 + throw new YamiShopBindException("未找到此用户信息"); + } + + UserInfoInTokenBO userInfoInToken = new UserInfoInTokenBO(); + userInfoInToken.setUserId(String.valueOf(sysUser.getUserId())); + userInfoInToken.setSysType(SysTypeEnum.ADMIN.value()); + userInfoInToken.setEnabled(sysUser.getStatus() == 1); + userInfoInToken.setPerms(getUserPermissions(sysUser.getUserId())); + userInfoInToken.setNickName(sysUser.getUsername()); + userInfoInToken.setShopId(sysUser.getShopId()); + // 存储token返回vo + TokenInfoVO tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken); + return ServerResponseEntity.success(tokenInfoVO); + } + + /** + * 获取用户权限 + * 根据用户ID获取用户的所有权限,如果是超级管理员,拥有最高权限。 + * @param userId 用户ID + * @return 用户权限的Set集合 + */ + private Set getUserPermissions(Long userId) { + List permsList; + + //系统管理员,拥有最高权限 + if(userId == Constant.SUPER_ADMIN_ID){ + List menuList = sysMenuService.list(Wrappers.emptyWrapper()); + permsList = menuList.stream().map(SysMenu::getPerms).collect(Collectors.toList()); + }else{ + permsList = sysUserService.queryAllPerms(userId); + } + return permsList.stream().flatMap((perms) -> { + if (StrUtil.isBlank(perms)) { + return null; + } + return Arrays.stream(perms.trim().split(StrUtil.COMMA)); + } + ).collect(Collectors.toSet()); + } +} diff --git a/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/AreaController.java b/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/AreaController.java new file mode 100644 index 0000000..1368175 --- /dev/null +++ b/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/AreaController.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.admin.controller; // 定义类所在的包 + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 引入MyBatis-Plus的条件查询包装器 +import com.baomidou.mybatisplus.core.metadata.IPage; // 引入MyBatis-Plus的分页接口 +import com.yami.shop.bean.enums.AreaLevelEnum; // 引入区域级别枚举 +import com.yami.shop.bean.model.Area; // 引入区域模型 +import com.yami.shop.common.exception.YamiShopBindException; // 引入自定义异常类 +import com.yami.shop.common.util.PageParam; // 引入分页参数工具类 +import com.yami.shop.service.AreaService; // 引入区域服务类 +import org.springframework.beans.factory.annotation.Autowired; // 引入Spring的@Autowired注解 +import com.yami.shop.common.response.ServerResponseEntity; // 引入服务器响应实体类 +import org.springframework.security.access.prepost.PreAuthorize; // 引入Spring Security的PreAuthorize注解 +import org.springframework.web.bind.annotation.*; + +import jakarta.validation.Valid; // 引入Jakarta Validation的Valid注解 +import java.util.List; // 引入Java的List接口 +import java.util.Objects; // 引入Java的Objects工具类 + +/** + * AreaController类,用于管理区域信息。 + * 该类包含了分页获取区域、获取省市、通过父级ID获取区域列表、获取区域信息、保存区域、修改区域和删除区域的方法。 + * @作者 lgh on 2018/10/26. + */ +@RestController // 标注这是一个控制器类,并且其返回结果直接写入HTTP响应体中,而不是视图名称 +@RequestMapping("/admin/area") // 定义请求路径的根地址为/admin/area +public class AreaController { + + @Autowired + private AreaService areaService; // 自动注入区域服务类 + + /** + * 分页获取区域信息 + * @param area 查询条件 + * @param page 分页参数 + * @return 服务器响应实体,包含分页后的区域信息 + */ + @GetMapping("/page") + @PreAuthorize("@pms.hasPermission('admin:area:page')") // 权限检查 + public ServerResponseEntity> page(Area area, PageParam page) { + IPage areaPage = areaService.page(page, new LambdaQueryWrapper()); + return ServerResponseEntity.success(areaPage); + } + + /** + * 获取省市信息 + * @param area 查询条件 + * @return 服务器响应实体,包含查询到的省市信息 + */ + @GetMapping("/list") + @PreAuthorize("@pms.hasPermission('admin:area:list')") // 权限检查 + public ServerResponseEntity> list(Area area) { + List areas = areaService.list(new LambdaQueryWrapper() + .like(area.getAreaName() != null, Area::getAreaName, area.getAreaName())); + return ServerResponseEntity.success(areas); + } + + /** + * 通过父级ID获取区域列表 + * @param pid 父级ID + * @return 服务器响应实体,包含查询到的区域列表 + */ + @GetMapping("/listByPid") + public ServerResponseEntity> listByPid(Long pid) { + List list = areaService.listByPid(pid); + return ServerResponseEntity.success(list); + } + + /** + * 获取区域信息 + * @param id 区域ID + * @return 服务器响应实体,包含查询到的区域信息 + */ + @GetMapping("/info/{id}") + @PreAuthorize("@pms.hasPermission('admin:area:info')") // 权限检查 + public ServerResponseEntity info(@PathVariable("id") Long id) { + Area area = areaService.getById(id); + return ServerResponseEntity.success(area); + } + + /** + * 保存区域信息 + * @param area 区域信息 + * @return 服务器响应实体 + */ + @PostMapping + @PreAuthorize("@pms.hasPermission('admin:area:save')") // 权限检查 + public ServerResponseEntity save(@Valid @RequestBody Area area) { + if (area.getParentId() != null) { + Area parentArea = areaService.getById(area.getParentId()); + area.setLevel(parentArea.getLevel() + 1); + areaService.removeAreaCacheByParentId(area.getParentId()); + } + areaService.save(area); + return ServerResponseEntity.success(); + } + + /** + * 修改区域信息 + * @param area 区域信息 + * @return 服务器响应实体 + */ + @PutMapping + @PreAuthorize("@pms.hasPermission('admin:area:update')") // 权限检查 + public ServerResponseEntity update(@Valid @RequestBody Area area) { + Area areaDb = areaService.getById(area.getAreaId()); + // 判断当前省市区级别,如果是1级、2级则不能修改级别,不能修改成别人的下级 + if(Objects.equals(areaDb.getLevel(), AreaLevelEnum.FIRST_LEVEL.value()) && !Objects.equals(area.getLevel(), AreaLevelEnum.FIRST_LEVEL.value())){ + throw new YamiShopBindException("不能改变一级行政地区的级别"); + } + if(Objects.equals(areaDb.getLevel(), AreaLevelEnum.SECOND_LEVEL.value()) && !Objects.equals(area.getLevel(), AreaLevelEnum.SECOND_LEVEL.value())){ + throw new YamiShopBindException("不能改变二级行政地区的级别"); + } + hasSameName(area); + areaService.updateById(area); + areaService.removeAreaCacheByParentId(area.getParentId()); + return ServerResponseEntity.success(); + } + + /** + * 删除区域信息 + * @param id 区域ID + * @return 服务器响应实体 + */ + @DeleteMapping("/{id}") + @PreAuthorize("@pms.hasPermission('admin:area:delete')") // 权限检查 + public ServerResponseEntity delete(@PathVariable Long id) { + Area area = areaService.getById(id); + areaService.removeById(id); + areaService.removeAreaCacheByParentId(area.getParentId()); + return ServerResponseEntity.success(); + } + + /** + * 判断是否有相同名称的区域 + * @param area 区域信息 + */ + private void hasSameName(Area area) { + long count = areaService.count(new LambdaQueryWrapper() + .eq(Area::getParentId, area.getParentId()) + .eq(Area::getAreaName, area.getAreaName()) + .ne(Objects.nonNull(area.getAreaId()) && !Objects.equals(area.getAreaId(), 0L), Area::getAreaId, area.getAreaId()) + ); + if (count > 0) { + throw new YamiShopBindException("该地区已存在"); + } + } +} \ No newline at end of file diff --git a/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/CategoryController.java b/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/CategoryController.java new file mode 100644 index 0000000..f42c71e --- /dev/null +++ b/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/CategoryController.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.admin.controller; // 定义类所在的包 + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 引入MyBatis-Plus的条件查询包装器 +import com.yami.shop.bean.model.Category; // 引入商品分类模型类 +import com.yami.shop.common.annotation.SysLog; // 引入自定义日志注解 +import com.yami.shop.common.exception.YamiShopBindException; // 引入自定义异常类 +import com.yami.shop.security.admin.util.SecurityUtils; // 引入安全工具类 +import com.yami.shop.service.CategoryService; // 引入商品分类服务类 +import org.springframework.beans.factory.annotation.Autowired; // 引入Spring的@Autowired注解 +import com.yami.shop.common.response.ServerResponseEntity; // 引入服务器响应实体类 +import org.springframework.security.access.prepost.PreAuthorize; // 引入Spring Security的PreAuthorize注解 +import org.springframework.web.bind.annotation.*; + +import java.util.Date; // 引入Java的Date类 +import java.util.List; // 引入Java的List接口 +import java.util.Objects; // 引入Java的Objects工具类 + +/** + * 商品分类管理控制器 + * 该类包含分页获取、获取详细信息、保存、修改和删除商品分类的方法。 + * @作者 lgh + */ +@RestController // 标注这是一个控制器类,并且其返回结果直接写入HTTP响应体中,而不是视图名称 +@RequestMapping("/prod/category") // 定义请求路径的根地址为/prod/category +public class CategoryController { + + @Autowired + private CategoryService categoryService; // 自动注入商品分类服务类 + + /** + * 获取菜单页面的表 + * @return 服务器响应实体,包含商品分类列表 + */ + @GetMapping("/table") + @PreAuthorize("@pms.hasPermission('prod:category:page')") // 权限检查 + public ServerResponseEntity> table() { + List categoryMenuList = categoryService.tableCategory(SecurityUtils.getSysUser().getShopId()); + return ServerResponseEntity.success(categoryMenuList); + } + + /** + * 获取分类信息 + * @param categoryId 商品分类ID + * @return 服务器响应实体,包含商品分类的详细信息 + */ + @GetMapping("/info/{categoryId}") + public ServerResponseEntity info(@PathVariable("categoryId") Long categoryId) { + Category category = categoryService.getById(categoryId); + return ServerResponseEntity.success(category); + } + + /** + * 保存分类 + * @param category 商品分类信息 + * @return 服务器响应实体 + */ + @SysLog("保存分类") // 自定义日志注解 + @PostMapping + @PreAuthorize("@pms.hasPermission('prod:category:save')") // 权限检查 + public ServerResponseEntity save(@RequestBody Category category) { + category.setShopId(SecurityUtils.getSysUser().getShopId()); + category.setRecTime(new Date()); + Category categoryName = categoryService.getOne(new LambdaQueryWrapper().eq(Category::getCategoryName, category.getCategoryName()) + .eq(Category::getShopId, category.getShopId())); + if (Objects.nonNull(categoryName)) { + throw new YamiShopBindException("类目名称已存在!"); + } + categoryService.saveCategory(category); + return ServerResponseEntity.success(); + } + + /** + * 更新分类 + * @param category 商品分类信息 + * @return 服务器响应实体 + */ + @SysLog("更新分类") // 自定义日志注解 + @PutMapping + @PreAuthorize("@pms.hasPermission('prod:category:update')") // 权限检查 + public ServerResponseEntity update(@RequestBody Category category) { + category.setShopId(SecurityUtils.getSysUser().getShopId()); + if (Objects.equals(category.getParentId(), category.getCategoryId())) { + return ServerResponseEntity.showFailMsg("分类的上级不能是自己本身"); + } + Category categoryName = categoryService.getOne(new LambdaQueryWrapper().eq(Category::getCategoryName, category.getCategoryName()) + .eq(Category::getShopId, category.getShopId()).ne(Category::getCategoryId, category.getCategoryId())); + if (categoryName != null) { + throw new YamiShopBindException("类目名称已存在!"); + } + Category categoryDb = categoryService.getById(category.getCategoryId()); + // 如果从下线改成正常,则需要判断上级的状态 + if (Objects.equals(categoryDb.getStatus(), 0) && Objects.equals(category.getStatus(), 1) && !Objects.equals(category.getParentId(), 0L)) { + Category parentCategory = categoryService.getOne(new LambdaQueryWrapper().eq(Category::getCategoryId, category.getParentId())); + if (Objects.isNull(parentCategory) || Objects.equals(parentCategory.getStatus(), 0)) { + // 修改失败,上级分类不存在或者不为正常状态 + throw new YamiShopBindException("修改失败,上级分类不存在或者不为正常状态"); + } + } + categoryService.updateCategory(category); + return ServerResponseEntity.success(); + } + + /** + * 删除分类 + * @param categoryId 商品分类ID + * @return 服务器响应实体 + */ + @SysLog("删除分类") // 自定义日志注解 + @DeleteMapping("/{categoryId}") + @PreAuthorize("@pms.hasPermission('prod:category:delete')") // 权限检查 + public ServerResponseEntity delete(@PathVariable("categoryId") Long categoryId) { + if (categoryService.count(new LambdaQueryWrapper().eq(Category::getParentId, categoryId)) > 0) { + return ServerResponseEntity.showFailMsg("请删除子分类,再删除该分类"); + } + categoryService.deleteCategory(categoryId); + return ServerResponseEntity.success(); + } + + /** + * 获取所有的分类 + * @return 服务器响应实体,包含商品分类列表 + */ + @GetMapping("/listCategory") + public ServerResponseEntity> listCategory() { + return ServerResponseEntity.success(categoryService.list(new LambdaQueryWrapper() + .le(Category::getGrade, 2) + .eq(Category::getShopId, SecurityUtils.getSysUser().getShopId()) + .orderByAsc(Category::getSeq))); + } + + /** + * 获取所有的产品分类 + * @return 服务器响应实体,包含产品分类列表 + */ + @GetMapping("/listProdCategory") + public ServerResponseEntity> listProdCategory() { + List categories = categoryService.treeSelect(SecurityUtils.getSysUser().getShopId(), 2); + return ServerResponseEntity.success(categories); + } +} diff --git a/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/DeliveryController.java b/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/DeliveryController.java new file mode 100644 index 0000000..86c6469 --- /dev/null +++ b/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/DeliveryController.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.admin.controller; // 定义类所在的包 + +import java.util.List; // 引入Java的List接口 + +import org.springframework.beans.factory.annotation.Autowired; // 引入Spring的@Autowired注解 +import com.yami.shop.common.response.ServerResponseEntity; // 引入服务器响应实体类 +import org.springframework.web.bind.annotation.GetMapping; // 引入Spring的@GetMapping注解 +import org.springframework.web.bind.annotation.RequestMapping; // 引入Spring的@RequestMapping注解 +import org.springframework.web.bind.annotation.RestController; // 引入Spring的@RestController注解 + +import com.yami.shop.bean.model.Delivery; // 引入配送模型 +import com.yami.shop.service.DeliveryService; // 引入配送服务类 + +/** + * DeliveryController类,用于管理配送信息。 + * 该类包含一个分页获取配送信息的方法。 + * @作者 lgh on 2018/11/26. + */ +@RestController // 标注这是一个控制器类,并且其返回结果直接写入HTTP响应体中,而不是视图名称 +@RequestMapping("/admin/delivery") // 定义请求路径的根地址为/admin/delivery +public class DeliveryController { + + @Autowired + private DeliveryService deliveryService; // 自动注入配送服务类 + + /** + * 分页获取配送信息 + * @return 服务器响应实体,包含配送信息列表 + */ + @GetMapping("/list") + public ServerResponseEntity> page() { + List list = deliveryService.list(); // 获取所有配送信息 + return ServerResponseEntity.success(list); // 返回配送信息列表 + } +} diff --git a/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/OrderController.java b/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/OrderController.java new file mode 100644 index 0000000..caef8f1 --- /dev/null +++ b/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/OrderController.java @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.admin.controller; // 定义类所在的包 + +import cn.hutool.core.date.DatePattern; // 引入Hutool工具类库中的日期格式化工具类 +import cn.hutool.core.date.DateUtil; // 引入Hutool工具类库中的日期工具类 +import cn.hutool.core.io.IORuntimeException; // 引入Hutool工具类库中的IO运行时异常类 +import cn.hutool.core.io.IoUtil; // 引入Hutool工具类库中的IO工具类 +import cn.hutool.poi.excel.ExcelUtil; // 引入Hutool工具类库中的Excel工具类 +import cn.hutool.poi.excel.ExcelWriter; // 引入Hutool工具类库中的Excel写入工具类 +import com.baomidou.mybatisplus.core.metadata.IPage; // 引入MyBatis-Plus的分页接口 +import com.google.common.base.Objects; // 引入Google Guava库中的Objects工具类 +import com.yami.shop.bean.enums.OrderStatus; // 引入订单状态枚举类 +import com.yami.shop.bean.model.Order; // 引入订单模型类 +import com.yami.shop.bean.model.OrderItem; // 引入订单项模型类 +import com.yami.shop.bean.model.UserAddrOrder; // 引入用户地址订单模型类 +import com.yami.shop.bean.param.DeliveryOrderParam; // 引入发货订单参数类 +import com.yami.shop.bean.param.OrderParam; // 引入订单参数类 +import com.yami.shop.common.exception.YamiShopBindException; // 引入自定义异常类 +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.*; // 引入各种服务类 +import jakarta.servlet.ServletOutputStream; // 引入Servlet输出流类 +import jakarta.servlet.http.HttpServletResponse; // 引入HTTP响应类 +import lombok.extern.slf4j.Slf4j; // 引入Lombok的日志记录注解 +import org.apache.poi.ss.usermodel.Sheet; // 引入Apache POI的Sheet类 +import org.springframework.beans.factory.annotation.Autowired; // 引入Spring的@Autowired注解 +import org.springframework.format.annotation.DateTimeFormat; // 引入Spring的日期时间格式化注解 +import org.springframework.security.access.prepost.PreAuthorize; // 引入Spring Security的PreAuthorize注解 +import org.springframework.web.bind.annotation.*; // 引入Spring Web的注解 + +import java.io.IOException; // 引入Java的IO异常类 +import java.util.Arrays; // 引入Java的Arrays工具类 +import java.util.Date; // 引入Java的Date类 +import java.util.List; // 引入Java的List接口 + +/** + * OrderController类,用于管理订单操作,包括分页获取、查看详情、发货、导出订单信息等功能。 + * @作者 lgh on 2018/09/15. + */ +@Slf4j // 标注这是一个需要日志记录的类 +@RestController // 标注这是一个控制器类,并且其返回结果直接写入HTTP响应体中,而不是视图名称 +@RequestMapping("/order/order") // 定义请求路径的根地址为/order/order +public class OrderController { + + @Autowired + private OrderService orderService; // 自动注入订单服务类 + + @Autowired + private OrderItemService orderItemService; // 自动注入订单项服务类 + + @Autowired + private UserAddrOrderService userAddrOrderService; // 自动注入用户地址订单服务类 + + @Autowired + private ProductService productService; // 自动注入商品服务类 + + @Autowired + private SkuService skuService; // 自动注入SKU服务类 + + /** + * 分页获取订单信息 + * @param orderParam 订单查询参数 + * @param page 分页参数 + * @return 服务器响应实体,包含分页后的订单信息 + */ + @GetMapping("/page") + @PreAuthorize("@pms.hasPermission('order:order:page')") // 权限检查 + public ServerResponseEntity> page(OrderParam orderParam, PageParam page) { + Long shopId = SecurityUtils.getSysUser().getShopId(); + orderParam.setShopId(shopId); + IPage orderPage = orderService.pageOrdersDetailByOrderParam(page, orderParam); + return ServerResponseEntity.success(orderPage); // 返回分页结果 + } + + /** + * 获取订单详情信息 + * @param orderNumber 订单编号 + * @return 服务器响应实体,包含订单的详细信息 + */ + @GetMapping("/orderInfo/{orderNumber}") + @PreAuthorize("@pms.hasPermission('order:order:info')") // 权限检查 + public ServerResponseEntity info(@PathVariable("orderNumber") String orderNumber) { + Long shopId = SecurityUtils.getSysUser().getShopId(); + Order order = orderService.getOrderByOrderNumber(orderNumber); + if (!Objects.equal(shopId, order.getShopId())) { + throw new YamiShopBindException("您没有权限获取该订单信息"); + } + List orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber); + order.setOrderItems(orderItems); + UserAddrOrder userAddrOrder = userAddrOrderService.getById(order.getAddrOrderId()); + order.setUserAddrOrder(userAddrOrder); + return ServerResponseEntity.success(order); + } + + /** + * 发货 + * @param deliveryOrderParam 发货订单参数 + * @return 服务器响应实体 + */ + @PutMapping("/delivery") + @PreAuthorize("@pms.hasPermission('order:order:delivery')") // 权限检查 + public ServerResponseEntity delivery(@RequestBody DeliveryOrderParam deliveryOrderParam) { + Long shopId = SecurityUtils.getSysUser().getShopId(); + Order order = orderService.getOrderByOrderNumber(deliveryOrderParam.getOrderNumber()); + if (!Objects.equal(shopId, order.getShopId())) { + throw new YamiShopBindException("您没有权限修改该订单信息"); + } + + Order orderParam = new Order(); + orderParam.setOrderId(order.getOrderId()); + orderParam.setDvyId(deliveryOrderParam.getDvyId()); + orderParam.setDvyFlowId(deliveryOrderParam.getDvyFlowId()); + orderParam.setDvyTime(new Date()); + orderParam.setStatus(OrderStatus.CONSIGNMENT.value()); + orderParam.setUserId(order.getUserId()); + + orderService.delivery(orderParam); + + List orderItems = orderItemService.getOrderItemsByOrderNumber(deliveryOrderParam.getOrderNumber()); + for (OrderItem orderItem : orderItems) { + productService.removeProductCacheByProdId(orderItem.getProdId()); + skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId()); + } + return ServerResponseEntity.success(); + } + + /** + * 打印待发货的订单表 + * @param order 订单对象 + * @param consignmentName 发件人姓名 + * @param consignmentMobile 发货人手机号 + * @param consignmentAddr 发货地址 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param response HTTP响应对象 + */ + * @param order 订单对象 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param response HTTP响应对象 + */ + @GetMapping("/waitingConsignmentExcel") + @PreAuthorize("@pms.hasPermission('order:order:waitingConsignmentExcel')") // 权限检查 + 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, + String consignmentAddr, HttpServletResponse response) { + Long shopId = SecurityUtils.getSysUser().getShopId(); + order.setShopId(shopId); + order.setStatus(OrderStatus.PADYED.value()); + List orders = orderService.listOrdersDetailByOrder(order, startTime, endTime); + + // 通过工具类创建ExcelWriter + ExcelWriter writer = ExcelUtil.getBigWriter(); + Sheet sheet = writer.getSheet(); + sheet.setColumnWidth(0, 20 * 256); + sheet.setColumnWidth(1, 20 * 256); + sheet.setColumnWidth(2, 20 * 256); + sheet.setColumnWidth(3, 60 * 256); + sheet.setColumnWidth(4, 60 * 256); + sheet.setColumnWidth(7, 60 * 256); + sheet.setColumnWidth(8, 60 * 256); + sheet.setColumnWidth(9, 60 * 256); + // 待发货 + String[] hearder = {"订单编号", "收件人", "手机", "收货地址", "商品名称", "数量", "发件人姓名", "发件人手机号", "发货地址", "备注"}; + writer.merge(hearder.length - 1, "发货信息整理"); + writer.writeRow(Arrays.asList(hearder)); + + int row = 1; + for (Order dbOrder : orders) { + UserAddrOrder addr = dbOrder.getUserAddrOrder(); + String addrInfo = addr.getProvince() + addr.getCity() + addr.getArea() + addr.getAddr(); + List orderItems = dbOrder.getOrderItems(); + row++; + for (OrderItem orderItem : orderItems) { + // 第0列开始 + int col = 0; + writer.writeCellValue(col++, row, dbOrder.getOrderNumber()); + writer.writeCellValue(col++, row, addr.getReceiver()); + writer.writeCellValue(col++, row, addr.getMobile()); + writer.writeCellValue(col++, row, addrInfo); + writer.writeCellValue(col++, row, orderItem.getProdName()); + writer.writeCellValue(col++, row, orderItem.getProdCount()); + writer.writeCellValue(col++, row, consignmentName); + writer.writeCellValue(col++, row, consignmentMobile); + writer.writeCellValue(col++, row, consignmentAddr); + writer.writeCellValue(col++, row, dbOrder.getRemarks()); + } + } + writeExcel(response, writer); + } + + /** + * 已销售订单 + * @param order 订单对象 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param response HTTP响应对象 + */ + + @GetMapping("/soldExcel") + @PreAuthorize("@pms.hasPermission('order:order:soldExcel')") // 权限检查 + 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) { + Long shopId = SecurityUtils.getSysUser().getShopId(); + order.setShopId(shopId); + order.setIsPayed(1); + List orders = orderService.listOrdersDetailByOrder(order, startTime, endTime); + + // 通过工具类创建ExcelWriter + ExcelWriter writer = ExcelUtil.getBigWriter(); + String[] hearder = {"订单编号", "下单时间", "收件人", "手机", "收货地址", "商品名称", "数量", "订单应付", "订单运费", "订单实付"}; + Sheet sheet = writer.getSheet(); + sheet.setColumnWidth(0, 20 * 256); + sheet.setColumnWidth(1, 20 * 256); + sheet.setColumnWidth(3, 20 * 256); + sheet.setColumnWidth(4, 60 * 256); + sheet.setColumnWidth(5, 60 * 256); + + writer.merge(hearder.length - 1, "销售信息整理"); + writer.writeRow(Arrays.asList(hearder)); + + int row = 1; + for (Order dbOrder : orders) { + UserAddrOrder addr = dbOrder.getUserAddrOrder(); + String addrInfo = addr.getProvince() + addr.getCity() + addr.getArea() + addr.getAddr(); + List 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); + } + + /** + * 如果需要合并的话,就合并 + * @param writer ExcelWriter对象 + * @param firstRow 起始行 + * @param lastRow 结束行 + * @param firstColumn 起始列 + * @param lastColumn 结束列 + * @param content 合并内容 + */ + 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); + } + } + + /** + * 写出Excel文件 + * @param response HTTP响应对象 + * @param writer ExcelWriter对象 + */ + private void writeExcel(HttpServletResponse response, ExcelWriter writer) { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + 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); + } + } +} \ No newline at end of file diff --git a/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/PickAddrController.java b/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/PickAddrController.java new file mode 100644 index 0000000..d3e60de --- /dev/null +++ b/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/PickAddrController.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.admin.controller; // 定义类所在的包 + +import cn.hutool.core.util.StrUtil; // 引入Hutool工具类库中的StrUtil工具类 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 引入MyBatis-Plus的条件查询包装器 +import com.baomidou.mybatisplus.core.metadata.IPage; // 引入MyBatis-Plus的分页接口 +import com.yami.shop.bean.model.PickAddr; // 引入自提点地址模型类 +import com.yami.shop.common.exception.YamiShopBindException; // 引入自定义异常类 +import com.yami.shop.common.response.ResponseEnum; // 引入响应枚举类 +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; // 引入Spring的@Autowired注解 +import org.springframework.security.access.prepost.PreAuthorize; // 引入Spring Security的PreAuthorize注解 +import org.springframework.web.bind.annotation.*; // 引入Spring Web的注解 + +import jakarta.validation.Valid; // 引入Jakarta Validation的Valid注解 +import java.util.Arrays; // 引入Java的Arrays工具类 +import java.util.Objects; // 引入Java的Objects工具类 + +/** + * PickAddrController类,用于管理自提点地址。 + * 该类包含分页获取、获取详细信息、保存、修改和删除自提点地址的方法。 + * @作者 lgh on 2018/10/17. + */ +@RestController // 标注这是一个控制器类,并且其返回结果直接写入HTTP响应体中,而不是视图名称 +@RequestMapping("/shop/pickAddr") // 定义请求路径的根地址为/shop/pickAddr +public class PickAddrController { + + @Autowired + private PickAddrService pickAddrService; // 自动注入自提点地址服务类 + + /** + * 分页获取自提点地址 + * @param pickAddr 自提点地址查询条件 + * @param page 分页参数 + * @return 服务器响应实体,包含分页后的自提点地址信息 + */ + @GetMapping("/page") + @PreAuthorize("@pms.hasPermission('shop:pickAddr:page')") // 权限检查 + public ServerResponseEntity> page(PickAddr pickAddr, PageParam page) { + IPage pickAddrs = pickAddrService.page(page, new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(pickAddr.getAddrName()), PickAddr::getAddrName, pickAddr.getAddrName()) + .orderByDesc(PickAddr::getAddrId)); + return ServerResponseEntity.success(pickAddrs); // 返回分页结果 + } + + /** + * 获取自提点地址详细信息 + * @param id 自提点地址ID + * @return 服务器响应实体,包含自提点地址的详细信息 + */ + @GetMapping("/info/{id}") + @PreAuthorize("@pms.hasPermission('shop:pickAddr:info')") // 权限检查 + public ServerResponseEntity info(@PathVariable("id") Long id) { + PickAddr pickAddr = pickAddrService.getById(id); + return ServerResponseEntity.success(pickAddr); // 返回自提点地址信息 + } + + /** + * 保存自提点地址 + * @param pickAddr 自提点地址信息 + * @return 服务器响应实体 + */ + @PostMapping + @PreAuthorize("@pms.hasPermission('shop:pickAddr:save')") // 权限检查 + public ServerResponseEntity save(@Valid @RequestBody PickAddr pickAddr) { + pickAddr.setShopId(SecurityUtils.getSysUser().getShopId()); // 设置店铺ID + pickAddrService.save(pickAddr); // 保存自提点地址 + return ServerResponseEntity.success(); // 返回成功响应 + } + + /** + * 修改自提点地址 + * @param pickAddr 自提点地址信息 + * @return 服务器响应实体 + */ + @PutMapping + @PreAuthorize("@pms.hasPermission('shop:pickAddr:update')") // 权限检查 + public ServerResponseEntity update(@Valid @RequestBody PickAddr pickAddr) { + PickAddr dbPickAddr = pickAddrService.getById(pickAddr.getAddrId()); + + if (!Objects.equals(dbPickAddr.getShopId(), SecurityUtils.getSysUser().getShopId())) { + throw new YamiShopBindException(ResponseEnum.UNAUTHORIZED); + } + pickAddrService.updateById(pickAddr); // 修改自提点地址 + return ServerResponseEntity.success(); // 返回成功响应 + } + + /** + * 删除自提点地址 + * @param ids 自提点地址ID数组 + * @return 服务器响应实体 + */ + @DeleteMapping + @PreAuthorize("@pms.hasPermission('shop:pickAddr:delete')") // 权限检查 + public ServerResponseEntity delete(@RequestBody Long[] ids) { + pickAddrService.removeByIds(Arrays.asList(ids)); // 删除自提点地址 + return ServerResponseEntity.success(); // 返回成功响应 + } +} diff --git a/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/TransportController.java b/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/TransportController.java new file mode 100644 index 0000000..1fcfc5d --- /dev/null +++ b/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/TransportController.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.admin.controller; // 定义类所在的包 + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 引入MyBatis-Plus的条件查询包装器 +import com.baomidou.mybatisplus.core.metadata.IPage; // 引入MyBatis-Plus的分页接口 +import com.yami.shop.bean.model.Transport; // 引入运费模板模型类 +import com.yami.shop.common.util.PageParam; // 引入分页参数工具类 +import com.yami.shop.security.admin.util.SecurityUtils; // 引入安全工具类 +import com.yami.shop.service.TransportService; // 引入运费模板服务类 +import org.apache.commons.lang3.StringUtils; // 引入Apache Commons的StringUtils工具类 +import org.springframework.beans.factory.annotation.Autowired; // 引入Spring的@Autowired注解 +import com.yami.shop.common.response.ServerResponseEntity; // 引入服务器响应实体类 +import org.springframework.security.access.prepost.PreAuthorize; // 引入Spring Security的PreAuthorize注解 +import org.springframework.web.bind.annotation.*; + +import java.util.Date; // 引入Java的Date类 +import java.util.List; // 引入Java的List接口 + +/** + * 运费模板管理控制器 + * 该类包含分页获取、获取详细信息、保存、修改和删除运费模板的方法。 + * @作者 lgh on 2018/11/16. + */ +@RestController // 标注这是一个控制器类,并且其返回结果直接写入HTTP响应体中,而不是视图名称 +@RequestMapping("/shop/transport") // 定义请求路径的根地址为/shop/transport +public class TransportController { + + @Autowired + private TransportService transportService; // 自动注入运费模板服务类 + + /** + * 分页获取运费模板 + * @param transport 运费模板查询条件 + * @param page 分页参数 + * @return 服务器响应实体,包含分页后的运费模板信息 + */ + @GetMapping("/page") + @PreAuthorize("@pms.hasPermission('shop:transport:page')") // 权限检查 + public ServerResponseEntity> page(Transport transport, PageParam page) { + Long shopId = SecurityUtils.getSysUser().getShopId(); + IPage transports = transportService.page(page, + new LambdaQueryWrapper() + .eq(Transport::getShopId, shopId) + .like(StringUtils.isNotBlank(transport.getTransName()), Transport::getTransName, transport.getTransName())); + return ServerResponseEntity.success(transports); + } + + /** + * 获取运费模板详细信息 + * @param id 运费模板ID + * @return 服务器响应实体,包含运费模板的详细信息 + */ + @GetMapping("/info/{id}") + @PreAuthorize("@pms.hasPermission('shop:transport:info')") // 权限检查 + public ServerResponseEntity info(@PathVariable("id") Long id) { + Transport transport = transportService.getTransportAndAllItems(id); + return ServerResponseEntity.success(transport); + } + + /** + * 保存运费模板 + * @param transport 运费模板信息 + * @return 服务器响应实体 + */ + @PostMapping + @PreAuthorize("@pms.hasPermission('shop:transport:save')") // 权限检查 + public ServerResponseEntity save(@RequestBody Transport transport) { + Long shopId = SecurityUtils.getSysUser().getShopId(); + transport.setShopId(shopId); + Date createTime = new Date(); + transport.setCreateTime(createTime); + transportService.insertTransportAndTransfee(transport); + return ServerResponseEntity.success(); + } + + /** + * 修改运费模板 + * @param transport 运费模板信息 + * @return 服务器响应实体 + */ + @PutMapping + @PreAuthorize("@pms.hasPermission('shop:transport:update')") // 权限检查 + public ServerResponseEntity update(@RequestBody Transport transport) { + transportService.updateTransportAndTransfee(transport); + return ServerResponseEntity.success(); + } + + /** + * 删除运费模板 + * @param ids 运费模板ID数组 + * @return 服务器响应实体 + */ + @DeleteMapping + @PreAuthorize("@pms.hasPermission('shop:transport:delete')") // 权限检查 + public ServerResponseEntity delete(@RequestBody Long[] ids) { + transportService.deleteTransportAndTransfeeAndTranscity(ids); + // 删除运费模板的缓存 + for (Long id : ids) { + transportService.removeTransportAndAllItemsCache(id); + } + return ServerResponseEntity.success(); + } + + /** + * 获取运费模板列表 + * @return 服务器响应实体,包含运费模板列表信息 + */ + @GetMapping("/list") + public ServerResponseEntity> list() { + Long shopId = SecurityUtils.getSysUser().getShopId(); + List list = transportService.list(new LambdaQueryWrapper().eq(Transport::getShopId, shopId)); + return ServerResponseEntity.success(list); + } +} \ No newline at end of file diff --git a/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/UserAddrController.java b/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/UserAddrController.java new file mode 100644 index 0000000..8e9b8ba --- /dev/null +++ b/yami-shop-admin/src/main/java/com/yami/shop/admin/controller/UserAddrController.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.admin.controller; // 定义类所在的包 + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 引入MyBatis-Plus的条件查询包装器 +import com.baomidou.mybatisplus.core.metadata.IPage; // 引入MyBatis-Plus的分页接口 +import com.yami.shop.common.util.PageParam; // 引入分页参数工具类 +import com.yami.shop.bean.model.UserAddr; // 引入用户地址模型类 +import com.yami.shop.common.annotation.SysLog; // 引入自定义日志注解 +import com.yami.shop.service.UserAddrService; // 引入用户地址服务类 +import lombok.AllArgsConstructor; // 引入Lombok的@AllArgsConstructor注解 +import com.yami.shop.common.response.ServerResponseEntity; // 引入服务器响应实体类 +import org.springframework.security.access.prepost.PreAuthorize; // 引入Spring Security的PreAuthorize注解 +import org.springframework.web.bind.annotation.*; + +import jakarta.validation.Valid; // 引入Jakarta Validation的Valid注解 + +/** + * 用户地址管理控制器 + * 该类包含分页获取、获取详细信息、保存、修改和删除用户地址的方法。 + * @作者 hzm + * @日期 2019-04-15 10:49:40 + */ +@RestController // 标注这是一个控制器类,并且其返回结果直接写入HTTP响应体中,而不是视图名称 +@AllArgsConstructor // 使用Lombok注解生成全参构造函数 +@RequestMapping("/user/addr") // 定义请求路径的根地址为/user/addr +public class UserAddrController { + + private final UserAddrService userAddrService; // 自动注入用户地址服务类 + + /** + * 分页获取用户地址 + * @param page 分页对象 + * @param userAddr 用户地址查询条件 + * @return 服务器响应实体,包含分页后的用户地址信息 + */ + @GetMapping("/page") + public ServerResponseEntity> getUserAddrPage(PageParam page, UserAddr userAddr) { + return ServerResponseEntity.success(userAddrService.page(page, new LambdaQueryWrapper())); // 返回分页结果 + } + + /** + * 通过ID查询用户地址 + * @param addrId 用户地址ID + * @return 服务器响应实体,包含用户地址的详细信息 + */ + @GetMapping("/info/{addrId}") + public ServerResponseEntity getById(@PathVariable("addrId") Long addrId) { + return ServerResponseEntity.success(userAddrService.getById(addrId)); // 返回用户地址信息 + } + + /** + * 新增用户地址 + * @param userAddr 用户地址信息 + * @return 服务器响应实体,包含新增结果 + */ + @SysLog("新增用户地址管理") // 自定义日志注解 + @PostMapping + @PreAuthorize("@pms.hasPermission('user:addr:save')") // 权限检查 + public ServerResponseEntity save(@RequestBody @Valid UserAddr userAddr) { + return ServerResponseEntity.success(userAddrService.save(userAddr)); // 保存用户地址并返回结果 + } + + /** + * 修改用户地址 + * @param userAddr 用户地址信息 + * @return 服务器响应实体,包含修改结果 + */ + @SysLog("修改用户地址管理") // 自定义日志注解 + @PutMapping + @PreAuthorize("@pms.hasPermission('user:addr:update')") // 权限检查 + public ServerResponseEntity updateById(@RequestBody @Valid UserAddr userAddr) { + return ServerResponseEntity.success(userAddrService.updateById(userAddr)); // 修改用户地址并返回结果 + } + + /** + * 通过ID删除用户地址 + * @param addrId 用户地址ID + * @return 服务器响应实体,包含删除结果 + */ + @SysLog("删除用户地址管理") // 自定义日志注解 + @DeleteMapping("/{addrId}") + @PreAuthorize("@pms.hasPermission('user:addr:delete')") // 权限检查 + public ServerResponseEntity removeById(@PathVariable Long addrId) { + return ServerResponseEntity.success(userAddrService.removeById(addrId)); // 删除用户地址并返回结果 + } +} diff --git a/yami-shop-admin/src/main/java/com/yami/shop/admin/task/OrderTask.java b/yami-shop-admin/src/main/java/com/yami/shop/admin/task/OrderTask.java new file mode 100644 index 0000000..a19d08e --- /dev/null +++ b/yami-shop-admin/src/main/java/com/yami/shop/admin/task/OrderTask.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.admin.task; // 定义类所在的包 + +import cn.hutool.core.collection.CollectionUtil; // 引入Hutool工具类库中的CollectionUtil工具类 +import cn.hutool.core.date.DateUtil; // 引入Hutool工具类库中的DateUtil工具类 +import com.xxl.job.core.handler.annotation.XxlJob; // 引入XXL-Job的注解 +import com.yami.shop.bean.enums.OrderStatus; // 引入订单状态枚举类 +import com.yami.shop.bean.model.Order; // 引入订单模型类 +import com.yami.shop.bean.model.OrderItem; // 引入订单项模型类 +import com.yami.shop.service.OrderService; // 引入订单服务类 +import com.yami.shop.service.ProductService; // 引入商品服务类 +import com.yami.shop.service.SkuService; // 引入SKU服务类 +import org.slf4j.Logger; // 引入SLF4J的Logger类 +import org.slf4j.LoggerFactory; // 引入SLF4J的LoggerFactory类 +import org.springframework.beans.factory.annotation.Autowired; // 引入Spring的@Autowired注解 +import org.springframework.stereotype.Component; // 引入Spring的@Component注解 + +import java.util.Date; // 引入Java的Date类 +import java.util.List; // 引入Java的List接口 + +/** + * OrderTask类,用于管理与订单相关的定时任务操作。 + * 定时任务的配置,请查看xxl-job的java配置文件。 + * @作者 FrozenWatermelon + * @参见 com.yami.shop.admin.config.XxlJobConfig + */ +@Component("orderTask") // 标注这是一个Spring组件,并且以orderTask为组件名称 +public class OrderTask { + + private static final Logger logger = LoggerFactory.getLogger(OrderTask.class); // 日志记录器 + + @Autowired + private OrderService orderService; // 自动注入订单服务类 + @Autowired + private ProductService productService; // 自动注入商品服务类 + @Autowired + private SkuService skuService; // 自动注入SKU服务类 + + /** + * 取消超时未支付订单 + */ + @XxlJob("cancelOrder") + public void cancelOrder() { + Date now = new Date(); + logger.info("取消超时未支付订单。。。"); + // 获取30分钟之前未支付的订单 + List orders = orderService.listOrderAndOrderItems(OrderStatus.UNPAY.value(), DateUtil.offsetMinute(now, -30)); + if (CollectionUtil.isEmpty(orders)) { + return; + } + orderService.cancelOrders(orders); + for (Order order : orders) { + List orderItems = order.getOrderItems(); + for (OrderItem orderItem : orderItems) { + productService.removeProductCacheByProdId(orderItem.getProdId()); + skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId()); + } + } + } + + /** + * 确认收货 + */ + @XxlJob("confirmOrder") + public void confirmOrder() { + Date now = new Date(); + logger.info("系统自动确认收货订单。。。"); + // 获取15天之前未支付的订单 + List orders = orderService.listOrderAndOrderItems(OrderStatus.CONSIGNMENT.value(), DateUtil.offsetDay(now, -15)); + if (CollectionUtil.isEmpty(orders)) { + return; + } + orderService.confirmOrder(orders); + for (Order order : orders) { + List orderItems = order.getOrderItems(); + for (OrderItem orderItem : orderItems) { + productService.removeProductCacheByProdId(orderItem.getProdId()); + skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId()); + } + } + } +} \ No newline at end of file diff --git a/yami-shop-api/src/main/java/com/yami/shop/api/config/ApiBeanConfig.java b/yami-shop-api/src/main/java/com/yami/shop/api/config/ApiBeanConfig.java new file mode 100644 index 0000000..256a117 --- /dev/null +++ b/yami-shop-api/src/main/java/com/yami/shop/api/config/ApiBeanConfig.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.api.config; // 定义类所在的包 + +import cn.hutool.core.lang.Snowflake; // 引入Hutool工具包中的Snowflake类,用于生成唯一ID +import lombok.AllArgsConstructor; // 引入Lombok的@AllArgsConstructor注解,自动生成全参构造函数 +import org.springframework.context.annotation.Bean; // 引入Spring的@Bean注解 +import org.springframework.context.annotation.Configuration; // 引入Spring的@Configuration注解 + +/** + * ApiBeanConfig类,用于配置Snowflake实例,生成唯一ID。 + * @作者 lanhai + */ +@Configuration // 标注这是一个配置类 +@AllArgsConstructor // 使用Lombok注解生成全参构造函数 +public class ApiBeanConfig { + + private final ApiConfig apiConfig; // 自动注入ApiConfig配置类的实例 + + @Bean // 标注这是一个Spring Bean,会被Spring容器管理 + public Snowflake snowflake() { + // 创建并返回一个Snowflake实例,用于生成全局唯一ID + return new Snowflake(apiConfig.getWorkerId(), apiConfig.getDatacenterId()); + } +} diff --git a/yami-shop-api/src/main/java/com/yami/shop/api/controller/AddrController.java b/yami-shop-api/src/main/java/com/yami/shop/api/controller/AddrController.java new file mode 100644 index 0000000..9e0acc5 --- /dev/null +++ b/yami-shop-api/src/main/java/com/yami/shop/api/controller/AddrController.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.api.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yami.shop.bean.app.dto.UserAddrDto; +import com.yami.shop.bean.app.param.AddrParam; +import com.yami.shop.bean.model.UserAddr; +import com.yami.shop.common.exception.YamiShopBindException; +import com.yami.shop.common.response.ServerResponseEntity; +import com.yami.shop.security.api.util.SecurityUtils; +import com.yami.shop.service.UserAddrService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import cn.hutool.core.bean.BeanUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import jakarta.validation.Valid; +import java.util.Date; +import java.util.List; + +/** + * @author lanhai + */ +@RestController // 标注这是一个控制器类,并且其返回结果直接写入HTTP响应体中,而不是视图名称 +@RequestMapping("/p/address") // 定义请求路径的根地址为/p/address +@Tag(name = "地址接口") // 给API文档添加标签,描述这个控制器的功能 +@AllArgsConstructor // Lombok注解,用于生成全参构造函数 +public class AddrController { + + @Autowired + private UserAddrService userAddrService; // 自动注入用户地址服务类 + + /** + * 选择订单配送地址 + */ + @GetMapping("/list") + @Operation(summary = "用户地址列表" , description = "获取用户的所有地址信息") + public ServerResponseEntity> dvyList() { + // 获取当前用户ID + String userId = SecurityUtils.getUser().getUserId(); + // 查询用户的所有地址信息,并按是否常用和更新时间倒序排序 + List userAddrs = userAddrService.list( + new LambdaQueryWrapper() + .eq(UserAddr::getUserId, userId) + .orderByDesc(UserAddr::getCommonAddr) + .orderByDesc(UserAddr::getUpdateTime) + ); + // 将地址信息转换成DTO对象并返回 + return ServerResponseEntity.success(BeanUtil.copyToList(userAddrs, UserAddrDto.class)); + } + + @PostMapping("/addAddr") + @Operation(summary = "新增用户地址" , description = "新增用户地址") + public ServerResponseEntity addAddr(@Valid @RequestBody AddrParam addrParam) { + String userId = SecurityUtils.getUser().getUserId(); + + // 检查地址是否已存在 + if (addrParam.getAddrId() != null && addrParam.getAddrId() != 0) { + return ServerResponseEntity.showFailMsg("该地址已存在"); + } + + // 统计用户已有的地址数量 + long addrCount = userAddrService.count(new LambdaQueryWrapper().eq(UserAddr::getUserId, userId)); + UserAddr userAddr = BeanUtil.copyProperties(addrParam, UserAddr.class); + + // 如果没有地址,设为常用地址,否则设为非常用 + if (addrCount == 0) { + userAddr.setCommonAddr(1); + } else { + userAddr.setCommonAddr(0); + } + + userAddr.setUserId(userId); + userAddr.setStatus(1); // 设置状态为有效 + userAddr.setCreateTime(new Date()); // 设置创建时间 + userAddr.setUpdateTime(new Date()); // 设置更新时间 + + // 保存地址信息 + userAddrService.save(userAddr); + + // 如果是常用地址,清除默认地址缓存 + if (userAddr.getCommonAddr() == 1) { + userAddrService.removeUserAddrByUserId(0L, userId); + } + return ServerResponseEntity.success("添加地址成功"); + } + + /** + * 修改订单配送地址 + */ + @PutMapping("/updateAddr") + @Operation(summary = "修改订单用户地址" , description = "修改用户地址") + public ServerResponseEntity updateAddr(@Valid @RequestBody AddrParam addrParam) { + String userId = SecurityUtils.getUser().getUserId(); + + // 根据用户ID和地址ID获取地址信息 + UserAddr dbUserAddr = userAddrService.getUserAddrByUserId(addrParam.getAddrId(), userId); + if (dbUserAddr == null) { + return ServerResponseEntity.showFailMsg("该地址已被删除"); + } + + // 更新地址信息 + UserAddr userAddr = BeanUtil.copyProperties(addrParam, UserAddr.class); + userAddr.setUserId(userId); + userAddr.setUpdateTime(new Date()); + userAddrService.updateById(userAddr); + + // 清除缓存 + userAddrService.removeUserAddrByUserId(addrParam.getAddrId(), userId); + userAddrService.removeUserAddrByUserId(0L, userId); + + return ServerResponseEntity.success("修改地址成功"); + } + + /** + * 删除订单配送地址 + */ + @DeleteMapping("/deleteAddr/{addrId}") + @Operation(summary = "删除订单用户地址" , description = "根据地址id,删除用户地址") + @Parameter(name = "addrId", description = "地址ID" , required = true) + public ServerResponseEntity deleteDvy(@PathVariable("addrId") Long addrId) { + String userId = SecurityUtils.getUser().getUserId(); + + // 根据用户ID和地址ID获取地址信息 + UserAddr userAddr = userAddrService.getUserAddrByUserId(addrId, userId); + if (userAddr == null) { + return ServerResponseEntity.showFailMsg("该地址已被删除"); + } + + // 检查是否为默认地址,默认地址无法删除 + if (userAddr.getCommonAddr() == 1) { + return ServerResponseEntity.showFailMsg("默认地址无法删除"); + } + + // 删除地址信息 + userAddrService.removeById(addrId); + userAddrService.removeUserAddrByUserId(addrId, userId); + return ServerResponseEntity.success("删除地址成功"); + } + + /** + * 设置默认地址 + */ + @PutMapping("/defaultAddr/{addrId}") + @Operation(summary = "设置默认地址" , description = "根据地址id,设置默认地址") + public ServerResponseEntity defaultAddr(@PathVariable("addrId") Long addrId) { + String userId = SecurityUtils.getUser().getUserId(); + + // 更新默认地址 + userAddrService.updateDefaultUserAddr(addrId, userId); + + // 清除缓存 + userAddrService.removeUserAddrByUserId(0L, userId); + userAddrService.removeUserAddrByUserId(addrId, userId); + + return ServerResponseEntity.success("修改地址成功"); + } + + /** + * 获取地址信息订单配送地址 + */ + @GetMapping("/addrInfo/{addrId}") + @Operation(summary = "获取地址信息" , description = "根据地址id,获取地址信息") + @Parameter(name = "addrId", description = "地址ID" , required = true) + public ServerResponseEntity addrInfo(@PathVariable("addrId") Long addrId) { + String userId = SecurityUtils.getUser().getUserId(); + + // 根据用户ID和地址ID获取地址信息 + UserAddr userAddr = userAddrService.getUserAddrByUserId(addrId, userId); + if (userAddr == null) { + throw new YamiShopBindException("该地址已被删除"); + } + + // 转换为DTO对象并返回 + return ServerResponseEntity.success(BeanUtil.copyProperties(userAddr, UserAddrDto.class)); + } + +} diff --git a/yami-shop-api/src/main/java/com/yami/shop/api/controller/AreaController.java b/yami-shop-api/src/main/java/com/yami/shop/api/controller/AreaController.java new file mode 100644 index 0000000..b00d67e --- /dev/null +++ b/yami-shop-api/src/main/java/com/yami/shop/api/controller/AreaController.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.api.controller; // 定义类所在的包 + +import java.util.List; // 引入Java的List接口 + +import com.yami.shop.service.AreaService; // 引入区域服务类 +import org.springframework.beans.factory.annotation.Autowired; // 引入Spring的@Autowired注解 +import com.yami.shop.common.response.ServerResponseEntity; // 引入服务器响应实体类 +import org.springframework.web.bind.annotation.GetMapping; // 引入Spring的@GetMapping注解 +import org.springframework.web.bind.annotation.RequestMapping; // 引入Spring的@RequestMapping注解 +import org.springframework.web.bind.annotation.RestController; // 引入Spring的@RestController注解 + +import com.yami.shop.bean.model.Area; // 引入区域模型 + +import io.swagger.v3.oas.annotations.tags.Tag; // 引入Swagger的Tag注解 +import io.swagger.v3.oas.annotations.Parameter; // 引入Swagger的Parameter注解 +import io.swagger.v3.oas.annotations.Operation; // 引入Swagger的Operation注解 + +/** + * AreaController类,负责处理省市区信息的获取请求。 + * 该类包含一个根据父级ID获取省市区信息的方法。 + * @作者 lgh on 2018/10/26. + */ +@RestController // 标注这是一个控制器类,并且其返回结果直接写入HTTP响应体中,而不是视图名称 +@RequestMapping("/p/area") // 定义请求路径的根地址为/p/area +@Tag(name = "省市区接口") // 给API文档添加标签,描述这个控制器的功能 +public class AreaController { + + @Autowired + private AreaService areaService; // 自动注入区域服务类 + + /** + * 根据省市区的父级ID获取地址信息 + * @param pid 省市区的父级ID(pid为0获取所有省份) + * @return 服务器响应实体,包含查询到的省市区信息 + */ + @GetMapping("/listByPid") + @Operation(summary = "获取省市区信息" , description = "根据省市区的pid获取地址信息") + @Parameter(name = "pid", description = "省市区的pid(pid为0获取所有省份)" , required = true) + public ServerResponseEntity> listByPid(Long pid) { + List list = areaService.listByPid(pid); + return ServerResponseEntity.success(list); + } +} diff --git a/yami-shop-api/src/main/java/com/yami/shop/api/controller/CategoryController.java b/yami-shop-api/src/main/java/com/yami/shop/api/controller/CategoryController.java new file mode 100644 index 0000000..acacb22 --- /dev/null +++ b/yami-shop-api/src/main/java/com/yami/shop/api/controller/CategoryController.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.api.controller; // 定义类所在的包 + +import java.util.List; // 引入Java的List接口 + +import org.springframework.beans.factory.annotation.Autowired; // 引入Spring的@Autowired注解 +import com.yami.shop.common.response.ServerResponseEntity; // 引入服务器响应实体类 +import org.springframework.web.bind.annotation.*; // 引入Spring Web的注解 + +import com.yami.shop.bean.app.dto.CategoryDto; // 引入分类DTO类 +import com.yami.shop.bean.model.Category; // 引入分类模型类 +import com.yami.shop.service.CategoryService; // 引入分类服务类 + +import io.swagger.v3.oas.annotations.tags.Tag; // 引入Swagger的Tag注解 +import io.swagger.v3.oas.annotations.Parameter; // 引入Swagger的Parameter注解 +import io.swagger.v3.oas.annotations.Operation; // 引入Swagger的Operation注解 +import cn.hutool.core.bean.BeanUtil; // 引入Hutool工具类库中的BeanUtil工具类 + +/** + * CategoryController类,用于管理商品分类。 + * 该类包含获取分类信息列表的方法。 + * @作者 lanhai + */ +@RestController // 标注这是一个控制器类,并且其返回结果直接写入HTTP响应体中,而不是视图名称 +@RequestMapping("/category") // 定义请求路径的根地址为/category +@Tag(name = "分类接口") // 给API文档添加标签,描述这个控制器的功能 +public class CategoryController { + + @Autowired + private CategoryService categoryService; // 自动注入分类服务类 + + /** + * 分类信息列表接口 + * @param parentId 分类ID,默认值为0 + * @return 服务器响应实体,包含分类信息列表 + */ + @GetMapping("/categoryInfo") + @Operation(summary = "分类信息列表", description = "获取所有的产品分类信息,顶级分类的parentId为0,默认为顶级分类") + @Parameter(name = "parentId", description = "分类ID", required = false) + public ServerResponseEntity> categoryInfo(@RequestParam(value = "parentId", defaultValue = "0") Long parentId) { + List categories = categoryService.listByParentId(parentId); + List categoryDtos = BeanUtil.copyToList(categories, CategoryDto.class); + return ServerResponseEntity.success(categoryDtos); + } +} \ No newline at end of file diff --git a/yami-shop-api/src/main/java/com/yami/shop/api/controller/DeliveryController.java b/yami-shop-api/src/main/java/com/yami/shop/api/controller/DeliveryController.java new file mode 100644 index 0000000..b7ef9ba --- /dev/null +++ b/yami-shop-api/src/main/java/com/yami/shop/api/controller/DeliveryController.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.api.controller; // 定义类所在的包 + +import com.yami.shop.service.OrderService; // 引入订单服务类 +import org.springframework.beans.factory.annotation.Autowired; // 引入Spring的@Autowired注解 +import com.yami.shop.common.response.ServerResponseEntity; // 引入服务器响应实体类 +import org.springframework.web.bind.annotation.GetMapping; // 引入Spring的@GetMapping注解 +import org.springframework.web.bind.annotation.RequestMapping; // 引入Spring的@RequestMapping注解 +import org.springframework.web.bind.annotation.RestController; // 引入Spring的@RestController注解 + +import com.yami.shop.bean.app.dto.DeliveryDto; // 引入物流DTO +import com.yami.shop.bean.model.Delivery; // 引入配送模型 +import com.yami.shop.bean.model.Order; // 引入订单模型 +import com.yami.shop.common.util.Json; // 引入JSON工具类 +import com.yami.shop.service.DeliveryService; // 引入配送服务类 + +import cn.hutool.http.HttpUtil; // 引入Hutool工具类库中的HttpUtil工具类 +import io.swagger.v3.oas.annotations.tags.Tag; // 引入Swagger的Tag注解 +import io.swagger.v3.oas.annotations.Parameter; // 引入Swagger的Parameter注解 +import io.swagger.v3.oas.annotations.Operation; // 引入Swagger的Operation注解 + +/** + * DeliveryController类,负责查看物流信息。 + * 该类包含一个查看物流信息的方法,根据订单号查看物流详情。 + * @作者 lanhai + */ +@RestController // 标注这是一个控制器类,并且其返回结果直接写入HTTP响应体中,而不是视图名称 +@RequestMapping("/delivery") // 定义请求路径的根地址为/delivery +@Tag(name = "查看物流接口") // 给API文档添加标签,描述这个控制器的功能 +public class DeliveryController { + + @Autowired + private DeliveryService deliveryService; // 自动注入配送服务类 + @Autowired + private OrderService orderService; // 自动注入订单服务类 + + /** + * 查看物流接口 + * @param orderNumber 订单号 + * @return 服务器响应实体,包含物流信息 + */ + @GetMapping("/check") + @Operation(summary = "查看物流" , description = "根据订单号查看物流") + @Parameter(name = "orderNumber", description = "订单号" , required = true) + public ServerResponseEntity checkDelivery(String orderNumber) { + + Order order = orderService.getOrderByOrderNumber(orderNumber); // 根据订单号获取订单信息 + Delivery delivery = deliveryService.getById(order.getDvyId()); // 根据配送ID获取配送信息 + String url = delivery.getQueryUrl().replace("{dvyFlowId}", order.getDvyFlowId()); // 构建查询物流信息的URL + String deliveryJson = HttpUtil.get(url); // 发送HTTP GET请求获取物流信息 + + DeliveryDto deliveryDto = Json.parseObject(deliveryJson, DeliveryDto.class); // 将JSON格式的物流信息转换为DTO对象 + deliveryDto.setDvyFlowId(order.getDvyFlowId()); // 设置物流流水号 + deliveryDto.setCompanyHomeUrl(delivery.getCompanyHomeUrl()); // 设置物流公司主页URL + deliveryDto.setCompanyName(delivery.getDvyName()); // 设置物流公司名称 + return ServerResponseEntity.success(deliveryDto); // 返回物流信息 + } +} diff --git a/yami-shop-api/src/main/java/com/yami/shop/api/controller/MyOrderController.java b/yami-shop-api/src/main/java/com/yami/shop/api/controller/MyOrderController.java new file mode 100644 index 0000000..69050c7 --- /dev/null +++ b/yami-shop-api/src/main/java/com/yami/shop/api/controller/MyOrderController.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.api.controller; // 定义类所在的包 + +import cn.hutool.core.bean.BeanUtil; // 引入Hutool工具类库中的BeanUtil工具类 +import com.baomidou.mybatisplus.core.metadata.IPage; // 引入MyBatis-Plus的分页接口 +import com.yami.shop.bean.app.dto.*; // 引入各种DTO类 +import com.yami.shop.bean.enums.OrderStatus; // 引入订单状态枚举 +import com.yami.shop.bean.model.Order; // 引入订单模型 +import com.yami.shop.bean.model.OrderItem; // 引入订单项模型 +import com.yami.shop.bean.model.ShopDetail; // 引入店铺详情模型 +import com.yami.shop.bean.model.UserAddrOrder; // 引入用户地址订单模型 +import com.yami.shop.common.exception.YamiShopBindException; // 引入自定义异常类 +import com.yami.shop.common.response.ServerResponseEntity; // 引入服务器响应实体类 +import com.yami.shop.common.util.Arith; // 引入算术工具类 +import com.yami.shop.common.util.PageParam; // 引入分页参数工具类 +import com.yami.shop.security.api.util.SecurityUtils; // 引入安全工具类 +import com.yami.shop.service.*; // 引入各种服务类 +import io.swagger.v3.oas.annotations.Operation; // 引入Swagger的Operation注解 +import io.swagger.v3.oas.annotations.Parameter; // 引入Swagger的Parameter注解 +import io.swagger.v3.oas.annotations.Parameters; // 引入Swagger的Parameters注解 +import io.swagger.v3.oas.annotations.tags.Tag; // 引入Swagger的Tag注解 +import lombok.AllArgsConstructor; // 引入Lombok的@AllArgsConstructor注解 +import org.springframework.web.bind.annotation.*; + +import java.util.Collections; // 引入Java的Collections工具类 +import java.util.List; // 引入Java的List接口 +import java.util.Objects; // 引入Java的Objects工具类 + +/** + * MyOrderController类,用于管理用户的订单操作。 + * 该类包含获取订单详情、获取订单列表、取消订单、确认收货、删除订单和获取订单数量的方法。 + * @作者 lanhai + */ +@RestController // 标注这是一个控制器类,并且其返回结果直接写入HTTP响应体中,而不是视图名称 +@RequestMapping("/p/myOrder") // 定义请求路径的根地址为/p/myOrder +@Tag(name = "我的订单接口") // 给API文档添加标签,描述这个控制器的功能 +@AllArgsConstructor // 使用Lombok注解生成全参构造函数 +public class MyOrderController { + + private final OrderService orderService; // 注入订单服务类 + + private final UserAddrOrderService userAddrOrderService; // 注入用户地址订单服务类 + + private final ProductService productService; // 注入商品服务类 + + private final SkuService skuService; // 注入SKU服务类 + + private final MyOrderService myOrderService; // 注入我的订单服务类 + + private final ShopDetailService shopDetailService; // 注入店铺详情服务类 + + private final OrderItemService orderItemService; // 注入订单项服务类 + + /** + * 订单详情信息接口 + * @param orderNumber 订单号 + * @return 服务器响应实体,包含订单详情信息 + */ + @GetMapping("/orderDetail") + @Operation(summary = "订单详情信息", description = "根据订单号获取订单详情信息") + @Parameter(name = "orderNumber", description = "订单号", required = true) + public ServerResponseEntity orderDetail(@RequestParam(value = "orderNumber") String orderNumber) { + + String userId = SecurityUtils.getUser().getUserId(); + OrderShopDto orderShopDto = new OrderShopDto(); + + Order order = orderService.getOrderByOrderNumber(orderNumber); + + if (order == null) { + throw new RuntimeException("该订单不存在"); + } + if (!Objects.equals(order.getUserId(), userId)) { + throw new RuntimeException("你没有权限获取该订单信息"); + } + + ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(order.getShopId()); + UserAddrOrder userAddrOrder = userAddrOrderService.getById(order.getAddrOrderId()); + UserAddrDto userAddrDto = BeanUtil.copyProperties(userAddrOrder, UserAddrDto.class); + List orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber); + List orderItemList = BeanUtil.copyToList(orderItems, OrderItemDto.class); + + orderShopDto.setShopId(shopDetail.getShopId()); + orderShopDto.setShopName(shopDetail.getShopName()); + orderShopDto.setActualTotal(order.getActualTotal()); + orderShopDto.setUserAddrDto(userAddrDto); + orderShopDto.setOrderItemDtos(orderItemList); + orderShopDto.setTransfee(order.getFreightAmount()); + orderShopDto.setReduceAmount(order.getReduceAmount()); + orderShopDto.setCreateTime(order.getCreateTime()); + orderShopDto.setRemarks(order.getRemarks()); + orderShopDto.setStatus(order.getStatus()); + + double total = 0.0; + Integer totalNum = 0; + for (OrderItemDto orderItem : orderShopDto.getOrderItemDtos()) { + total = Arith.add(total, orderItem.getProductTotalAmount()); + totalNum += orderItem.getProdCount(); + } + orderShopDto.setTotal(total); + orderShopDto.setTotalNum(totalNum); + + return ServerResponseEntity.success(orderShopDto); + } + + /** + * 订单列表接口 + * @param status 订单状态 + * @param page 分页参数 + * @return 服务器响应实体,包含订单列表信息 + */ + @GetMapping("/myOrder") + @Operation(summary = "订单列表信息", description = "根据订单状态获取订单列表信息,状态为0时获取所有订单") + @Parameters({ + @Parameter(name = "status", description = "订单状态 1:待付款 2:待发货 3:待收货 4:待评价 5:成功 6:失败") + }) + public ServerResponseEntity> myOrder(@RequestParam(value = "status") Integer status, PageParam page) { + String userId = SecurityUtils.getUser().getUserId(); + IPage myOrderDtoIpage = myOrderService.pageMyOrderByUserIdAndStatus(page, userId, status); + return ServerResponseEntity.success(myOrderDtoIpage); + } + + /** + * 取消订单 + * @param orderNumber 订单号 + * @return 服务器响应实体 + */ + @PutMapping("/cancel/{orderNumber}") + @Operation(summary = "根据订单号取消订单", description = "根据订单号取消订单") + @Parameter(name = "orderNumber", description = "订单号", required = true) + public ServerResponseEntity cancel(@PathVariable("orderNumber") String orderNumber) { + String userId = SecurityUtils.getUser().getUserId(); + Order order = orderService.getOrderByOrderNumber(orderNumber); + if (!Objects.equals(order.getUserId(), userId)) { + throw new YamiShopBindException("你没有权限获取该订单信息"); + } + if (!Objects.equals(order.getStatus(), OrderStatus.UNPAY.value())) { + throw new YamiShopBindException("订单已支付,无法取消订单"); + } + List orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber); + order.setOrderItems(orderItems); + // 取消订单 + orderService.cancelOrders(Collections.singletonList(order)); + + // 清除缓存 + for (OrderItem orderItem : orderItems) { + productService.removeProductCacheByProdId(orderItem.getProdId()); + skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId()); + } + return ServerResponseEntity.success(); + } + + /** + * 确认收货 + * @param orderNumber 订单号 + * @return 服务器响应实体 + */ + @PutMapping("/receipt/{orderNumber}") + @Operation(summary = "根据订单号确认收货", description = "根据订单号确认收货") + public ServerResponseEntity receipt(@PathVariable("orderNumber") String orderNumber) { + String userId = SecurityUtils.getUser().getUserId(); + Order order = orderService.getOrderByOrderNumber(orderNumber); + if (!Objects.equals(order.getUserId(), userId)) { + throw new YamiShopBindException("你没有权限获取该订单信息"); + } + if (!Objects.equals(order.getStatus(), OrderStatus.CONSIGNMENT.value())) { + throw new YamiShopBindException("订单未发货,无法确认收货"); + } + List orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber); + order.setOrderItems(orderItems); + // 确认收货 + orderService.confirmOrder(Collections.singletonList(order)); + + for (OrderItem orderItem : orderItems) { + productService.removeProductCacheByProdId(orderItem.getProdId()); + skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId()); + } + return ServerResponseEntity.success(); + } + + /** + * 删除订单 + * @param orderNumber 订单号 + * @return 服务器响应实体 + */ + @DeleteMapping("/{orderNumber}") + @Operation(summary = "根据订单号删除订单", description = "根据订单号删除订单") + @Parameter(name = "orderNumber", description = "订单号", required = true) + public ServerResponseEntity delete(@PathVariable("orderNumber") String orderNumber) { + String userId = SecurityUtils.getUser().getUserId(); + + 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 getOrderCount() { + String userId = SecurityUtils.getUser().getUserId(); + OrderCountData orderCountMap = orderService.getOrderCount(userId); + return ServerResponseEntity.success(orderCountMap); + } +} diff --git a/yami-shop-api/src/main/java/com/yami/shop/api/controller/OrderController.java b/yami-shop-api/src/main/java/com/yami/shop/api/controller/OrderController.java new file mode 100644 index 0000000..3dca831 --- /dev/null +++ b/yami-shop-api/src/main/java/com/yami/shop/api/controller/OrderController.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.api.controller; // 定义类所在的包 + +import cn.hutool.core.collection.CollectionUtil; // 引入Hutool工具类库中的CollectionUtil工具类 +import com.yami.shop.bean.app.dto.*; // 引入各种DTO类 +import com.yami.shop.bean.app.param.OrderParam; // 引入订单参数类 +import com.yami.shop.bean.app.param.OrderShopParam; // 引入订单店铺参数类 +import com.yami.shop.bean.app.param.SubmitOrderParam; // 引入提交订单参数类 +import com.yami.shop.bean.event.ConfirmOrderEvent; // 引入确认订单事件类 +import com.yami.shop.bean.model.Order; // 引入订单模型类 +import com.yami.shop.bean.model.UserAddr; // 引入用户地址模型类 +import com.yami.shop.common.exception.YamiShopBindException; // 引入自定义异常类 +import com.yami.shop.common.util.Arith; // 引入算术工具类 +import com.yami.shop.security.api.util.SecurityUtils; // 引入安全工具类 +import com.yami.shop.service.*; // 引入各种服务类 +import io.swagger.v3.oas.annotations.tags.Tag; // 引入Swagger的Tag注解 +import io.swagger.v3.oas.annotations.Operation; // 引入Swagger的Operation注解 +import cn.hutool.core.bean.BeanUtil; // 引入Hutool工具类库中的BeanUtil工具类 +import org.springframework.beans.factory.annotation.Autowired; // 引入Spring的@Autowired注解 +import org.springframework.context.ApplicationContext; // 引入Spring的ApplicationContext类 +import com.yami.shop.common.response.ServerResponseEntity; // 引入服务器响应实体类 +import org.springframework.web.bind.annotation.*; // 引入Spring Web的注解 + +import jakarta.validation.Valid; // 引入Jakarta Validation的Valid注解 +import java.util.ArrayList; // 引入Java的ArrayList类 +import java.util.List; // 引入Java的List接口 +import java.util.Objects; // 引入Java的Objects工具类 + +/** + * OrderController类,用于管理订单操作,包括生成订单和提交订单等功能。 + * @作者 lanhai + */ +@RestController // 标注这是一个控制器类,并且其返回结果直接写入HTTP响应体中,而不是视图名称 +@RequestMapping("/p/order") // 定义请求路径的根地址为/p/order +@Tag(name = "订单接口") // 给API文档添加标签,描述这个控制器的功能 +public class OrderController { + + @Autowired + private OrderService orderService; // 自动注入订单服务类 + @Autowired + private SkuService skuService; // 自动注入SKU服务类 + @Autowired + private ProductService productService; // 自动注入商品服务类 + @Autowired + private UserAddrService userAddrService; // 自动注入用户地址服务类 + @Autowired + private BasketService basketService; // 自动注入购物车服务类 + @Autowired + private ApplicationContext applicationContext; // 自动注入Spring应用上下文 + + /** + * 生成订单 + * @param orderParam 订单参数 + * @return 服务器响应实体,包含生成的订单信息 + */ + @PostMapping("/confirm") + @Operation(summary = "结算,生成订单信息" , description = "传入下单所需要的参数进行下单") + public ServerResponseEntity confirm(@Valid @RequestBody OrderParam orderParam) { + String userId = SecurityUtils.getUser().getUserId(); + + // 获取订单的地址信息 + UserAddr userAddr = userAddrService.getUserAddrByUserId(orderParam.getAddrId(), userId); + UserAddrDto userAddrDto = BeanUtil.copyProperties(userAddr, UserAddrDto.class); + + // 获取用户提交的购物车商品项 + List shopCartItems = basketService.getShopCartItemsByOrderItems(orderParam.getBasketIds(), orderParam.getOrderItem(), userId); + if (CollectionUtil.isEmpty(shopCartItems)) { + throw new YamiShopBindException("请选择您需要的商品加入购物车"); + } + + // 根据店铺组装购物车中的商品信息 + List shopCarts = basketService.getShopCarts(shopCartItems); + + // 生成完整的订单信息 + ShopCartOrderMergerDto shopCartOrderMergerDto = new ShopCartOrderMergerDto(); + shopCartOrderMergerDto.setUserAddr(userAddrDto); + List shopCartOrders = new ArrayList<>(); + double actualTotal = 0.0; + double total = 0.0; + int totalCount = 0; + double orderReduce = 0.0; + + for (ShopCartDto shopCart : shopCarts) { + ShopCartOrderDto shopCartOrder = new ShopCartOrderDto(); + shopCartOrder.setShopId(shopCart.getShopId()); + shopCartOrder.setShopName(shopCart.getShopName()); + + List shopCartItemDiscounts = shopCart.getShopCartItemDiscounts(); + List shopAllShopCartItems = new ArrayList<>(); + for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartItemDiscounts) { + List discountShopCartItems = shopCartItemDiscount.getShopCartItems(); + shopAllShopCartItems.addAll(discountShopCartItems); + } + + shopCartOrder.setShopCartItemDiscounts(shopCartItemDiscounts); + applicationContext.publishEvent(new ConfirmOrderEvent(shopCartOrder, orderParam, shopAllShopCartItems)); + + actualTotal = Arith.add(actualTotal, shopCartOrder.getActualTotal()); + total = Arith.add(total, shopCartOrder.getTotal()); + totalCount += shopCartOrder.getTotalCount(); + orderReduce = Arith.add(orderReduce, shopCartOrder.getShopReduce()); + shopCartOrders.add(shopCartOrder); + } + + shopCartOrderMergerDto.setActualTotal(actualTotal); + shopCartOrderMergerDto.setTotal(total); + shopCartOrderMergerDto.setTotalCount(totalCount); + shopCartOrderMergerDto.setShopCartOrders(shopCartOrders); + shopCartOrderMergerDto.setOrderReduce(orderReduce); + + shopCartOrderMergerDto = orderService.putConfirmOrderCache(userId, shopCartOrderMergerDto); + + return ServerResponseEntity.success(shopCartOrderMergerDto); + } + + /** + * 提交订单,根据店铺拆单 + * @param submitOrderParam 提交订单参数 + * @return 服务器响应实体,包含支付流水号 + */ + @PostMapping("/submit") + @Operation(summary = "提交订单,返回支付流水号" , description = "根据传入的参数判断是否为购物车提交订单,同时对购物车进行删除,用户开始进行支付") + public ServerResponseEntity submitOrders(@Valid @RequestBody SubmitOrderParam submitOrderParam) { + String userId = SecurityUtils.getUser().getUserId(); + ShopCartOrderMergerDto mergerOrder = orderService.getConfirmOrderCache(userId); + if (mergerOrder == null) { + throw new YamiShopBindException("订单已过期,请重新下单"); + } + + List orderShopParams = submitOrderParam.getOrderShopParam(); + List shopCartOrders = mergerOrder.getShopCartOrders(); + + // 设置备注 + if (CollectionUtil.isNotEmpty(orderShopParams)) { + for (ShopCartOrderDto shopCartOrder : shopCartOrders) { + for (OrderShopParam orderShopParam : orderShopParams) { + if (Objects.equals(shopCartOrder.getShopId(), orderShopParam.getShopId())) { + shopCartOrder.setRemarks(orderShopParam.getRemarks()); + } + } + } + } + + List orders = orderService.submit(userId, mergerOrder); + + StringBuilder orderNumbers = new StringBuilder(); + for (Order order : orders) { + orderNumbers.append(order.getOrderNumber()).append(","); + } + orderNumbers.deleteCharAt(orderNumbers.length() - 1); + + boolean isShopCartOrder = false; + // 移除缓存 + for (ShopCartOrderDto shopCartOrder : shopCartOrders) { + for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartOrder.getShopCartItemDiscounts()) { + for (ShopCartItemDto shopCartItem : shopCartItemDiscount.getShopCartItems()) { + Long basketId = shopCartItem.getBasketId(); + if (basketId != null && basketId != 0) { + isShopCartOrder = true; + } + skuService.removeSkuCacheBySkuId(shopCartItem.getSkuId(), shopCartItem.getProdId()); + productService.removeProductCacheByProdId(shopCartItem.getProdId()); + } + } + } + // 购物车提交订单时(即有购物车ID时) + if (isShopCartOrder) { + basketService.removeShopCartItemsCacheByUserId(userId); + } + orderService.removeConfirmOrderCache(userId); + return ServerResponseEntity.success(new OrderNumbersDto(orderNumbers.toString())); + } + +} diff --git a/yami-shop-api/src/main/java/com/yami/shop/api/controller/PayController.java b/yami-shop-api/src/main/java/com/yami/shop/api/controller/PayController.java new file mode 100644 index 0000000..464314a --- /dev/null +++ b/yami-shop-api/src/main/java/com/yami/shop/api/controller/PayController.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.api.controller; // 定义类所在的包 + +import com.yami.shop.bean.app.param.PayParam; // 引入支付参数类 +import com.yami.shop.bean.pay.PayInfoDto; // 引入支付信息DTO类 +import com.yami.shop.security.api.model.YamiUser; // 引入用户模型类 +import com.yami.shop.security.api.util.SecurityUtils; // 引入安全工具类 +import com.yami.shop.service.PayService; // 引入支付服务类 +import io.swagger.v3.oas.annotations.tags.Tag; // 引入Swagger的Tag注解 +import io.swagger.v3.oas.annotations.Operation; // 引入Swagger的Operation注解 +import lombok.AllArgsConstructor; // 引入Lombok的@AllArgsConstructor注解 +import com.yami.shop.common.response.ServerResponseEntity; // 引入服务器响应实体类 +import org.springframework.web.bind.annotation.*; // 引入Spring Web的注解 + +/** + * PayController类,用于处理支付相关的操作。 + * @作者 lanhai + */ +@RestController // 标注这是一个控制器类,并且其返回结果直接写入HTTP响应体中,而不是视图名称 +@RequestMapping("/p/order") // 定义请求路径的根地址为/p/order +@Tag(name = "订单接口") // 给API文档添加标签,描述这个控制器的功能 +@AllArgsConstructor // 使用Lombok注解生成全参构造函数 +public class PayController { + + private final PayService payService; // 自动注入支付服务类 + + /** + * 支付接口 + * @param payParam 支付参数 + * @return 服务器响应实体 + */ + @PostMapping("/pay") + @Operation(summary = "根据订单号进行支付", description = "根据订单号进行支付") + public ServerResponseEntity pay(@RequestBody PayParam payParam) { + YamiUser user = SecurityUtils.getUser(); + String userId = user.getUserId(); + + PayInfoDto payInfo = payService.pay(userId, payParam); // 调用支付服务进行支付 + payService.paySuccess(payInfo.getPayNo(), ""); // 调用支付成功处理 + return ServerResponseEntity.success(); // 返回成功响应 + } + + /** + * 普通支付接口 + * @param payParam 支付参数 + * @return 服务器响应实体 + */ + @PostMapping("/normalPay") + @Operation(summary = "根据订单号进行支付", description = "根据订单号进行支付") + public ServerResponseEntity normalPay(@RequestBody PayParam payParam) { + YamiUser user = SecurityUtils.getUser(); + String userId = user.getUserId(); + PayInfoDto pay = payService.pay(userId, payParam); // 调用支付服务进行支付 + + // 根据内部订单号更新订单结算信息 + payService.paySuccess(pay.getPayNo(), ""); + + return ServerResponseEntity.success(true); // 返回成功响应 + } +} diff --git a/yami-shop-api/src/main/java/com/yami/shop/api/controller/PayNoticeController.java b/yami-shop-api/src/main/java/com/yami/shop/api/controller/PayNoticeController.java new file mode 100644 index 0000000..6e6542d --- /dev/null +++ b/yami-shop-api/src/main/java/com/yami/shop/api/controller/PayNoticeController.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.api.controller; // 定义类所在的包 + +import lombok.AllArgsConstructor; // 引入Lombok的@AllArgsConstructor注解 +import org.springframework.web.bind.annotation.RequestMapping; // 引入Spring的@RequestMapping注解 +import org.springframework.web.bind.annotation.RestController; // 引入Spring的@RestController注解 +import io.swagger.v3.oas.annotations.Hidden; // 引入Swagger的Hidden注解 + +/** + * PayNoticeController类,用于处理支付通知。 + * @作者 lanhai + */ +@Hidden // 隐藏这个控制器,不在Swagger文档中展示 +@RestController // 标注这是一个控制器类,并且其返回结果直接写入HTTP响应体中,而不是视图名称 +@RequestMapping("/notice/pay") // 定义请求路径的根地址为/notice/pay +@AllArgsConstructor // 使用Lombok注解生成全参构造函数 +public class PayNoticeController { +// 模拟支付不需要回调 +// /** +// * 小程序支付 +// */ +// private final WxPayService wxMiniPayService; +// +// private final PayService payService; +// +// @RequestMapping("/order") +// public ServerResponseEntity submit(@RequestBody String xmlData) throws WxPayException { +// WxPayOrderNotifyResult parseOrderNotifyResult = wxMiniPayService.parseOrderNotifyResult(xmlData); +// +// String payNo = parseOrderNotifyResult.getOutTradeNo(); +// String bizPayNo = parseOrderNotifyResult.getTransactionId(); +// +// // 根据内部订单号更新order settlement +// payService.paySuccess(payNo, bizPayNo); +// +// return ServerResponseEntity.success(); +// } +} diff --git a/yami-shop-api/src/main/java/com/yami/shop/api/listener/ConfirmOrderListener.java b/yami-shop-api/src/main/java/com/yami/shop/api/listener/ConfirmOrderListener.java new file mode 100644 index 0000000..f097361 --- /dev/null +++ b/yami-shop-api/src/main/java/com/yami/shop/api/listener/ConfirmOrderListener.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.api.listener; // 定义类所在的包 + +import com.yami.shop.bean.app.dto.ShopCartItemDto; // 引入购物车项目DTO +import com.yami.shop.bean.app.dto.ShopCartOrderDto; // 引入购物车订单DTO +import com.yami.shop.bean.app.param.OrderParam; // 引入订单参数 +import com.yami.shop.bean.event.ConfirmOrderEvent; // 引入确认订单事件 +import com.yami.shop.bean.model.Product; // 引入商品模型 +import com.yami.shop.bean.model.Sku; // 引入SKU模型 +import com.yami.shop.bean.model.UserAddr; // 引入用户地址模型 +import com.yami.shop.bean.order.ConfirmOrderOrder; // 引入确认订单顺序 +import com.yami.shop.common.exception.YamiShopBindException; // 引入自定义异常类 +import com.yami.shop.common.util.Arith; // 引入算术工具类 +import com.yami.shop.security.api.util.SecurityUtils; // 引入安全工具类 +import com.yami.shop.service.ProductService; // 引入商品服务类 +import com.yami.shop.service.SkuService; // 引入SKU服务类 +import com.yami.shop.service.TransportManagerService; // 引入运输管理服务类 +import com.yami.shop.service.UserAddrService; // 引入用户地址服务类 +import lombok.AllArgsConstructor; // 引入Lombok的@AllArgsConstructor注解 +import org.springframework.context.event.EventListener; // 引入Spring的事件监听注解 +import org.springframework.core.annotation.Order; // 引入Spring的Order注解 +import org.springframework.stereotype.Component; // 引入Spring的Component注解 + +/** + * 确认订单信息时的默认操作 + * ConfirmOrderListener类处理确认订单时的默认操作,包括计算订单金额、商品检查和运费计算等。 + * @作者 LGH + */ +@Component("defaultConfirmOrderListener") // 标注这是一个Spring组件,并且以defaultConfirmOrderListener为组件名称 +@AllArgsConstructor // 使用Lombok注解生成全参构造函数 +public class ConfirmOrderListener { + + private final UserAddrService userAddrService; // 自动注入用户地址服务类 + + private final TransportManagerService transportManagerService; // 自动注入运输管理服务类 + + private final ProductService productService; // 自动注入商品服务类 + + private final SkuService skuService; // 自动注入SKU服务类 + + /** + * 计算订单金额 + * @param event 确认订单事件 + */ + @EventListener(ConfirmOrderEvent.class) // 标注这是一个事件监听器,监听ConfirmOrderEvent事件 + @Order(ConfirmOrderOrder.DEFAULT) // 设置事件监听的顺序 + public void defaultConfirmOrderEvent(ConfirmOrderEvent event) { + + ShopCartOrderDto shopCartOrderDto = event.getShopCartOrderDto(); // 获取购物车订单DTO + + OrderParam orderParam = event.getOrderParam(); // 获取订单参数 + + String userId = SecurityUtils.getUser().getUserId(); // 获取当前用户ID + + // 订单的地址信息 + UserAddr userAddr = userAddrService.getUserAddrByUserId(orderParam.getAddrId(), userId); + + double total = 0.0; // 总金额 + + int totalCount = 0; // 总商品数量 + + double transfee = 0.0; // 总运费 + + for (ShopCartItemDto shopCartItem : event.getShopCartItems()) { + // 获取商品信息 + Product product = productService.getProductByProdId(shopCartItem.getProdId()); + // 获取sku信息 + Sku sku = skuService.getSkuBySkuId(shopCartItem.getSkuId()); + if (product == null || sku == null) { + throw new YamiShopBindException("购物车包含无法识别的商品"); // 商品或SKU为空,抛出异常 + } + if (product.getStatus() != 1 || sku.getStatus() != 1) { + throw new YamiShopBindException("商品[" + sku.getProdName() + "]已下架"); // 商品或SKU已下架,抛出异常 + } + + totalCount = shopCartItem.getProdCount() + totalCount; // 累加商品数量 + total = Arith.add(shopCartItem.getProductTotalAmount(), total); // 累加商品总金额 + // 用户地址如果为空,则表示该用户从未设置过任何地址相关信息 + if (userAddr != null) { + // 每个产品的运费相加 + transfee = Arith.add(transfee, transportManagerService.calculateTransfee(shopCartItem, userAddr)); + } + + shopCartItem.setActualTotal(shopCartItem.getProductTotalAmount()); // 设置实际总金额 + shopCartOrderDto.setActualTotal(Arith.add(total, transfee)); // 设置订单实际总金额 + shopCartOrderDto.setTotal(total); // 设置订单总金额 + shopCartOrderDto.setTotalCount(totalCount); // 设置订单总商品数量 + shopCartOrderDto.setTransfee(transfee); // 设置订单总运费 + } + } +} diff --git a/yami-shop-api/src/main/java/com/yami/shop/api/listener/SubmitOrderListener.java b/yami-shop-api/src/main/java/com/yami/shop/api/listener/SubmitOrderListener.java new file mode 100644 index 0000000..8ada33e --- /dev/null +++ b/yami-shop-api/src/main/java/com/yami/shop/api/listener/SubmitOrderListener.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.api.listener; // 定义类所在的包 + +import cn.hutool.core.lang.Snowflake; // 引入Hutool工具类库中的Snowflake工具类 +import cn.hutool.core.util.StrUtil; // 引入Hutool工具类库中的StrUtil工具类 +import com.yami.shop.bean.app.dto.*; // 引入各种DTO类 +import com.yami.shop.bean.enums.OrderStatus; // 引入订单状态枚举类 +import com.yami.shop.bean.event.SubmitOrderEvent; // 引入提交订单事件类 +import com.yami.shop.bean.model.*; // 引入各种模型类 +import com.yami.shop.bean.order.SubmitOrderOrder; // 引入提交订单顺序类 +import com.yami.shop.common.constants.Constant; // 引入常量类 +import com.yami.shop.common.exception.YamiShopBindException; // 引入自定义异常类 +import com.yami.shop.common.util.Arith; // 引入算术工具类 +import com.yami.shop.dao.*; // 引入各种数据访问对象 +import com.yami.shop.security.api.util.SecurityUtils; // 引入安全工具类 +import com.yami.shop.service.*; // 引入各种服务类 +import lombok.AllArgsConstructor; // 引入Lombok的@AllArgsConstructor注解 +import cn.hutool.core.bean.BeanUtil; // 引入Hutool工具类库中的BeanUtil工具类 +import org.springframework.context.event.EventListener; // 引入Spring的EventListener注解 +import org.springframework.core.annotation.Order; // 引入Spring的Order注解 +import org.springframework.stereotype.Component; // 引入Spring的Component注解 + +import java.util.*; // 引入Java的各种集合类 + +/** + * 确认订单信息时的默认操作 + * 该类包含处理提交订单事件的逻辑,包括订单的创建和库存的更新。 + * @作者 LGH + */ +@Component("defaultSubmitOrderListener") // 标注这是一个Spring组件,并且以defaultSubmitOrderListener为组件名称 +@AllArgsConstructor // 使用Lombok注解生成全参构造函数 +public class SubmitOrderListener { + + private final UserAddrOrderService userAddrOrderService; // 自动注入用户地址订单服务类 + private final ProductService productService; // 自动注入商品服务类 + private final SkuService skuService; // 自动注入SKU服务类 + private final Snowflake snowflake; // 自动注入雪花算法实例 + private final OrderItemMapper orderItemMapper; // 自动注入订单项数据访问对象 + private final SkuMapper skuMapper; // 自动注入SKU数据访问对象 + private final ProductMapper productMapper; // 自动注入商品数据访问对象 + private final OrderMapper orderMapper; // 自动注入订单数据访问对象 + private final OrderSettlementMapper orderSettlementMapper; // 自动注入订单结算数据访问对象 + private final BasketMapper basketMapper; // 自动注入购物车数据访问对象 + + /** + * 计算订单金额 + * @param event 提交订单事件 + */ + @EventListener(SubmitOrderEvent.class) + @Order(SubmitOrderOrder.DEFAULT) + public void defaultSubmitOrderListener(SubmitOrderEvent event) { + Date now = new Date(); + String userId = SecurityUtils.getUser().getUserId(); + + ShopCartOrderMergerDto mergerOrder = event.getMergerOrder(); + + // 订单商品参数 + List shopCartOrders = mergerOrder.getShopCartOrders(); + + List basketIds = new ArrayList<>(); + // 商品skuId为key,需要更新的sku为value的map + Map skuStocksMap = new HashMap<>(16); + // 商品productId为key,需要更新的product为value的map + Map prodStocksMap = new HashMap<>(16); + + // 把订单地址保存到数据库 + UserAddrOrder userAddrOrder = BeanUtil.copyProperties(mergerOrder.getUserAddr(), UserAddrOrder.class); + if (userAddrOrder == null) { + throw new YamiShopBindException("请填写收货地址"); + } + userAddrOrder.setUserId(userId); + userAddrOrder.setCreateTime(now); + userAddrOrderService.save(userAddrOrder); + + // 订单地址id + Long addrOrderId = userAddrOrder.getAddrOrderId(); + + // 每个店铺生成一个订单 + for (ShopCartOrderDto shopCartOrderDto : shopCartOrders) { + createOrder(event, now, userId, basketIds, skuStocksMap, prodStocksMap, addrOrderId, shopCartOrderDto); + } + + // 删除购物车的商品信息 + if (!basketIds.isEmpty()) { + basketMapper.deleteShopCartItemsByBasketIds(userId, basketIds); + } + + // 更新sku库存 + skuStocksMap.forEach((key, sku) -> { + if (skuMapper.updateStocks(sku) == 0) { + skuService.removeSkuCacheBySkuId(key, sku.getProdId()); + throw new YamiShopBindException("商品:[" + sku.getProdName() + "]库存不足"); + } + }); + + // 更新商品库存 + prodStocksMap.forEach((prodId, prod) -> { + if (productMapper.updateStocks(prod) == 0) { + productService.removeProductCacheByProdId(prodId); + throw new YamiShopBindException("商品:[" + prod.getProdName() + "]库存不足"); + } + }); + } + + private void createOrder(SubmitOrderEvent event, Date now, String userId, List basketIds, Map skuStocksMap, Map prodStocksMap, Long addrOrderId, ShopCartOrderDto shopCartOrderDto) { + // 使用雪花算法生成的订单号 + String orderNumber = String.valueOf(snowflake.nextId()); + shopCartOrderDto.setOrderNumber(orderNumber); + + Long shopId = shopCartOrderDto.getShopId(); + + // 订单商品名称 + StringBuilder orderProdName = new StringBuilder(100); + + List orderItems = new ArrayList<>(); + + List shopCartItemDiscounts = shopCartOrderDto.getShopCartItemDiscounts(); + for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartItemDiscounts) { + List shopCartItems = shopCartItemDiscount.getShopCartItems(); + for (ShopCartItemDto shopCartItem : shopCartItems) { + Sku sku = checkAndGetSku(shopCartItem.getSkuId(), shopCartItem, skuStocksMap); + Product product = checkAndGetProd(shopCartItem.getProdId(), shopCartItem, prodStocksMap); + + OrderItem orderItem = getOrderItem(now, userId, orderNumber, shopId, orderProdName, shopCartItem, sku, product); + + orderItems.add(orderItem); + + if (shopCartItem.getBasketId() != null && shopCartItem.getBasketId() != 0) { + basketIds.add(shopCartItem.getBasketId()); + } + } + } + + orderProdName.subSequence(0, Math.min(orderProdName.length() - 1, 100)); + if (orderProdName.lastIndexOf(Constant.COMMA) == orderProdName.length() - 1) { + orderProdName.deleteCharAt(orderProdName.length() - 1); + } + + // 订单信息 + com.yami.shop.bean.model.Order order = getOrder(now, userId, addrOrderId, shopCartOrderDto, orderNumber, shopId, orderProdName, orderItems); + event.getOrders().add(order); + // 插入订单结算表 + OrderSettlement orderSettlement = new OrderSettlement(); + orderSettlement.setUserId(userId); + orderSettlement.setIsClearing(0); + orderSettlement.setCreateTime(now); + orderSettlement.setOrderNumber(orderNumber); + orderSettlement.setPayAmount(order.getActualTotal()); + orderSettlement.setPayStatus(0); + orderSettlement.setVersion(0); + orderSettlementMapper.insert(orderSettlement); + } + + private com.yami.shop.bean.model.Order getOrder(Date now, String userId, Long addrOrderId, ShopCartOrderDto shopCartOrderDto, String orderNumber, Long shopId, StringBuilder orderProdName, List orderItems) { + com.yami.shop.bean.model.Order order = new com.yami.shop.bean.model.Order(); + + order.setShopId(shopId); + order.setOrderNumber(orderNumber); + order.setProdName(orderProdName.toString()); // 订单商品名称 + order.setUserId(userId); // 用户ID + order.setTotal(shopCartOrderDto.getTotal()); // 商品总额 + order.setActualTotal(shopCartOrderDto.getActualTotal()); // 实际总额 + order.setStatus(OrderStatus.UNPAY.value()); // 订单状态为未支付 + order.setUpdateTime(now); + order.setCreateTime(now); + order.setIsPayed(0); + order.setDeleteStatus(0); + order.setProductNums(shopCartOrderDto.getTotalCount()); + order.setAddrOrderId(addrOrderId); + order.setReduceAmount(Arith.sub(Arith.add(shopCartOrderDto.getTotal(), shopCartOrderDto.getTransfee()), shopCartOrderDto.getActualTotal())); + order.setFreightAmount(shopCartOrderDto.getTransfee()); + order.setRemarks(shopCartOrderDto.getRemarks()); + + order.setOrderItems(orderItems); + return order; + } + + private OrderItem getOrderItem(Date now, String userId, String orderNumber, Long shopId, StringBuilder orderProdName, ShopCartItemDto shopCartItem, Sku sku, Product product) { + OrderItem orderItem = new OrderItem(); + orderItem.setShopId(shopId); + orderItem.setOrderNumber(orderNumber); + orderItem.setProdId(sku.getProdId()); + orderItem.setSkuId(sku.getSkuId()); + orderItem.setSkuName(sku.getSkuName()); + orderItem.setProdCount(shopCartItem.getProdCount()); + orderItem.setProdName(sku.getProdName()); + orderItem.setPic(StrUtil.isBlank(sku.getPic()) ? product.getPic() : sku.getPic()); + orderItem.setPrice(shopCartItem.getPrice()); + orderItem.setUserId(userId); + orderItem.setProductTotalAmount(shopCartItem.getProductTotalAmount()); + orderItem.setRecTime(now); + orderItem.setCommSts(0); + orderItem.setBasketDate(shopCartItem.getBasketDate()); + orderProdName.append(orderItem.getProdName()).append(","); + //推广员卡号 + orderItem.setDistributionCardNo(shopCartItem.getDistributionCardNo()); + return orderItem; + } + + @SuppressWarnings({"Duplicates"}) + private Product checkAndGetProd(Long prodId, ShopCartItemDto shopCartItem, Map prodStocksMap) { + Product product = productService.getProductByProdId(prodId); + if (product == null) { + throw new YamiShopBindException("购物车包含无法识别的商品"); + } + + if (product.getStatus() != 1) { + throw new YamiShopBindException("商品[" + product.getProdName() + "]已下架"); + } + + // 商品需要改变的库存 + Product mapProduct = prodStocksMap.get(prodId); + + if (mapProduct == null) { + mapProduct = new Product(); + mapProduct.setTotalStocks(0); + mapProduct.setProdId(prodId); + mapProduct.setProdName(product.getProdName()); + } + + if (product.getTotalStocks() != -1) { + mapProduct.setTotalStocks(mapProduct.getTotalStocks() + shopCartItem.getProdCount()); + prodStocksMap.put(product.getProdId(), mapProduct); + } + + // -1为无限库存 + if (product.getTotalStocks() != -1 && mapProduct.getTotalStocks() > product.getTotalStocks()) { + throw new YamiShopBindException("商品:[" + product.getProdName() + "]库存不足"); + } + + return product; + } + + @SuppressWarnings({"Duplicates"}) + private Sku checkAndGetSku(Long skuId, ShopCartItemDto shopCartItem, Map skuStocksMap) { + // 获取sku信息 + Sku sku = skuService.getSkuBySkuId(skuId); + if (sku == null) { + throw new YamiShopBindException("购物车包含无法识别的商品"); + } + + if (sku.getStatus() != 1) { + throw new YamiShopBindException("商品[" + sku.getProdName() + "]已下架"); + } + // -1为无限库存 + if (sku.getStocks() != -1 && shopCartItem.getProdCount() > sku.getStocks()) { + throw new YamiShopBindException("商品:[" + sku.getProdName() + "]库存不足"); + } + + if (sku.getStocks() != -1) { + Sku mapSku = new Sku(); + mapSku.setProdId(sku.getProdId()); + // 这里的库存是改变的库存 + mapSku.setStocks(shopCartItem.getProdCount()); + mapSku.setSkuId(sku.getSkuId()); + mapSku.setProdName(sku.getProdName()); + skuStocksMap.put(sku.getSkuId(), mapSku); + } + return sku; + } +} diff --git a/yami-shop-security/yami-shop-security-common/src/main/resources/captcha/original/1.png b/yami-shop-security/yami-shop-security-common/src/main/resources/captcha/original/1.png new file mode 100644 index 0000000..51573a0 Binary files /dev/null and b/yami-shop-security/yami-shop-security-common/src/main/resources/captcha/original/1.png differ