diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..7b016a89 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.compile.nullAnalysis.mode": "automatic" +} \ No newline at end of file diff --git a/hiver-admin/test-output/test-report.html b/hiver-admin/test-output/test-report.html index a00a53b3..ce3d4dff 100644 --- a/hiver-admin/test-output/test-report.html +++ b/hiver-admin/test-output/test-report.html @@ -35,7 +35,7 @@ Hiver
  • -五月 13, 2026 18:10:53 +五月 16, 2026 17:37:44
  • @@ -84,7 +84,7 @@

    passTest

    -

    18:10:53 下午 / 0.017 secs

    +

    17:37:45 下午 / 0.018 secs

    @@ -92,9 +92,9 @@
    #test-id=1
    passTest
    -05.13.2026 18:10:53 -05.13.2026 18:10:53 -0.017 secs +05.16.2026 17:37:45 +05.16.2026 17:37:45 +0.018 secs
    @@ -104,7 +104,7 @@ Pass - 18:10:53 + 17:37:45 Test passed @@ -128,13 +128,13 @@

    Started

    -

    五月 13, 2026 18:10:53

    +

    五月 16, 2026 17:37:44

    Ended

    -

    五月 13, 2026 18:10:53

    +

    五月 16, 2026 17:37:45

    diff --git a/hiver-modules/hiver-base/src/main/java/cc/hiver/base/controller/manage/AuthController.java b/hiver-modules/hiver-base/src/main/java/cc/hiver/base/controller/manage/AuthController.java index ecb2696c..4f148dbb 100644 --- a/hiver-modules/hiver-base/src/main/java/cc/hiver/base/controller/manage/AuthController.java +++ b/hiver-modules/hiver-base/src/main/java/cc/hiver/base/controller/manage/AuthController.java @@ -430,6 +430,7 @@ public class AuthController { shop.setShopLecard(registerShopVo.getShopLecard()); shop.setAliAccount(registerShopVo.getAliAccount()); shop.setAliName(registerShopVo.getAliName()); + shop.setMerchantType(registerShopVo.getMerchantType()); shop.setRegion(registerShopVo.getRegion()); shop.setRegionId(registerShopVo.getRegionId()); shop.setIsStudent(registerShopVo.getIsStudent()); diff --git a/hiver-modules/hiver-base/src/main/java/cc/hiver/base/vo/RegisterShopVo.java b/hiver-modules/hiver-base/src/main/java/cc/hiver/base/vo/RegisterShopVo.java index 02919aad..086871f0 100644 --- a/hiver-modules/hiver-base/src/main/java/cc/hiver/base/vo/RegisterShopVo.java +++ b/hiver-modules/hiver-base/src/main/java/cc/hiver/base/vo/RegisterShopVo.java @@ -17,6 +17,8 @@ public class RegisterShopVo { private Integer isStudent; + private Integer merchantType; + private String unionid; private String password; diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/AdminSeckillGroupController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/AdminSeckillGroupController.java new file mode 100644 index 00000000..bb94c05a --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/AdminSeckillGroupController.java @@ -0,0 +1,216 @@ +package cc.hiver.mall.controller; + +import cc.hiver.core.common.utils.ResultUtil; +import cc.hiver.core.common.vo.Result; +import cc.hiver.mall.entity.SeckillGroupCategory; +import cc.hiver.mall.entity.SeckillGroupProduct; +import cc.hiver.mall.pojo.dto.SeckillGroupProductDTO; +import cc.hiver.mall.pojo.query.SeckillGroupProductQuery; +import cc.hiver.mall.pojo.vo.SeckillGroupProductVO; +import cc.hiver.mall.service.mybatis.SeckillGroupCategoryService; +import cc.hiver.mall.service.mybatis.SeckillGroupProductService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Slf4j +@RestController +@Api(tags = "鍚庡彴绠$悊绯荤粺绉掓潃鍥㈡帴鍙") +@RequestMapping("/hiver/mall/admin/seckillGroup") +@Transactional +public class AdminSeckillGroupController { + + @Autowired + private SeckillGroupCategoryService seckillGroupCategoryService; + + @Autowired + private SeckillGroupProductService seckillGroupProductService; + + @RequestMapping(value = "/category/save", method = RequestMethod.POST) + @ApiOperation(value = "鏂板绉掓潃鍥㈠垎绫") + public Result saveCategory(@RequestBody SeckillGroupCategory category) { + if (category == null || StringUtils.isBlank(category.getCategoryName())) { + return ResultUtil.error("鍒嗙被鍚嶇О涓嶈兘涓虹┖"); + } + if (StringUtils.isBlank(category.getRegionId())) { + return ResultUtil.error("鍖哄煙ID涓嶈兘涓虹┖"); + } + category.setStatus(category.getStatus() == null ? 1 : category.getStatus()); + category.setSort(category.getSort() == null ? 0 : category.getSort()); + boolean result = seckillGroupCategoryService.save(category); + if (result) { + seckillGroupCategoryService.refreshCategoryCache(category.getRegionId()); + return ResultUtil.success("娣诲姞鎴愬姛"); + } + return ResultUtil.error("娣诲姞澶辫触"); + } + + @RequestMapping(value = "/category/edit", method = RequestMethod.POST) + @ApiOperation(value = "淇敼绉掓潃鍥㈠垎绫") + public Result editCategory(@RequestBody SeckillGroupCategory category) { + if (category == null || StringUtils.isBlank(category.getId())) { + return ResultUtil.error("鍒嗙被ID涓嶈兘涓虹┖"); + } + SeckillGroupCategory old = seckillGroupCategoryService.getById(category.getId()); + if (old == null) { + return ResultUtil.error("鍒嗙被涓嶅瓨鍦"); + } + boolean result = seckillGroupCategoryService.updateById(category); + if (result) { + seckillGroupCategoryService.refreshCategoryCache(old.getRegionId()); + if (StringUtils.isNotBlank(category.getRegionId()) && !category.getRegionId().equals(old.getRegionId())) { + seckillGroupCategoryService.refreshCategoryCache(category.getRegionId()); + } + return ResultUtil.success("淇敼鎴愬姛"); + } + return ResultUtil.error("淇敼澶辫触"); + } + + @RequestMapping(value = "/category/delete", method = RequestMethod.POST) + @ApiOperation(value = "鍒犻櫎绉掓潃鍥㈠垎绫") + public Result deleteCategory(String id) { + if (StringUtils.isBlank(id)) { + return ResultUtil.error("鍒嗙被ID涓嶈兘涓虹┖"); + } + SeckillGroupCategory category = seckillGroupCategoryService.getById(id); + if (category == null) { + return ResultUtil.error("鍒嗙被涓嶅瓨鍦"); + } + category.setDelFlag(1); + boolean result = seckillGroupCategoryService.updateById(category); + if (result) { + seckillGroupCategoryService.removeCategoryCache(category.getRegionId(), id); + seckillGroupCategoryService.refreshCategoryCache(category.getRegionId()); + seckillGroupProductService.removeProductCache(id); + return ResultUtil.success("鍒犻櫎鎴愬姛"); + } + return ResultUtil.error("鍒犻櫎澶辫触"); + } + + @RequestMapping(value = "/category/list", method = RequestMethod.GET) + @ApiOperation(value = "鏌ヨ绉掓潃鍥㈠垎绫诲垪琛") + public Result> listCategory(@RequestParam(value = "regionId") String regionId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SeckillGroupCategory::getDelFlag, 0); + if (StringUtils.isNotBlank(regionId)) { + wrapper.eq(SeckillGroupCategory::getRegionId, regionId); + } + wrapper.orderByDesc(SeckillGroupCategory::getSort) + .orderByAsc(SeckillGroupCategory::getCreateTime); + return new ResultUtil>().setData(seckillGroupCategoryService.list(wrapper)); + } + + @RequestMapping(value = "/category/sort", method = RequestMethod.POST) + @ApiOperation(value = "鎵归噺璋冩暣绉掓潃鍥㈠垎绫绘帓搴") + public Result sortCategory(@RequestBody List categories) { + if (categories == null || categories.isEmpty()) { + return ResultUtil.error("鍒嗙被鍒楄〃涓嶈兘涓虹┖"); + } + boolean result = seckillGroupCategoryService.updateBatchById(categories); + if (result) { + for (SeckillGroupCategory category : categories) { + if (StringUtils.isNotBlank(category.getRegionId())) { + seckillGroupCategoryService.refreshCategoryCache(category.getRegionId()); + } else if (StringUtils.isNotBlank(category.getId())) { + SeckillGroupCategory old = seckillGroupCategoryService.getById(category.getId()); + if (old != null) { + seckillGroupCategoryService.refreshCategoryCache(old.getRegionId()); + } + } + } + return ResultUtil.success("鎺掑簭鎴愬姛"); + } + return ResultUtil.error("鎺掑簭澶辫触"); + } + + @RequestMapping(value = "/product/save", method = RequestMethod.POST) + @ApiOperation(value = "鏂板绉掓潃鍥㈠晢鍝") + public Result saveProduct(@RequestBody SeckillGroupProductDTO dto) { + try { + return new ResultUtil().setData(seckillGroupProductService.saveFromProduct(dto)); + } catch (IllegalArgumentException e) { + return ResultUtil.error(e.getMessage()); + } + } + + @RequestMapping(value = "/product/edit", method = RequestMethod.POST) + @ApiOperation(value = "淇敼绉掓潃鍥㈠晢鍝") + public Result editProduct(@RequestBody SeckillGroupProductDTO dto) { + try { + return new ResultUtil().setData(seckillGroupProductService.editSeckillProduct(dto)); + } catch (IllegalArgumentException e) { + return ResultUtil.error(e.getMessage()); + } + } + + @RequestMapping(value = "/product/page", method = RequestMethod.POST) + @ApiOperation(value = "鍒嗛〉鏌ヨ绉掓潃鍥㈠晢鍝") + public Result> pageProduct(@RequestBody SeckillGroupProductQuery query) { + if (query == null) { + query = new SeckillGroupProductQuery(); + } + return new ResultUtil>().setData(seckillGroupProductService.pageProducts(query)); + } + + @RequestMapping(value = "/product/up", method = RequestMethod.POST) + @ApiOperation(value = "涓婃灦绉掓潃鍥㈠晢鍝") + public Result upProduct(String id) { + return updateProductStatus(id, 1, "涓婃灦"); + } + + @RequestMapping(value = "/product/down", method = RequestMethod.POST) + @ApiOperation(value = "涓嬫灦绉掓潃鍥㈠晢鍝") + public Result downProduct(String id) { + return updateProductStatus(id, 0, "涓嬫灦"); + } + + @RequestMapping(value = "/product/delete", method = RequestMethod.POST) + @ApiOperation(value = "鍒犻櫎绉掓潃鍥㈠晢鍝") + public Result deleteProduct(String id) { + if (StringUtils.isBlank(id)) { + return ResultUtil.error("鍟嗗搧ID涓嶈兘涓虹┖"); + } + SeckillGroupProduct product = seckillGroupProductService.getById(id); + if (product == null) { + return ResultUtil.error("鍟嗗搧涓嶅瓨鍦"); + } + product.setDelFlag(1); + boolean result = seckillGroupProductService.updateById(product); + if (result) { + seckillGroupProductService.refreshProductCache(product.getCategoryId()); + seckillGroupProductService.removeProductDetailCache(id); + return ResultUtil.success("鍒犻櫎鎴愬姛"); + } + return ResultUtil.error("鍒犻櫎澶辫触"); + } + + private Result updateProductStatus(String id, Integer status, String actionName) { + if (StringUtils.isBlank(id)) { + return ResultUtil.error("鍟嗗搧ID涓嶈兘涓虹┖"); + } + SeckillGroupProduct product = seckillGroupProductService.getById(id); + if (product == null) { + return ResultUtil.error("鍟嗗搧涓嶅瓨鍦"); + } + product.setStatus(status); + boolean result = seckillGroupProductService.updateById(product); + if (result) { + if (status == 1) { + seckillGroupProductService.refreshProductCacheByProductId(id); + } else { + seckillGroupProductService.refreshProductCache(product.getCategoryId()); + seckillGroupProductService.removeProductDetailCache(id); + } + return ResultUtil.success(actionName + "鎴愬姛"); + } + return ResultUtil.error(actionName + "澶辫触"); + } +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/SeckillGroupController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/SeckillGroupController.java new file mode 100644 index 00000000..15be2f31 --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/SeckillGroupController.java @@ -0,0 +1,66 @@ +package cc.hiver.mall.controller; + +import cc.hiver.core.common.annotation.RateLimiter; +import cc.hiver.core.common.utils.ResultUtil; +import cc.hiver.core.common.vo.Result; +import cc.hiver.mall.entity.SeckillGroupCategory; +import cc.hiver.mall.pojo.query.SeckillGroupProductQuery; +import cc.hiver.mall.pojo.vo.SeckillGroupProductVO; +import cc.hiver.mall.service.mybatis.SeckillGroupCategoryService; +import cc.hiver.mall.service.mybatis.SeckillGroupProductService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@Api(tags = "绉掓潃鍥㈡帴鍙") +@RequestMapping("/hiver/app/seckillGroup") +public class SeckillGroupController { + + @Autowired + private SeckillGroupCategoryService seckillGroupCategoryService; + + @Autowired + private SeckillGroupProductService seckillGroupProductService; + + @RequestMapping(value = "/category/list", method = RequestMethod.GET) + @ApiOperation(value = "鏌ヨ绉掓潃鍥㈡í鎺掑垎绫") + public Result> listCategory(String regionId) { + if (StringUtils.isBlank(regionId)) { + return ResultUtil.error("鍖哄煙ID涓嶈兘涓虹┖"); + } + return new ResultUtil>().setData(seckillGroupCategoryService.listFromCache(regionId)); + } + + @RequestMapping(value = "/product/page", method = RequestMethod.POST) + @ApiOperation(value = "鍒嗛〉鏌ヨ绉掓潃鍥㈠晢鍝") + @RateLimiter(name = "seckillGroupProductPage", ipLimit = true) + public Result> pageProduct(@RequestBody SeckillGroupProductQuery query) { + if (query == null) { + query = new SeckillGroupProductQuery(); + } + if (StringUtils.isBlank(query.getRegionId())) { + return ResultUtil.error("鍖哄煙ID涓嶈兘涓虹┖"); + } + query.setOnlyActive(true); + return new ResultUtil>().setData(seckillGroupProductService.appPageProducts(query)); + } + + @RequestMapping(value = "/product/detail/{id}", method = RequestMethod.GET) + @ApiOperation(value = "鏌ヨ绉掓潃鍥㈠晢鍝佽鎯") + public Result detail(@PathVariable("id") String id) { + if (StringUtils.isBlank(id)) { + return ResultUtil.error("鍟嗗搧ID涓嶈兘涓虹┖"); + } + SeckillGroupProductVO detail = seckillGroupProductService.getDetail(id); + if (detail == null) { + return ResultUtil.error("鍟嗗搧涓嶅瓨鍦"); + } + return new ResultUtil().setData(detail); + } +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/ShopController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/ShopController.java index 681dab27..a67ee230 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/ShopController.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/ShopController.java @@ -246,7 +246,17 @@ public class ShopController { } if (shop.getIsStudent() != null) { - if (!(shop.getIsStudent() == s.getIsStudent())) { + if (!shop.getIsStudent().equals(s.getIsStudent())) { + continue; + } + } + if (shop.getMerchantType() != null) { + Integer merchantType = s.getMerchantType(); + if (shop.getMerchantType().equals(1)) { + if (merchantType != null && !shop.getMerchantType().equals(merchantType)) { + continue; + } + } else if (!shop.getMerchantType().equals(merchantType)) { continue; } } @@ -427,7 +437,7 @@ public class ShopController { final Shop shop = shopService.get(id); shop.setStatus(ShopConstant.SHOP_STATUS_LOCK); shopService.update(shop); - shopService.removeShopCache(shop.getId(), shop.getRegionId()); + shopService.refreshShopCache(shop.getId(), shop.getRegionId()); return ResultUtil.success("鎿嶄綔鎴愬姛"); } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WechatPayController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WechatPayController.java index 563e4889..4f3eac27 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WechatPayController.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WechatPayController.java @@ -99,11 +99,16 @@ public class WechatPayController { String description = request.get("description"); String outTradeNo = request.get("outTradeNo"); String userRequireMake = request.get("userRequireMake"); - if(userRequireMake != null && userRequireMake.equals("1")){ + String isPack = request.get("isPack"); + if(isPack != null){ MallOrder order = mallOrderService.getById(outTradeNo); - order.setUserRequireMake(1); + order.setIsPack(Integer.valueOf(isPack)); + if(userRequireMake != null && userRequireMake.equals("1")){ + order.setUserRequireMake(1); + } mallOrderService.updateById(order); } + if(description.equals("澧炲姞閰嶉佷剑閲")){ outTradeNo = "ORDERDE_" + outTradeNo; }else{ diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/SeckillGroupCategoryMapper.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/SeckillGroupCategoryMapper.java new file mode 100644 index 00000000..95f9e13c --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/SeckillGroupCategoryMapper.java @@ -0,0 +1,9 @@ +package cc.hiver.mall.dao.mapper; + +import cc.hiver.mall.entity.SeckillGroupCategory; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.springframework.stereotype.Repository; + +@Repository +public interface SeckillGroupCategoryMapper extends BaseMapper { +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/SeckillGroupProductMapper.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/SeckillGroupProductMapper.java new file mode 100644 index 00000000..767d649e --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/SeckillGroupProductMapper.java @@ -0,0 +1,9 @@ +package cc.hiver.mall.dao.mapper; + +import cc.hiver.mall.entity.SeckillGroupProduct; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.springframework.stereotype.Repository; + +@Repository +public interface SeckillGroupProductMapper extends BaseMapper { +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrder.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrder.java index dbc49350..719e2c49 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrder.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrder.java @@ -84,6 +84,8 @@ public class MallOrder implements Serializable { private String regionId; @ApiModelProperty("鏄惁鍏嶅崟") private Integer isFreeOrder; + @ApiModelProperty("0 鍫傞 1 鎵撳寘") + private Integer isPack; @ApiModelProperty("鍏嶅崟閲戦") private BigDecimal freeAmount; diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/SeckillGroupCategory.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/SeckillGroupCategory.java new file mode 100644 index 00000000..ccb00070 --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/SeckillGroupCategory.java @@ -0,0 +1,32 @@ +package cc.hiver.mall.entity; + +import cc.hiver.core.base.HiverBaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel(value = "绉掓潃鍥㈡í鎺掑垎绫") +@TableName(value = "t_seckill_group_category", autoResultMap = true) +public class SeckillGroupCategory extends HiverBaseEntity { + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "鍒嗙被鍚嶇О") + private String categoryName; + + @ApiModelProperty(value = "鍒嗙被鍥炬爣") + private String categoryIcon; + + @ApiModelProperty(value = "鍖哄煙ID") + private String regionId; + + @ApiModelProperty(value = "鎺掑簭鍊硷紝瓒婂ぇ瓒婇潬鍓") + private Integer sort; + + @ApiModelProperty(value = "鐘舵侊細0绂佺敤 1鍚敤") + private Integer status; + + @ApiModelProperty(value = "澶囨敞") + private String remark; +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/SeckillGroupProduct.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/SeckillGroupProduct.java new file mode 100644 index 00000000..07629242 --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/SeckillGroupProduct.java @@ -0,0 +1,95 @@ +package cc.hiver.mall.entity; + +import cc.hiver.core.base.HiverBaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@ApiModel(value = "绉掓潃鍥㈠晢鍝") +@TableName(value = "t_seckill_group_product", autoResultMap = true) +public class SeckillGroupProduct extends HiverBaseEntity { + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "绉掓潃鍒嗙被ID") + private String categoryId; + + @ApiModelProperty(value = "鍘熷晢鍝両D") + private String productId; + + @ApiModelProperty(value = "鍟嗗搧鍚嶇О蹇収") + private String productName; + + @ApiModelProperty(value = "鍟嗗搧鍥剧墖蹇収") + private String productPicture; + + @ApiModelProperty(value = "鍗曚綅蹇収") + private String unit; + + @ApiModelProperty(value = "鍟嗗搧灞炴у垪琛/瑙勬牸蹇収") + private String attributeList; + + @ApiModelProperty(value = "鍟嗗搧绠浠嬪揩鐓") + private String productIntro; + + @ApiModelProperty(value = "鍘熶环/鍒掔嚎浠峰揩鐓") + private BigDecimal originalPrice; + + @ApiModelProperty(value = "绉掓潃浠锋牸") + private BigDecimal seckillPrice; + + @ApiModelProperty(value = "绉掓潃鎬诲簱瀛") + private Integer totalStock; + + @ApiModelProperty(value = "宸插敭搴撳瓨") + private Integer soldStock; + + @ApiModelProperty(value = "閿佸畾搴撳瓨") + private Integer lockStock; + + @ApiModelProperty(value = "姣忎汉闄愯喘鏁伴噺锛岀┖琛ㄧず涓嶉檺") + private Integer limitNum; + + @ApiModelProperty(value = "鍟嗗ID") + private String shopId; + + @ApiModelProperty(value = "鍟嗗鍚嶇О蹇収") + private String shopName; + + @ApiModelProperty(value = "鍟嗗鐢佃瘽蹇収") + private String shopPhone; + + @ApiModelProperty(value = "鍟嗗鍦板潃蹇収") + private String shopAddress; + + @ApiModelProperty(value = "鍙栬揣鍖哄煙ID锛屼究浜庝笅鍗曚紶鍙") + private String getAreaId; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "绉掓潃寮濮嬫椂闂") + private Date startTime; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "绉掓潃缁撴潫鏃堕棿") + private Date endTime; + + @ApiModelProperty(value = "浜哄伐鎺掑簭鍊硷紝瓒婂ぇ瓒婇潬鍓") + private Integer orderFiled; + + @ApiModelProperty(value = "绋冲畾闅忔満鎺掑簭鍊") + private Long sortHash; + + @ApiModelProperty(value = "鐘舵侊細0涓嬫灦 1涓婃灦") + private Integer status; + + @ApiModelProperty(value = "澶囨敞") + private String remark; +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/Shop.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/Shop.java index a7d58e52..32f00fd6 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/Shop.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/Shop.java @@ -175,6 +175,9 @@ public class Shop extends HiverBaseEntity { @ApiModelProperty(value = "鏄惁涓哄鐢熷晢瀹 0 涓嶆槸 1鏄") private Integer isStudent; + @ApiModelProperty(value = "1澶栧崠鍟嗗 / 2鍥㈣喘鍟嗗") + private Integer merchantType; + @Transient @TableField(exist = false) @ApiModelProperty(value = "搴楅摵鎶戒剑绛夎缃") @@ -184,4 +187,9 @@ public class Shop extends HiverBaseEntity { @TableField(exist = false) @ApiModelProperty(value = "妯$硦鎼滅储鏉′欢") private String keyWord; + + @Transient + @TableField(exist = false) + @ApiModelProperty(value = "鏄惁鍚庡彴鏌ヨ") + private Integer isAdmin; } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/CreateOrderDTO.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/CreateOrderDTO.java index 5e213796..6940972f 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/CreateOrderDTO.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/CreateOrderDTO.java @@ -42,6 +42,9 @@ public class CreateOrderDTO { @ApiModelProperty(value = "閫佽揣鍖哄煙ID(鐢ㄦ埛鏀惰揣鍦板潃鍏宠仈鍖哄煙id)") private String putAreaId; + @ApiModelProperty("0 鍫傞 1 鎵撳寘") + private Integer isPack; + @ApiModelProperty(value = "鎵撳寘璐癸紙榛樿0锛") private BigDecimal packageFee; diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/SeckillGroupProductDTO.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/SeckillGroupProductDTO.java new file mode 100644 index 00000000..cb4807f3 --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/SeckillGroupProductDTO.java @@ -0,0 +1,55 @@ +package cc.hiver.mall.pojo.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@ApiModel("绉掓潃鍥㈠晢鍝佷繚瀛樺璞") +public class SeckillGroupProductDTO { + + @ApiModelProperty(value = "绉掓潃鍥㈠晢鍝両D锛岀紪杈戞椂蹇呬紶") + private String id; + + @ApiModelProperty(value = "绉掓潃鍒嗙被ID", required = true) + private String categoryId; + + @ApiModelProperty(value = "鍘熷晢鍝両D", required = true) + private String productId; + + @ApiModelProperty(value = "绉掓潃浠锋牸", required = true) + private BigDecimal seckillPrice; + + @ApiModelProperty(value = "绉掓潃搴撳瓨", required = true) + private Integer totalStock; + + @ApiModelProperty(value = "姣忎汉闄愯喘鏁伴噺") + private Integer limitNum; + + @ApiModelProperty(value = "鍙栬揣鍖哄煙ID锛屼笉浼犲垯浣跨敤鍟嗗鎵灞炲尯鍩烮D") + private String getAreaId; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "绉掓潃寮濮嬫椂闂") + private Date startTime; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "绉掓潃缁撴潫鏃堕棿") + private Date endTime; + + @ApiModelProperty(value = "浜哄伐鎺掑簭鍊硷紝瓒婂ぇ瓒婇潬鍓") + private Integer orderFiled; + + @ApiModelProperty(value = "鐘舵侊細0涓嬫灦 1涓婃灦") + private Integer status; + + @ApiModelProperty(value = "澶囨敞") + private String remark; +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/query/SeckillGroupProductQuery.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/query/SeckillGroupProductQuery.java new file mode 100644 index 00000000..bda6e09e --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/query/SeckillGroupProductQuery.java @@ -0,0 +1,32 @@ +package cc.hiver.mall.pojo.query; + +import cc.hiver.core.base.HiverBasePageQuery; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel("绉掓潃鍥㈠晢鍝佸垎椤垫煡璇㈠璞") +public class SeckillGroupProductQuery extends HiverBasePageQuery { + + @ApiModelProperty(value = "绉掓潃鍒嗙被ID") + private String categoryId; + + @ApiModelProperty(value = "鍖哄煙ID") + private String regionId; + + @ApiModelProperty(value = "鍟嗗ID") + private String shopId; + + @ApiModelProperty(value = "鍏抽敭瀛") + private String keywords; + + @ApiModelProperty(value = "鍟嗗搧鍚嶇О") + private String productName; + + @ApiModelProperty(value = "鐘舵侊細0涓嬫灦 1涓婃灦") + private Integer status; + + @ApiModelProperty(value = "鏄惁浠呮煡璇㈠綋鍓嶆湁鏁堟椿鍔ㄥ晢鍝") + private Boolean onlyActive; +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/vo/SeckillGroupProductVO.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/vo/SeckillGroupProductVO.java new file mode 100644 index 00000000..3ed611aa --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/vo/SeckillGroupProductVO.java @@ -0,0 +1,89 @@ +package cc.hiver.mall.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class SeckillGroupProductVO { + + @ApiModelProperty(value = "绉掓潃鍥㈠晢鍝両D") + private String id; + + @ApiModelProperty(value = "绉掓潃鍒嗙被ID") + private String categoryId; + + @ApiModelProperty(value = "鍘熷晢鍝両D") + private String productId; + + @ApiModelProperty(value = "鍟嗗搧鍚嶇О") + private String productName; + + @ApiModelProperty(value = "鍟嗗搧鍥剧墖") + private String productPicture; + + @ApiModelProperty(value = "鍗曚綅") + private String unit; + + @ApiModelProperty(value = "鍟嗗搧灞炴у垪琛/瑙勬牸") + private String attributeList; + + @ApiModelProperty(value = "鍟嗗搧绠浠") + private String productIntro; + + @ApiModelProperty(value = "鍘熶环/鍒掔嚎浠") + private BigDecimal originalPrice; + + @ApiModelProperty(value = "绉掓潃浠锋牸") + private BigDecimal seckillPrice; + + @ApiModelProperty(value = "绉掓潃鎬诲簱瀛") + private Integer totalStock; + + @ApiModelProperty(value = "宸插敭搴撳瓨") + private Integer soldStock; + + @ApiModelProperty(value = "閿佸畾搴撳瓨") + private Integer lockStock; + + @ApiModelProperty(value = "鍙敭搴撳瓨") + private Integer availableStock; + + @ApiModelProperty(value = "姣忎汉闄愯喘鏁伴噺") + private Integer limitNum; + + @ApiModelProperty(value = "鍟嗗ID") + private String shopId; + + @ApiModelProperty(value = "鍟嗗鍚嶇О") + private String shopName; + + @ApiModelProperty(value = "鍟嗗鐢佃瘽") + private String shopPhone; + + @ApiModelProperty(value = "鍟嗗鍦板潃") + private String shopAddress; + + @ApiModelProperty(value = "鍙栬揣鍖哄煙ID") + private String getAreaId; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "绉掓潃寮濮嬫椂闂") + private Date startTime; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "绉掓潃缁撴潫鏃堕棿") + private Date endTime; + + @ApiModelProperty(value = "浜哄伐鎺掑簭鍊") + private Integer orderFiled; + + @ApiModelProperty(value = "鐘舵侊細0涓嬫灦 1涓婃灦") + private Integer status; +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/SeckillGroupCategoryService.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/SeckillGroupCategoryService.java new file mode 100644 index 00000000..509f8583 --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/SeckillGroupCategoryService.java @@ -0,0 +1,17 @@ +package cc.hiver.mall.service.mybatis; + +import cc.hiver.mall.entity.SeckillGroupCategory; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +public interface SeckillGroupCategoryService extends IService { + + List listForApp(String regionId); + + List listFromCache(String regionId); + + void refreshCategoryCache(String regionId); + + void removeCategoryCache(String regionId, String categoryId); +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/SeckillGroupProductService.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/SeckillGroupProductService.java new file mode 100644 index 00000000..bcfc3147 --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/SeckillGroupProductService.java @@ -0,0 +1,29 @@ +package cc.hiver.mall.service.mybatis; + +import cc.hiver.mall.entity.SeckillGroupProduct; +import cc.hiver.mall.pojo.dto.SeckillGroupProductDTO; +import cc.hiver.mall.pojo.query.SeckillGroupProductQuery; +import cc.hiver.mall.pojo.vo.SeckillGroupProductVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +public interface SeckillGroupProductService extends IService { + + SeckillGroupProduct saveFromProduct(SeckillGroupProductDTO dto); + + SeckillGroupProduct editSeckillProduct(SeckillGroupProductDTO dto); + + IPage pageProducts(SeckillGroupProductQuery query); + + IPage appPageProducts(SeckillGroupProductQuery query); + + SeckillGroupProductVO getDetail(String id); + + void refreshProductCache(String categoryId); + + void refreshProductCacheByProductId(String id); + + void removeProductCache(String categoryId); + + void removeProductDetailCache(String id); +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopServiceImpl.java index 5fe81ced..5e4da361 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopServiceImpl.java @@ -116,7 +116,8 @@ public class ShopServiceImpl implements ShopService { final Path ShopNameField = root.get("shopName"); final Path regionField = root.get("regionId"); final Path shopAreaField = root.get("shopArea"); - final Path isStudentField = root.get("isStudent"); + final Path isStudentField = root.get("isStudent"); + final Path merchantTypeField = root.get("merchantType"); final Path statusField = root.get("status"); @@ -160,6 +161,13 @@ public class ShopServiceImpl implements ShopService { if (shop.getIsStudent() != null) { list.add(cb.equal(isStudentField, shop.getIsStudent())); } + if (shop.getMerchantType() != null) { + if (shop.getMerchantType().equals(1)) { + list.add(cb.or(cb.equal(merchantTypeField, shop.getMerchantType()), cb.isNull(merchantTypeField))); + } else { + list.add(cb.equal(merchantTypeField, shop.getMerchantType())); + } + } final Predicate[] arr = new Predicate[list.size()]; diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java index 0cb3d214..57162358 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java @@ -1480,6 +1480,7 @@ public class MallOrderServiceImpl extends ServiceImpl implements SeckillGroupCategoryService { + + private static final String CATEGORY_CACHE_KEY = "SECKILL_GROUP_CATEGORY_CACHE"; + + @Autowired + private RedisTemplateHelper redisTemplateHelper; + + @Override + public List listForApp(String regionId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SeckillGroupCategory::getDelFlag, 0) + .eq(SeckillGroupCategory::getStatus, 1) + .eq(SeckillGroupCategory::getRegionId, regionId) + .orderByDesc(SeckillGroupCategory::getSort) + .orderByAsc(SeckillGroupCategory::getCreateTime); + return list(wrapper); + } + + @Override + public List listFromCache(String regionId) { + if (CharSequenceUtil.isBlank(regionId)) { + return new ArrayList<>(); + } + List values = redisTemplateHelper.hValues(categoryCacheKey(regionId)); + if (values != null && !values.isEmpty()) { + List categories = new ArrayList<>(); + for (Object value : values) { + categories.add(JSONUtil.toBean(value.toString(), SeckillGroupCategory.class)); + } + sortCategories(categories); + return categories; + } + List categories = listForApp(regionId); + refreshCategoryCache(regionId, categories); + return categories; + } + + @Override + public void refreshCategoryCache(String regionId) { + if (CharSequenceUtil.isBlank(regionId)) { + return; + } + refreshCategoryCache(regionId, listForApp(regionId)); + } + + @Override + public void removeCategoryCache(String regionId, String categoryId) { + if (CharSequenceUtil.isBlank(regionId) || CharSequenceUtil.isBlank(categoryId)) { + return; + } + redisTemplateHelper.hDelete(categoryCacheKey(regionId), categoryId); + } + + private void refreshCategoryCache(String regionId, List categories) { + String key = categoryCacheKey(regionId); + redisTemplateHelper.delete(key); + if (categories == null || categories.isEmpty()) { + return; + } + for (SeckillGroupCategory category : categories) { + redisTemplateHelper.hPut(key, category.getId(), JSONUtil.toJsonStr(category)); + } + } + + private String categoryCacheKey(String regionId) { + return CATEGORY_CACHE_KEY + ":" + regionId; + } + + private void sortCategories(List categories) { + categories.sort((o1, o2) -> { + Integer sort1 = o1.getSort() == null ? 0 : o1.getSort(); + Integer sort2 = o2.getSort() == null ? 0 : o2.getSort(); + int sortCompare = sort2.compareTo(sort1); + if (sortCompare != 0) { + return sortCompare; + } + return o1.getId().compareTo(o2.getId()); + }); + } +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/SeckillGroupProductServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/SeckillGroupProductServiceImpl.java new file mode 100644 index 00000000..e99f04f9 --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/SeckillGroupProductServiceImpl.java @@ -0,0 +1,509 @@ +package cc.hiver.mall.serviceimpl.mybatis; + +import cc.hiver.core.common.redis.RedisTemplateHelper; +import cc.hiver.mall.dao.mapper.SeckillGroupProductMapper; +import cc.hiver.mall.entity.Product; +import cc.hiver.mall.entity.SeckillGroupCategory; +import cc.hiver.mall.entity.SeckillGroupProduct; +import cc.hiver.mall.entity.Shop; +import cc.hiver.mall.pojo.dto.SeckillGroupProductDTO; +import cc.hiver.mall.pojo.query.SeckillGroupProductQuery; +import cc.hiver.mall.pojo.vo.SeckillGroupProductVO; +import cc.hiver.mall.service.ShopService; +import cc.hiver.mall.service.mybatis.ProductService; +import cc.hiver.mall.service.mybatis.SeckillGroupCategoryService; +import cc.hiver.mall.service.mybatis.SeckillGroupProductService; +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.zip.CRC32; + +@Service +public class SeckillGroupProductServiceImpl extends ServiceImpl implements SeckillGroupProductService { + + private static final String PRODUCT_LIST_CACHE_PREFIX = "SECKILL_GROUP_PRODUCTS:"; + private static final String PRODUCT_REGION_LIST_CACHE_PREFIX = "SECKILL_GROUP_PRODUCTS_REGION:"; + private static final String PRODUCT_DETAIL_CACHE_PREFIX = "SECKILL_GROUP_PRODUCT:"; + private static final DateTimeFormatter SEED_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHH"); + + @Autowired + private ProductService productService; + + @Autowired + private ShopService shopService; + + @Autowired + private SeckillGroupCategoryService seckillGroupCategoryService; + + @Autowired + private RedisTemplateHelper redisTemplateHelper; + + @Transactional(rollbackFor = Exception.class) + @Override + public SeckillGroupProduct saveFromProduct(SeckillGroupProductDTO dto) { + validateSaveDto(dto, false); + Product product = productService.getById(dto.getProductId()); + if (product == null) { + throw new IllegalArgumentException("鍘熷晢鍝佷笉瀛樺湪"); + } + Shop shop = shopService.get(product.getShopId()); + + SeckillGroupProduct seckillProduct = new SeckillGroupProduct(); + fillSnapshot(seckillProduct, dto, product, shop); + seckillProduct.setSoldStock(0); + seckillProduct.setLockStock(0); + seckillProduct.setStatus(dto.getStatus() == null ? 0 : dto.getStatus()); + seckillProduct.setSortHash(stableHash(seckillProduct.getId())); + save(seckillProduct); + refreshProductCache(seckillProduct.getCategoryId()); + refreshProductDetailCache(seckillProduct); + return seckillProduct; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public SeckillGroupProduct editSeckillProduct(SeckillGroupProductDTO dto) { + validateSaveDto(dto, true); + SeckillGroupProduct old = getById(dto.getId()); + if (old == null) { + throw new IllegalArgumentException("绉掓潃鍥㈠晢鍝佷笉瀛樺湪"); + } + + String productId = CharSequenceUtil.isBlank(dto.getProductId()) ? old.getProductId() : dto.getProductId(); + Product product = productService.getById(productId); + if (product == null) { + throw new IllegalArgumentException("鍘熷晢鍝佷笉瀛樺湪"); + } + Shop shop = shopService.get(product.getShopId()); + + SeckillGroupProduct updated = new SeckillGroupProduct(); + updated.setId(old.getId()); + updated.setSoldStock(old.getSoldStock() == null ? 0 : old.getSoldStock()); + updated.setLockStock(old.getLockStock() == null ? 0 : old.getLockStock()); + fillSnapshot(updated, dto, product, shop); + updated.setStatus(dto.getStatus() == null ? old.getStatus() : dto.getStatus()); + updated.setSortHash(stableHash(updated.getId())); + + int usedStock = safeStock(updated.getSoldStock()) + safeStock(updated.getLockStock()); + if (updated.getTotalStock() != null && updated.getTotalStock() < usedStock) { + throw new IllegalArgumentException("绉掓潃搴撳瓨涓嶈兘灏忎簬宸插敭鍜岄攣瀹氬簱瀛"); + } + + updateById(updated); + refreshProductCache(old.getCategoryId()); + if (!old.getCategoryId().equals(updated.getCategoryId())) { + refreshProductCache(updated.getCategoryId()); + } + refreshProductDetailCache(updated); + return updated; + } + + @Override + public IPage pageProducts(SeckillGroupProductQuery query) { + Page page = new Page<>(query.getPageNum(), query.getPageSize()); + IPage result = page(page, buildQueryWrapper(query, false)); + return convertPage(result); + } + + @Override + public IPage appPageProducts(SeckillGroupProductQuery query) { + int pageNum = query.getPageNum() > 0 ? query.getPageNum() : 1; + int pageSize = query.getPageSize() > 0 ? query.getPageSize() : 10; + List allProducts = filterProducts(getRegionAppListFromCache(query.getRegionId()), query); + applyQuerySort(allProducts, query); + + int fromIndex = (pageNum - 1) * pageSize; + int toIndex = Math.min(fromIndex + pageSize, allProducts.size()); + List records = fromIndex >= allProducts.size() + ? new ArrayList<>() + : allProducts.subList(fromIndex, toIndex); + + Page page = new Page<>(pageNum, pageSize, allProducts.size()); + page.setRecords(records); + return page; + } + + @Override + public SeckillGroupProductVO getDetail(String id) { + if (CharSequenceUtil.isBlank(id)) { + return null; + } + String cached = redisTemplateHelper.get(PRODUCT_DETAIL_CACHE_PREFIX + id); + if (CharSequenceUtil.isNotBlank(cached)) { + return JSONUtil.toBean(cached, SeckillGroupProductVO.class); + } + SeckillGroupProduct product = getById(id); + if (product == null || product.getDelFlag() != 0) { + return null; + } + SeckillGroupProductVO vo = toVo(product); + redisTemplateHelper.set(PRODUCT_DETAIL_CACHE_PREFIX + id, JSONUtil.toJsonStr(vo)); + return vo; + } + + @Override + public void refreshProductCache(String categoryId) { + if (CharSequenceUtil.isBlank(categoryId)) { + return; + } + List products = toVoList(list(buildActiveCategoryWrapper(categoryId))); + sortForApp(products); + redisTemplateHelper.set(productListCacheKey(categoryId), JSONUtil.toJsonStr(products)); + removeRegionProductCacheByCategory(categoryId); + for (SeckillGroupProductVO product : products) { + redisTemplateHelper.set(PRODUCT_DETAIL_CACHE_PREFIX + product.getId(), JSONUtil.toJsonStr(product)); + } + } + + @Override + public void refreshProductCacheByProductId(String id) { + SeckillGroupProduct product = getById(id); + if (product != null) { + refreshProductCache(product.getCategoryId()); + refreshProductDetailCache(product); + } + } + + @Override + public void removeProductCache(String categoryId) { + if (CharSequenceUtil.isBlank(categoryId)) { + return; + } + redisTemplateHelper.deleteByPattern(PRODUCT_LIST_CACHE_PREFIX + categoryId + ":*"); + removeRegionProductCacheByCategory(categoryId); + } + + @Override + public void removeProductDetailCache(String id) { + if (CharSequenceUtil.isNotBlank(id)) { + redisTemplateHelper.delete(PRODUCT_DETAIL_CACHE_PREFIX + id); + } + } + + private List getAppListFromCache(String categoryId) { + String cacheKey = productListCacheKey(categoryId); + String cached = redisTemplateHelper.get(cacheKey); + if (CharSequenceUtil.isNotBlank(cached)) { + return JSONUtil.toList(cached, SeckillGroupProductVO.class); + } + List products = toVoList(list(buildActiveCategoryWrapper(categoryId))); + sortForApp(products); + redisTemplateHelper.set(cacheKey, JSONUtil.toJsonStr(products)); + return products; + } + + private List getRegionAppListFromCache(String regionId) { + String cacheKey = regionProductListCacheKey(regionId); + String cached = redisTemplateHelper.get(cacheKey); + if (CharSequenceUtil.isNotBlank(cached)) { + return JSONUtil.toList(cached, SeckillGroupProductVO.class); + } + SeckillGroupProductQuery query = new SeckillGroupProductQuery(); + query.setRegionId(regionId); + List products = toVoList(list(buildQueryWrapper(query, true))); + sortForApp(products); + redisTemplateHelper.set(cacheKey, JSONUtil.toJsonStr(products)); + return products; + } + + private List filterProducts(List products, SeckillGroupProductQuery query) { + List filtered = new ArrayList<>(); + if (products == null || products.isEmpty()) { + return filtered; + } + String keywords = CharSequenceUtil.isNotBlank(query.getKeywords()) ? query.getKeywords() : query.getProductName(); + for (SeckillGroupProductVO product : products) { + if (CharSequenceUtil.isNotBlank(query.getCategoryId()) && !query.getCategoryId().equals(product.getCategoryId())) { + continue; + } + if (CharSequenceUtil.isNotBlank(query.getShopId()) && !query.getShopId().equals(product.getShopId())) { + continue; + } + if (query.getStatus() != null && !query.getStatus().equals(product.getStatus())) { + continue; + } + if (CharSequenceUtil.isNotBlank(keywords)) { + String productName = product.getProductName() == null ? "" : product.getProductName(); + String shopName = product.getShopName() == null ? "" : product.getShopName(); + if (!productName.contains(keywords) && !shopName.contains(keywords)) { + continue; + } + } + filtered.add(product); + } + return filtered; + } + + private LambdaQueryWrapper buildQueryWrapper(SeckillGroupProductQuery query, boolean onlyActive) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SeckillGroupProduct::getDelFlag, 0); + if (CharSequenceUtil.isNotBlank(query.getCategoryId())) { + wrapper.eq(SeckillGroupProduct::getCategoryId, query.getCategoryId()); + } + appendRegionCondition(wrapper, query.getRegionId()); + if (CharSequenceUtil.isNotBlank(query.getShopId())) { + wrapper.eq(SeckillGroupProduct::getShopId, query.getShopId()); + } + if (query.getStatus() != null) { + wrapper.eq(SeckillGroupProduct::getStatus, query.getStatus()); + } + String keywords = CharSequenceUtil.isNotBlank(query.getKeywords()) ? query.getKeywords() : query.getProductName(); + if (CharSequenceUtil.isNotBlank(keywords)) { + wrapper.and(w -> w.like(SeckillGroupProduct::getProductName, keywords) + .or() + .like(SeckillGroupProduct::getShopName, keywords)); + } + if (onlyActive || Boolean.TRUE.equals(query.getOnlyActive())) { + appendActiveCondition(wrapper); + } + if (isSeckillPriceSort(query)) { + boolean asc = "asc".equalsIgnoreCase(query.getOrder()) || "asc".equalsIgnoreCase(query.getSort()); + wrapper.orderBy(true, asc, SeckillGroupProduct::getSeckillPrice); + } else { + wrapper.orderByDesc(SeckillGroupProduct::getCreateTime); + } + return wrapper; + } + + private LambdaQueryWrapper buildActiveCategoryWrapper(String categoryId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SeckillGroupProduct::getDelFlag, 0) + .eq(SeckillGroupProduct::getStatus, 1) + .eq(SeckillGroupProduct::getCategoryId, categoryId); + appendActiveCondition(wrapper); + return wrapper; + } + + private void appendActiveCondition(LambdaQueryWrapper wrapper) { + Date now = new Date(); + wrapper.eq(SeckillGroupProduct::getStatus, 1) + .and(w -> w.isNull(SeckillGroupProduct::getStartTime).or().le(SeckillGroupProduct::getStartTime, now)) + .and(w -> w.isNull(SeckillGroupProduct::getEndTime).or().ge(SeckillGroupProduct::getEndTime, now)); + } + + private void appendRegionCondition(LambdaQueryWrapper wrapper, String regionId) { + if (CharSequenceUtil.isBlank(regionId)) { + return; + } + List categories = seckillGroupCategoryService.listForApp(regionId); + if (categories == null || categories.isEmpty()) { + wrapper.eq(SeckillGroupProduct::getCategoryId, "__none__"); + return; + } + List categoryIds = new ArrayList<>(); + for (SeckillGroupCategory category : categories) { + categoryIds.add(category.getId()); + } + wrapper.in(SeckillGroupProduct::getCategoryId, categoryIds); + } + + private boolean categoryBelongsToRegion(String categoryId, String regionId) { + if (CharSequenceUtil.isBlank(regionId)) { + return true; + } + SeckillGroupCategory category = seckillGroupCategoryService.getById(categoryId); + return category != null + && Integer.valueOf(0).equals(category.getDelFlag()) + && Integer.valueOf(1).equals(category.getStatus()) + && regionId.equals(category.getRegionId()); + } + + private IPage convertPage(IPage result) { + Page page = new Page<>(result.getCurrent(), result.getSize(), result.getTotal()); + page.setRecords(toVoList(result.getRecords())); + return page; + } + + private List toVoList(List products) { + List voList = new ArrayList<>(); + if (products == null || products.isEmpty()) { + return voList; + } + for (SeckillGroupProduct product : products) { + voList.add(toVo(product)); + } + return voList; + } + + private SeckillGroupProductVO toVo(SeckillGroupProduct product) { + SeckillGroupProductVO vo = new SeckillGroupProductVO(); + vo.setId(product.getId()); + vo.setCategoryId(product.getCategoryId()); + vo.setProductId(product.getProductId()); + vo.setProductName(product.getProductName()); + vo.setProductPicture(product.getProductPicture()); + vo.setUnit(product.getUnit()); + vo.setAttributeList(product.getAttributeList()); + vo.setProductIntro(product.getProductIntro()); + vo.setOriginalPrice(product.getOriginalPrice()); + vo.setSeckillPrice(product.getSeckillPrice()); + vo.setTotalStock(product.getTotalStock()); + vo.setSoldStock(product.getSoldStock()); + vo.setLockStock(product.getLockStock()); + vo.setAvailableStock(Math.max(0, safeStock(product.getTotalStock()) - safeStock(product.getSoldStock()) - safeStock(product.getLockStock()))); + vo.setLimitNum(product.getLimitNum()); + vo.setShopId(product.getShopId()); + vo.setShopName(product.getShopName()); + vo.setShopPhone(product.getShopPhone()); + vo.setShopAddress(product.getShopAddress()); + vo.setGetAreaId(product.getGetAreaId()); + vo.setStartTime(product.getStartTime()); + vo.setEndTime(product.getEndTime()); + vo.setOrderFiled(product.getOrderFiled()); + vo.setStatus(product.getStatus()); + return vo; + } + + private void sortForApp(List products) { + products.sort((o1, o2) -> { + int hashCompare = Long.compare(stableHash(o1.getId()), stableHash(o2.getId())); + if (hashCompare != 0) { + return hashCompare; + } + Integer order1 = o1.getOrderFiled() == null ? 0 : o1.getOrderFiled(); + Integer order2 = o2.getOrderFiled() == null ? 0 : o2.getOrderFiled(); + int orderCompare = order2.compareTo(order1); + if (orderCompare != 0) { + return orderCompare; + } + return o1.getId().compareTo(o2.getId()); + }); + } + + private void applyQuerySort(List products, SeckillGroupProductQuery query) { + if (!isSeckillPriceSort(query)) { + return; + } + boolean asc = "asc".equalsIgnoreCase(query.getOrder()) || "asc".equalsIgnoreCase(query.getSort()); + products.sort((o1, o2) -> { + java.math.BigDecimal p1 = o1.getSeckillPrice() == null ? java.math.BigDecimal.ZERO : o1.getSeckillPrice(); + java.math.BigDecimal p2 = o2.getSeckillPrice() == null ? java.math.BigDecimal.ZERO : o2.getSeckillPrice(); + int compare = p1.compareTo(p2); + return asc ? compare : -compare; + }); + } + + private boolean isSeckillPriceSort(SeckillGroupProductQuery query) { + return "seckillPrice".equalsIgnoreCase(query.getSort()) + || "seckill_price".equalsIgnoreCase(query.getSort()) + || "seckillPrice".equalsIgnoreCase(query.getOrder()) + || "seckill_price".equalsIgnoreCase(query.getOrder()); + } + + private void fillSnapshot(SeckillGroupProduct target, SeckillGroupProductDTO dto, Product product, Shop shop) { + target.setCategoryId(dto.getCategoryId()); + target.setProductId(product.getId()); + target.setProductName(product.getProductName()); + target.setProductPicture(product.getProductPicture()); + target.setUnit(product.getUnit()); + target.setAttributeList(product.getAttributeList()); + target.setProductIntro(product.getProductIntro()); + target.setOriginalPrice(resolveOriginalPrice(product)); + target.setSeckillPrice(dto.getSeckillPrice()); + target.setTotalStock(dto.getTotalStock()); + target.setLimitNum(dto.getLimitNum()); + target.setStartTime(dto.getStartTime()); + target.setEndTime(dto.getEndTime()); + target.setOrderFiled(dto.getOrderFiled() == null ? 0 : dto.getOrderFiled()); + target.setRemark(dto.getRemark()); + target.setShopId(product.getShopId()); + if (shop != null) { + target.setShopName(shop.getShopName()); + target.setShopPhone(shop.getContactPhone()); + target.setShopAddress(shop.getShopAddress()); + target.setGetAreaId(CharSequenceUtil.isBlank(dto.getGetAreaId()) ? shop.getRegionId() : dto.getGetAreaId()); + } else { + target.setGetAreaId(dto.getGetAreaId()); + } + } + + private void validateSaveDto(SeckillGroupProductDTO dto, boolean edit) { + if (dto == null) { + throw new IllegalArgumentException("鍙傛暟涓嶈兘涓虹┖"); + } + if (edit && CharSequenceUtil.isBlank(dto.getId())) { + throw new IllegalArgumentException("绉掓潃鍥㈠晢鍝両D涓嶈兘涓虹┖"); + } + if (CharSequenceUtil.isBlank(dto.getCategoryId())) { + throw new IllegalArgumentException("绉掓潃鍒嗙被涓嶈兘涓虹┖"); + } + if (!edit && CharSequenceUtil.isBlank(dto.getProductId())) { + throw new IllegalArgumentException("鍘熷晢鍝佷笉鑳戒负绌"); + } + if (dto.getSeckillPrice() == null || dto.getSeckillPrice().signum() <= 0) { + throw new IllegalArgumentException("绉掓潃浠锋牸蹇呴』澶т簬0"); + } + if (dto.getTotalStock() == null || dto.getTotalStock() < 0) { + throw new IllegalArgumentException("绉掓潃搴撳瓨涓嶈兘灏忎簬0"); + } + } + + private void refreshProductDetailCache(SeckillGroupProduct product) { + redisTemplateHelper.set(PRODUCT_DETAIL_CACHE_PREFIX + product.getId(), JSONUtil.toJsonStr(toVo(product))); + } + + private java.math.BigDecimal resolveOriginalPrice(Product product) { + if (product == null || CharSequenceUtil.isBlank(product.getAttributeListPrice())) { + return product == null ? null : product.getPrice(); + } + try { + JSONArray specs = JSONUtil.parseArray(product.getAttributeListPrice()); + if (!specs.isEmpty()) { + JSONObject firstSpec = specs.getJSONObject(0); + String specPrice = firstSpec.getStr("specPrice"); + if (CharSequenceUtil.isNotBlank(specPrice)) { + return new java.math.BigDecimal(specPrice); + } + } + } catch (Exception ignored) { + } + return product.getPrice(); + } + + private String productListCacheKey(String categoryId) { + return PRODUCT_LIST_CACHE_PREFIX + categoryId + ":" + currentHalfHourSeed(); + } + + private String regionProductListCacheKey(String regionId) { + return PRODUCT_REGION_LIST_CACHE_PREFIX + regionId + ":" + currentHalfHourSeed(); + } + + private void removeRegionProductCacheByCategory(String categoryId) { + SeckillGroupCategory category = seckillGroupCategoryService.getById(categoryId); + if (category != null && CharSequenceUtil.isNotBlank(category.getRegionId())) { + redisTemplateHelper.deleteByPattern(PRODUCT_REGION_LIST_CACHE_PREFIX + category.getRegionId() + ":*"); + } + } + + private int safeStock(Integer value) { + return value == null ? 0 : value; + } + + private long stableHash(String id) { + CRC32 crc32 = new CRC32(); + String seed = currentHalfHourSeed(); + crc32.update((id + ":" + seed).getBytes(StandardCharsets.UTF_8)); + return crc32.getValue(); + } + + private String currentHalfHourSeed() { + LocalDateTime now = LocalDateTime.now(); + String minuteBucket = now.getMinute() < 30 ? "00" : "30"; + return now.format(SEED_FORMATTER) + minuteBucket; + } +} diff --git a/hiver-modules/hiver-mall/src/main/resources/db/alter_snapshot_fields.sql b/hiver-modules/hiver-mall/src/main/resources/db/alter_snapshot_fields.sql new file mode 100644 index 00000000..3d23d476 --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/resources/db/alter_snapshot_fields.sql @@ -0,0 +1,17 @@ +-- 涓 mall_order 琛ㄦ坊鍔犲揩鐓у瓧娈碉紙鐢ㄦ埛/鍟嗗鑱旂郴淇℃伅锛 +ALTER TABLE `mall_order` + ADD COLUMN `receiver_name` VARCHAR(64) NULL COMMENT '鏀惰揣浜哄鍚嶏紙涓嬪崟蹇収锛' AFTER `remark`, + ADD COLUMN `receiver_phone` VARCHAR(20) NULL COMMENT '鏀惰揣浜虹數璇濓紙涓嬪崟蹇収锛' AFTER `receiver_name`, + ADD COLUMN `receiver_address` VARCHAR(255) NULL COMMENT '鏀惰揣鍦板潃锛堜笅鍗曞揩鐓э紝areaName+floor+roomNum锛' AFTER `receiver_phone`, + ADD COLUMN `shop_name` VARCHAR(128) NULL COMMENT '鍟嗗鍚嶇О锛堜笅鍗曞揩鐓э級' AFTER `receiver_address`, + ADD COLUMN `shop_phone` VARCHAR(20) NULL COMMENT '鍟嗗鑱旂郴鐢佃瘽锛堜笅鍗曞揩鐓э級' AFTER `shop_name`, + ADD COLUMN `shop_address` VARCHAR(255) NULL COMMENT '鍟嗗鍦板潃锛堜笅鍗曞揩鐓э級' AFTER `shop_phone`; + +-- 涓 mall_delivery_order 琛ㄦ坊鍔犲揩鐓у瓧娈碉紙閰嶉佸憳鐪嬪埌鐨勬敹璐т汉鍙婂晢瀹朵俊鎭級 +ALTER TABLE `mall_delivery_order` + ADD COLUMN `receiver_name` VARCHAR(64) NULL COMMENT '鏀惰揣浜哄鍚嶏紙蹇収锛' AFTER `status`, + ADD COLUMN `receiver_phone` VARCHAR(20) NULL COMMENT '鏀惰揣浜虹數璇濓紙蹇収锛' AFTER `receiver_name`, + ADD COLUMN `receiver_address` VARCHAR(255) NULL COMMENT '鏀惰揣鍦板潃锛堝揩鐓э紝areaName+floor+roomNum锛' AFTER `receiver_phone`, + ADD COLUMN `shop_name` VARCHAR(128) NULL COMMENT '鍟嗗鍚嶇О锛堝揩鐓э級' AFTER `receiver_address`, + ADD COLUMN `shop_phone` VARCHAR(20) NULL COMMENT '鍟嗗鑱旂郴鐢佃瘽锛堝揩鐓э級' AFTER `shop_name`, + ADD COLUMN `shop_address` VARCHAR(255) NULL COMMENT '鍟嗗鍦板潃锛堝揩鐓э級' AFTER `shop_phone`; diff --git a/hiver-modules/hiver-mall/src/main/resources/db/seckill_group.sql b/hiver-modules/hiver-mall/src/main/resources/db/seckill_group.sql new file mode 100644 index 00000000..3aec75c8 --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/resources/db/seckill_group.sql @@ -0,0 +1,54 @@ +CREATE TABLE `t_seckill_group_category` ( + `id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '涓婚敭ID', + `create_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '鍒涘缓鑰', + `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿', + `update_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '鏇存柊鑰', + `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿', + `del_flag` int DEFAULT 0 COMMENT '鍒犻櫎鏍囧織 榛樿0', + `category_name` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '鍒嗙被鍚嶇О', + `category_icon` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '鍒嗙被鍥炬爣', + `region_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '鍖哄煙ID', + `sort` int DEFAULT 0 COMMENT '鎺掑簭鍊硷紝瓒婂ぇ瓒婇潬鍓', + `status` tinyint DEFAULT 1 COMMENT '鐘舵侊細0绂佺敤 1鍚敤', + `remark` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '澶囨敞', + PRIMARY KEY (`id`), + KEY `idx_region_status_sort` (`region_id`, `status`, `del_flag`, `sort`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='绉掓潃鍥㈡í鎺掑垎绫昏〃'; + +CREATE TABLE `t_seckill_group_product` ( + `id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '涓婚敭ID', + `create_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '鍒涘缓鑰', + `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿', + `update_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '鏇存柊鑰', + `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿', + `del_flag` int DEFAULT 0 COMMENT '鍒犻櫎鏍囧織 榛樿0', + `category_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '绉掓潃鍒嗙被ID', + `product_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '鍘熷晢鍝両D', + `product_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '鍟嗗搧鍚嶇О蹇収', + `product_picture` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '鍟嗗搧鍥剧墖蹇収', + `unit` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '鍗曚綅蹇収', + `attribute_list` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '鍟嗗搧灞炴у垪琛/瑙勬牸蹇収', + `product_intro` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '鍟嗗搧绠浠嬪揩鐓', + `original_price` decimal(10,2) DEFAULT NULL COMMENT '鍘熶环/鍒掔嚎浠峰揩鐓', + `seckill_price` decimal(10,2) NOT NULL COMMENT '绉掓潃浠锋牸', + `total_stock` int NOT NULL DEFAULT 0 COMMENT '绉掓潃鎬诲簱瀛', + `sold_stock` int NOT NULL DEFAULT 0 COMMENT '宸插敭搴撳瓨', + `lock_stock` int NOT NULL DEFAULT 0 COMMENT '閿佸畾搴撳瓨', + `limit_num` int DEFAULT NULL COMMENT '姣忎汉闄愯喘鏁伴噺锛岀┖琛ㄧず涓嶉檺', + `shop_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '鍟嗗ID', + `shop_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '鍟嗗鍚嶇О蹇収', + `shop_phone` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '鍟嗗鐢佃瘽蹇収', + `shop_address` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '鍟嗗鍦板潃蹇収', + `get_area_id` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '鍙栬揣鍖哄煙ID锛屼究浜庝笅鍗曚紶鍙', + `start_time` datetime DEFAULT NULL COMMENT '绉掓潃寮濮嬫椂闂', + `end_time` datetime DEFAULT NULL COMMENT '绉掓潃缁撴潫鏃堕棿', + `order_filed` int DEFAULT 0 COMMENT '浜哄伐鎺掑簭鍊硷紝瓒婂ぇ瓒婇潬鍓', + `sort_hash` bigint unsigned DEFAULT 0 COMMENT '绋冲畾闅忔満鎺掑簭鍊', + `status` tinyint DEFAULT 0 COMMENT '鐘舵侊細0涓嬫灦 1涓婃灦', + `remark` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '澶囨敞', + PRIMARY KEY (`id`), + KEY `idx_category_status_time` (`category_id`, `status`, `del_flag`, `start_time`, `end_time`), + KEY `idx_sort_hash` (`category_id`, `status`, `del_flag`, `sort_hash`, `order_filed`), + KEY `idx_product_id` (`product_id`), + KEY `idx_shop_id` (`shop_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='绉掓潃鍥㈠晢鍝佽〃'; diff --git a/hiver-modules/hiver-mall/src/main/resources/mapper/MallDeliveryOrderMapper.xml b/hiver-modules/hiver-mall/src/main/resources/mapper/MallDeliveryOrderMapper.xml index ace3a0d7..495f93c9 100644 --- a/hiver-modules/hiver-mall/src/main/resources/mapper/MallDeliveryOrderMapper.xml +++ b/hiver-modules/hiver-mall/src/main/resources/mapper/MallDeliveryOrderMapper.xml @@ -223,17 +223,6 @@ AND finish_time >= CURDATE() - - diff --git a/hiver-modules/hiver-mall/src/main/resources/sql/init_coupon.sql b/hiver-modules/hiver-mall/src/main/resources/sql/init_coupon.sql new file mode 100644 index 00000000..d152e7e0 --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/resources/sql/init_coupon.sql @@ -0,0 +1,73 @@ +-- 1. 浼樻儬鍒告ā鏉胯〃 / 閰嶇疆琛 +CREATE TABLE `t_mall_coupon` ( + `id` varchar(32) NOT NULL COMMENT '鍞竴鏍囪瘑', + `name` varchar(100) NOT NULL COMMENT '浼樻儬鍒稿悕绉帮紝濡傦細5鍏冩棤闂ㄦ绾㈠寘銆佹弧20鍑5鍒', + + -- 鍙戞斁鏂逛笌閫傜敤鑼冨洿瑙勫垯 + `issuer_type` tinyint(2) NOT NULL DEFAULT '1' COMMENT '鍙戞斁鏂癸細1-骞冲彴锛2-鍟嗗', + `issuer_id` varchar(32) NOT NULL DEFAULT '0' COMMENT '鍙戞斁鏂笽D锛屽钩鍙颁负0锛屽晢瀹跺垯涓哄叿浣撳晢鎴稩D', + `apply_scene` tinyint(2) NOT NULL DEFAULT '0' COMMENT '閫傜敤鍦烘櫙锛0-閫氱敤锛1-澶栧崠/涔伴キ锛2-蹇/璺戣吙锛3-浜屾墜鐗╁搧浜ゆ槗', + + -- 闈㈤涓庨棬妲涜鍒 + `type` tinyint(2) NOT NULL DEFAULT '1' COMMENT '浼樻儬鍒哥被鍨嬶細1-婊″噺鍒革紝2-鏃犻棬妲涚洿鍑忓埜', + `min_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '浣跨敤闂ㄦ閲戦锛0鎴栨病濉〃绀烘棤闂ㄦ', + `discount_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '鎶垫墸闈㈤锛堢洿鍑忛噾棰濓級', + + -- 搴撳瓨涓庨檺棰嗚鍒 + `total_count` int(11) NOT NULL DEFAULT '0' COMMENT '鍙戣鎬绘暟锛-1琛ㄧず涓嶉檺鍒舵暟閲', + `remain_count` int(11) NOT NULL DEFAULT '0' COMMENT '鍓╀綑搴撳瓨鍙鍙栨暟閲', + `limit_per_user` int(11) NOT NULL DEFAULT '1' COMMENT '姣忎汉鏈澶氶檺棰嗗紶鏁', + + -- 鏈夋晥鏈熻鍒 + `valid_type` tinyint(2) NOT NULL DEFAULT '1' COMMENT '鏈夋晥鏈熻绠楃被鍨嬶細1-缁濆鏃堕棿娈垫湁鏁堬紝2-棰嗗彇鍚庣浉瀵瑰ぉ鏁版湁鏁', + `valid_start_time` datetime DEFAULT NULL COMMENT '鏈夋晥鏈熷紑濮嬫椂闂达紙閫傜敤缁濆鏃堕棿娈碉級', + `valid_end_time` datetime DEFAULT NULL COMMENT '鏈夋晥鏈熺粨鏉熸椂闂达紙閫傜敤缁濆鏃堕棿娈碉級', + `valid_days` int(11) DEFAULT '0' COMMENT '鑷鍙栦箣鏃ヨ捣鏈夋晥澶╂暟锛堥傜敤鐩稿澶╂暟锛', + + -- 鐘舵佹帶鍒 + `status` tinyint(2) NOT NULL DEFAULT '1' COMMENT '鐘舵侊細0-宸蹭笅鏋/鍋滃彂锛1-鍙戞斁涓', + + -- 鍩虹瀛楁 + `create_by` varchar(32) DEFAULT NULL COMMENT '鍒涘缓鑰', + `create_time` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿', + `update_by` varchar(32) DEFAULT NULL COMMENT '鏇存柊鑰', + `update_time` datetime DEFAULT NULL COMMENT '鏇存柊鏃堕棿', + `del_flag` int(1) DEFAULT '0' COMMENT '鍒犻櫎鏍囧織 榛樿0', + PRIMARY KEY (`id`), + KEY `idx_issuer` (`issuer_type`, `issuer_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='浼樻儬鍒告ā鏉块厤缃〃'; + + +-- 2. 鐢ㄦ埛棰嗗彇鐨勪紭鎯犲埜璧勪骇琛 +CREATE TABLE `t_mall_user_coupon` ( + `id` varchar(32) NOT NULL COMMENT '鍞竴鏍囪瘑', + `user_id` varchar(32) NOT NULL COMMENT '棰嗗彇鐢ㄦ埛ID', + `coupon_id` varchar(32) NOT NULL COMMENT '鍏宠仈鐨勪紭鎯犲埜妯℃澘ID', + + -- 鍐椾綑鏍稿績鍖归厤瀛楁锛堢┖闂存崲鏃堕棿锛岄伩鍏嶄笅鍗曟煡璇㈡椂鐤媯Join锛 + `issuer_type` tinyint(2) NOT NULL COMMENT '鍐椾綑锛氬彂鏀炬柟(1骞冲彴/2鍟嗗)', + `issuer_id` varchar(32) NOT NULL DEFAULT '0' COMMENT '鍐椾綑锛氬彂鏀炬柟ID', + `apply_scene` tinyint(2) NOT NULL COMMENT '鍐椾綑锛氶傜敤鍦烘櫙', + `min_amount` decimal(10,2) NOT NULL COMMENT '鍐椾綑锛氫娇鐢ㄩ棬妲', + `discount_amount` decimal(10,2) NOT NULL COMMENT '鍐椾綑锛氭姷鎵i噾棰', + + -- 鐢ㄦ埛瀹炰綋鐨勭敓鍛藉懆鏈 + `status` tinyint(2) NOT NULL DEFAULT '0' COMMENT '鐘舵侊細0-鏈娇鐢紝1-宸叉寕璧(涓嬪崟閿佸畾涓)锛2-宸蹭娇鐢紝3-宸茶繃鏈', + `receive_time` datetime DEFAULT NULL COMMENT '棰嗗彇鏃堕棿', + `valid_start_time` datetime DEFAULT NULL COMMENT '瀹為檯鍙娇鐢ㄧ敓鏁堟椂闂', + `valid_end_time` datetime DEFAULT NULL COMMENT '瀹為檯鍙娇鐢ㄥけ鏁堟椂闂', + + -- 鏍搁攢涓庝娇鐢ㄨ褰 + `use_time` datetime DEFAULT NULL COMMENT '鐪熷疄鏍搁攢鏃堕棿', + `order_id` varchar(32) DEFAULT NULL COMMENT '鏍搁攢鏃朵娇鐢ㄧ殑璁㈠崟ID', + + -- 鍩虹瀛楁 + `create_by` varchar(32) DEFAULT NULL COMMENT '鍒涘缓鑰', + `create_time` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿', + `update_by` varchar(32) DEFAULT NULL COMMENT '鏇存柊鑰', + `update_time` datetime DEFAULT NULL COMMENT '鏇存柊鏃堕棿', + `del_flag` int(1) DEFAULT '0' COMMENT '鍒犻櫎鏍囧織 榛樿0', + PRIMARY KEY (`id`), + KEY `idx_user_valid` (`user_id`, `status`, `valid_end_time`) USING BTREE, + KEY `idx_order_id` (`order_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鐢ㄦ埛棰嗗彇鐨勪紭鎯犲埜鏄庣粏琛';