diff --git a/hiver-core/src/main/java/cc/hiver/core/common/constant/SettingConstant.java b/hiver-core/src/main/java/cc/hiver/core/common/constant/SettingConstant.java
index c12fddb3..052a47f2 100644
--- a/hiver-core/src/main/java/cc/hiver/core/common/constant/SettingConstant.java
+++ b/hiver-core/src/main/java/cc/hiver/core/common/constant/SettingConstant.java
@@ -107,6 +107,8 @@ public interface SettingConstant {
SMS_REJECT_ORDER,
// 被指定未处理通知
SMS_TIMEOUT,
+ // 中转单商家送达后校内取货超时提醒
+ SMS_TRANSFER,
//订单退款
SMS_ORDERRETURN
}
diff --git a/hiver-core/src/main/java/cc/hiver/core/config/security/WebSecurityConfig.java b/hiver-core/src/main/java/cc/hiver/core/config/security/WebSecurityConfig.java
index e58803a8..2e127291 100644
--- a/hiver-core/src/main/java/cc/hiver/core/config/security/WebSecurityConfig.java
+++ b/hiver-core/src/main/java/cc/hiver/core/config/security/WebSecurityConfig.java
@@ -13,14 +13,20 @@ import cc.hiver.core.config.security.validate.ImageValidateFilter;
import cc.hiver.core.config.security.validate.SmsValidateFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.security.web.firewall.HttpFirewall;
+import org.springframework.security.web.firewall.StrictHttpFirewall;
/**
* Security 核心配置类
@@ -62,6 +68,28 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SecurityUtil securityUtil;
+ @Autowired
+ private UserDetailsServiceImpl userDetailsService;
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+ @Bean
+ public HttpFirewall allowSemicolonHttpFirewall() {
+ StrictHttpFirewall firewall = new StrictHttpFirewall();
+ // 微信小程序真机环境/代理链路偶发携带 matrix 参数(;xxx),默认防火墙会直接拒绝并返回 Servlet 异常。
+ // 仅放开分号,其他可疑 URL 编码规则仍保持 StrictHttpFirewall 默认校验。
+ firewall.setAllowSemicolon(true);
+ return firewall;
+ }
+
+ @Override
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+ auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
+ }
+
@Override
protected void configure(HttpSecurity http) throws Exception {
ExpressionUrlAuthorizationConfigurer
.ExpressionInterceptUrlRegistry registry = http
diff --git a/hiver-core/src/main/java/cc/hiver/core/entity/Worker.java b/hiver-core/src/main/java/cc/hiver/core/entity/Worker.java
index fd978bdc..6a8178fe 100644
--- a/hiver-core/src/main/java/cc/hiver/core/entity/Worker.java
+++ b/hiver-core/src/main/java/cc/hiver/core/entity/Worker.java
@@ -210,4 +210,8 @@ public class Worker implements Serializable {
@ApiModelProperty(value = "超出3楼额外费用")
@Column(name = "high_floor_fee")
private BigDecimal highFloorFee;
+
+ @ApiModelProperty(value = "送达地点:0送上楼 1送到宿舍门口")
+ @Column(name = "delivery_location")
+ private Integer deliveryLocation;
}
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
index bb94c05a..a59c9bbe 100644
--- 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
@@ -1,14 +1,17 @@
package cc.hiver.mall.controller;
+import cc.hiver.core.common.redis.RedisTemplateHelper;
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.dto.ShopCacheDTO;
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 cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
@@ -28,12 +31,101 @@ import java.util.List;
@Transactional
public class AdminSeckillGroupController {
+ public static final String GROUP_DELIVERY_TIME_KEY_PREFIX = "GROUP_DELIVERY_TIME:";
+ public static final int DEFAULT_GROUP_DELIVERY_TIME = 30;
+
@Autowired
private SeckillGroupCategoryService seckillGroupCategoryService;
@Autowired
private SeckillGroupProductService seckillGroupProductService;
+ @Autowired
+ private RedisTemplateHelper redisTemplateHelper;
+
+ @RequestMapping(value = "/deliveryTime/save", method = RequestMethod.POST)
+ @ApiOperation(value = "保存区域拼团配送时长")
+ public Result saveDeliveryTime(@RequestBody DeliveryTimeSetting setting) {
+ if (setting == null || StringUtils.isBlank(setting.getRegionId())) {
+ return ResultUtil.error("区域ID不能为空");
+ }
+ if (setting.getDeliveryTime() == null || setting.getDeliveryTime() < 0) {
+ return ResultUtil.error("配送时长必须为非负整数");
+ }
+ redisTemplateHelper.set(buildDeliveryTimeKey(setting.getRegionId()), String.valueOf(setting.getDeliveryTime()));
+ return ResultUtil.success("保存成功");
+ }
+
+ @RequestMapping(value = "/deliveryTime/get", method = RequestMethod.GET)
+ @ApiOperation(value = "查询区域拼团配送时长")
+ public Result getDeliveryTime(@RequestParam(value = "regionId") String regionId) {
+ return new ResultUtil().setData(getDeliveryTimeByRegionId(redisTemplateHelper, regionId));
+ }
+
+ public static Integer getDeliveryTimeByRegionId(RedisTemplateHelper redisTemplateHelper, String regionId) {
+ if (redisTemplateHelper == null || StringUtils.isBlank(regionId)) {
+ return DEFAULT_GROUP_DELIVERY_TIME;
+ }
+ String value = redisTemplateHelper.get(buildDeliveryTimeKey(regionId));
+ if (StringUtils.isBlank(value)) {
+ return DEFAULT_GROUP_DELIVERY_TIME;
+ }
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ return DEFAULT_GROUP_DELIVERY_TIME;
+ }
+ }
+
+ public static Integer getTotalDeliveryTimeByRegionIdAndShopId(RedisTemplateHelper redisTemplateHelper, String regionId, String shopId) {
+ return getShopCookingTimeByRegionIdAndShopId(redisTemplateHelper, regionId, shopId)
+ + getDeliveryTimeByRegionId(redisTemplateHelper, regionId);
+ }
+
+ public static Integer getShopCookingTimeByRegionIdAndShopId(RedisTemplateHelper redisTemplateHelper, String regionId, String shopId) {
+ if (redisTemplateHelper == null || StringUtils.isBlank(regionId) || StringUtils.isBlank(shopId)) {
+ return 0;
+ }
+ Object value = redisTemplateHelper.hGet("SHOP_CACHE:" + regionId, shopId);
+ if (value == null) {
+ return 0;
+ }
+ try {
+ ShopCacheDTO dto = JSONUtil.toBean(value.toString(), ShopCacheDTO.class);
+ if (dto != null && dto.getShopTakeaway() != null && dto.getShopTakeaway().getCookingTime() != null) {
+ return dto.getShopTakeaway().getCookingTime();
+ }
+ } catch (Exception e) {
+ log.warn("读取店铺出餐时间缓存失败 regionId={}, shopId={}", regionId, shopId, e);
+ }
+ return 0;
+ }
+
+ public static String buildDeliveryTimeKey(String regionId) {
+ return GROUP_DELIVERY_TIME_KEY_PREFIX + regionId;
+ }
+
+ public static class DeliveryTimeSetting {
+ private String regionId;
+ private Integer deliveryTime;
+
+ public String getRegionId() {
+ return regionId;
+ }
+
+ public void setRegionId(String regionId) {
+ this.regionId = regionId;
+ }
+
+ public Integer getDeliveryTime() {
+ return deliveryTime;
+ }
+
+ public void setDeliveryTime(Integer deliveryTime) {
+ this.deliveryTime = deliveryTime;
+ }
+ }
+
@RequestMapping(value = "/category/save", method = RequestMethod.POST)
@ApiOperation(value = "新增秒杀团分类")
public Result saveCategory(@RequestBody SeckillGroupCategory category) {
diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallDeliveryOrderController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallDeliveryOrderController.java
index e0c6053a..8462b7c5 100644
--- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallDeliveryOrderController.java
+++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallDeliveryOrderController.java
@@ -11,6 +11,7 @@ import cc.hiver.core.serviceimpl.JPushServiceImpl;
import cc.hiver.mall.entity.MallAdPosition;
import cc.hiver.mall.entity.MallDeliveryOrder;
import cc.hiver.mall.entity.MallOrder;
+import cc.hiver.mall.mq.OrderAsyncProducer;
import cc.hiver.mall.pojo.query.MallDeliveryOrderPageQuery;
import cc.hiver.mall.pojo.vo.WorkerRedisVo;
import cc.hiver.mall.pojo.vo.WorkerRewardVO;
@@ -25,6 +26,7 @@ import cc.hiver.mall.utils.WorkerRedisCacheUtil;
import cc.hiver.mall.utils.WorkerRewardCacheUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -33,6 +35,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -80,6 +83,9 @@ public class MallDeliveryOrderController {
@Autowired
private HomeReminderService homeReminderService;
+ @Autowired
+ private OrderAsyncProducer orderAsyncProducer;
+
/**
* 分页查询配送单
* hallOnly=true 时查询抢单大厅(未被接单的单)
@@ -147,7 +153,7 @@ public class MallDeliveryOrderController {
}
}
}
- cacheResult.put("homeReminders", homeReminderService.homeReminders(currentUserId(), q.getRegionId()));
+ cacheResult.put("homeReminders", homeReminderService.homeReminders(StrUtil.blankToDefault(q.getUserId(), currentUserId()), q.getRegionId()));
return new ResultUtil