feat(order): 重构核销码逻辑并优化订单查询接口

- 核销码生成与验证逻辑重构,支持按订单项生成多个核销码- 新增核销码状态枚举,完善状态管理(待核销、已核销、已过期、已取消)
- 优化商户端订单列表查询逻辑,区分基于订单和订单项的租户查询
- 更新微信小程序配置appId与secret
- 订单商品查询改为使用sku快照,确保数据一致性
This commit is contained in:
huk 2025-09-23 16:02:08 +08:00
parent a74ed5dde0
commit eeb50d18b1
25 changed files with 325 additions and 371 deletions

View File

@ -70,13 +70,4 @@ public class AppOrderController {
return R.ok();
}
@Operation(summary = "生成核销码")
@GetMapping("/generate/{orderId}")
@Tag(name = "生成核销码")
public R generateCode(@PathVariable Long orderId) {
return verificationCodeService.generateVerificationCode(orderId);
}
}

View File

@ -8,7 +8,7 @@ import com.wzj.soopin.order.domain.vo.OrderVO;
import com.wzj.soopin.order.service.IMerchantOrderService;
import com.wzj.soopin.order.service.VerificationCodeService;
import io.swagger.annotations.Api;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
@ -39,41 +39,24 @@ public class AppMerchantOrderController {
* @return 核销结果
*/
@GetMapping("/verify")
@Tag(name = "核销接口")
@Operation(summary = "扫码核销接口")
public R verifyCode(@RequestParam("code") String code) {
LoginUser loginUser = LoginHelper.getLoginUser();
if (loginUser == null) {
return R.fail("用户未登录");
}
if(loginUser.getTenantId() == null){
return R.fail("用户不是商户");
}
verificationCodeService.verifyCode(code, loginUser.getTenantId());
verificationCodeService.verifyCode(code);
return R.ok("核销成功");
}
/**
* 扫码核销接口
* 查询核销码对应的订单
* @return 核销结果
*/
@GetMapping("/scan")
@Tag(name = "扫码接口")
public R scan(@RequestParam("code") String code) {
LoginUser loginUser = LoginHelper.getLoginUser();
if (loginUser == null) {
return R.fail("用户未登录");
}
if(loginUser.getTenantId() == null){
return R.fail("用户不是商户");
}
@Operation(summary = "查询核销码对应的订单")
public R<OrderVO> scan(@RequestParam("code") String code) {
Order order = verificationCodeService.scan(code);
return R.ok(orderBusiness.info(order.getId()));
}
@Tag(name ="查询订单列表")
@PostMapping("/page")
@Operation(summary = "查询当前商家订单列表")
public R<IPage<OrderVO>> page(@RequestBody OrderBo bo){
LoginUser loginUser= LoginHelper.getLoginUser();
bo.setTenantId(loginUser.getTenantId());

View File

@ -385,8 +385,10 @@ wechat:
app-id: wxebcdaea31881caab # 应用ID
secret: your_wechat_secret # 应用密钥
mini-program:
app-id: wx87a5db19138da60d
secret: 856ca8bae38ccaecc1353c9abedf6b41
# app-id: wx87a5db19138da60d
# secret: 856ca8bae38ccaecc1353c9abedf6b41
app-id: wx2fb87f0f1f05d314
secret: 86fbcab880e4066ac5c75af6f4f003c2
http:
client:

View File

@ -4,6 +4,11 @@ package org.dromara.common.core.enums;
* 数字编码前缀扫码用
*/
public enum CodePrefix {
/**
* 订单
*/
DD,
/**
* 核销
*/

View File

@ -61,4 +61,16 @@ public final class ServiceException extends RuntimeException {
this.detailMessage = detailMessage;
return this;
}
public static ServiceException of(String message) {
return new ServiceException(message);
}
public static ServiceException of(Integer code, String message) {
return new ServiceException(message, code);
}
public static ServiceException of(ResultCode resultCode) {
return new ServiceException(resultCode);
}
}

View File

@ -1,13 +1,8 @@
package com.wzj.soopin.order.controller;
import com.wzj.soopin.order.service.VerificationCodeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@ -23,18 +18,4 @@ import org.springframework.web.bind.annotation.RestController;
public class VerificationCodeController {
private final VerificationCodeService verificationCodeService;
/**
* 生成核销码
*/
@GetMapping("/generate/{orderId}")
@Operation(summary = "生成核销码")
public R generateCode(@PathVariable Long orderId) {
return verificationCodeService.generateVerificationCode(orderId);
}
}

View File

@ -125,7 +125,6 @@ public class Order extends BaseAudit implements Serializable {
@Excel(name = "支付时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime paymentTime;
@Schema(description = "优惠券ID")
private Long memberCouponId;
@ -144,7 +143,6 @@ public class Order extends BaseAudit implements Serializable {
@Schema(description = "提现状态1->等待转账2->转帐中;3->转账成功;4->转账失败")
private Integer withdrawStatus;
@Schema(description = "配送方式 1->到店核销2->自提;3->配送;")
private Integer distribution;

View File

@ -3,15 +3,20 @@ package com.wzj.soopin.order.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.wzj.soopin.order.emum.VerificationCodeStatus;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.*;
import org.dromara.common.core.domain.model.BaseAudit;
import java.time.LocalDateTime;
@EqualsAndHashCode(callSuper = true)
@Schema(description = "核销码表对象")
@Data
@TableName("oms_verification_codes")
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class VerificationCodes extends BaseAudit {
@Schema(description = "ID")
@ -24,7 +29,13 @@ public class VerificationCodes extends BaseAudit {
@Schema(description = "关联订单id")
private Long orderId;
@Schema(description = "是否使用")
@Schema(description = "订单子项id")
private Long orderItemId;
/**
* 状态 {@link VerificationCodeStatus}
*/
@Schema(description = "状态")
private Integer status;
@Schema(description = "用户id")

View File

@ -1,38 +0,0 @@
package com.wzj.soopin.order.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.dromara.common.core.domain.model.BaseAudit;
import java.time.LocalDateTime;
@Schema(description = "核销日志表对象")
@Data
@TableName("oms_verification_logs")
public class VerificationLogs extends BaseAudit {
@Schema(description = "ID")
@TableId(type = IdType.ASSIGN_ID)
private Long id;
@Schema(description = "核销码id")
private Long codeId;
@Schema(description = "订单id")
private Long orderId;
@Schema(description = "商家ID")
private Long merchantId;
@Schema(description = "核销时间")
private LocalDateTime verificationTime;
@Schema(description = "核销结果")
private Integer result;
@Schema(description = "失败原因")
private String reason;
}

View File

@ -4,15 +4,15 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 订单状态0->待付款1->待核销2->已支付3->已关闭4->退款中5->已退款 6->已取消 9->已分账10->无效订单 精确匹配
* 订单状态0->待付款1->支付中2->待核销3->已关闭4->退款中5->已退款 6->已取消 9->已分账10->无效订单 精确匹配
*/
@Getter
@AllArgsConstructor
public enum OrderStatusEnum {
UNPAID(0, "待支付"),
VERIFY(1, "待核销"),
PAID(2, "已支付"),
PAYMENT(1, "支付中"),
VERIFY(2, "待核销"),
CLOSED(3, "已关闭"),
REFUNDED(4, "退款中"),
REFUNDED_SUCCESS(5, "已退款"),

View File

@ -0,0 +1,30 @@
package com.wzj.soopin.order.emum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 核销码状态枚举
*/
@Getter
@AllArgsConstructor
public enum VerificationCodeStatus {
TO_BE_USED(0, "待核销"),
USED(1, "已核销"),
EXPIRED(2, "已过期"),
CANCELLED(3, "已取消");
private final Integer code;
private final String description;
public static VerificationCodeStatus getByCode(Integer code) {
for (VerificationCodeStatus value : VerificationCodeStatus.values()) {
if (value.code.equals(code)) {
return value;
}
}
return null;
}
}

View File

@ -60,4 +60,7 @@ public interface OrderItemMapper extends BaseMapper<OrderItem> {
"ORDER BY total_quantity DESC " +
"LIMIT #{limit}")
List<Map<String, Object>> selectTopTradingProducts(int limit);
@Select("SELECT * FROM oms_order_item WHERE order_id = #{orderId}")
List<OrderItem> findByOrderId(Long orderId);
}

View File

@ -35,7 +35,21 @@ public interface OrderMapper extends BaseMapper<Order> {
List<ManagerOrderVO> selectManagerOrderPage(ManagerOrderQueryForm request);
IPage<OrderVO> getlist(@Param("page") Page<Order> page,@Param("query") OrderBo query);
/**
* 通过订单所属租户查询订单列表
* @param page
* @param query
* @return
*/
IPage<OrderVO> getListByOrderTenant(@Param("page") Page<Order> page, @Param("query") OrderBo query);
/**
* 通过订单所属租户查询订单列表
* @param page
* @param query
* @return
*/
IPage<OrderVO> getListByOrderItemTenant(@Param("page") Page<Order> page, @Param("query") OrderBo query);
@Select("SELECT " +
"COUNT(*) AS count1, " +

View File

@ -3,6 +3,8 @@ package com.wzj.soopin.order.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wzj.soopin.order.domain.entity.VerificationCodes;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.time.LocalDateTime;
import java.util.Map;
@ -16,4 +18,7 @@ public interface VerificationCodesMapper extends BaseMapper<VerificationCodes> {
Map<String, Object> getProduvtNameAndMemberId(String code);
String getReason(String code);
@Select("select * from oms_verification_codes where code = #{code} limit 1")
VerificationCodes getByCode(@Param("code") String code);
}

View File

@ -1,9 +0,0 @@
package com.wzj.soopin.order.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wzj.soopin.order.domain.entity.VerificationLogs;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface VerificationLogsMapper extends BaseMapper<VerificationLogs> {
}

View File

@ -3,19 +3,19 @@ package com.wzj.soopin.order.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.wzj.soopin.order.domain.entity.Order;
import com.wzj.soopin.order.domain.entity.VerificationCodes;
import com.wzj.soopin.order.domain.vo.ManagerOrderDetailVO;
import org.dromara.common.core.domain.R;
import java.util.List;
public interface VerificationCodeService extends IService<VerificationCodes> {
R generateVerificationCode(Long orderId);
void generateVerificationCode(Order order);
void verifyCode(String code,String tenantId);
void verifyCode(String code);
Order scan(String code);
List<VerificationCodes> getByOrderId(Long orderId);
void Invalid(Long orderId, List<Long> orderItemIdList);
}

View File

@ -1,7 +0,0 @@
package com.wzj.soopin.order.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.wzj.soopin.order.domain.entity.VerificationLogs;
public interface VerificationLogsService extends IService<VerificationLogs> {
}

View File

@ -1,25 +1,21 @@
package com.wzj.soopin.order.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wzj.soopin.order.domain.entity.OrderItem;
import com.wzj.soopin.order.mapper.OrderItemMapper;
import com.wzj.soopin.order.service.OrderItemService;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.redis.redis.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wzj.soopin.content.utils.RedisOperator;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
/**
* 订单中所包含的商品Service业务层处理
*
@ -35,8 +31,7 @@ public class OrderItemServiceImpl extends ServiceImpl<OrderItemMapper, OrderItem
@Override
public List<OrderItem> findByOrderId(Long orderId) {
return baseMapper.selectList(new QueryWrapper<OrderItem>().lambda()
.eq(OrderItem::getOrderId, orderId));
return baseMapper.findByOrderId(orderId);
}
@Override

View File

@ -16,8 +16,10 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wzj.soopin.goods.domain.entity.Product;
import com.wzj.soopin.goods.domain.entity.Sku;
import com.wzj.soopin.goods.domain.entity.SkuSnapshot;
import com.wzj.soopin.goods.mapper.ProductMapper;
import com.wzj.soopin.goods.mapper.SkuMapper;
import com.wzj.soopin.goods.mapper.SkuSnapshotMapper;
import com.wzj.soopin.member.domain.po.Member;
import com.wzj.soopin.member.mapper.MemberMapper;
import com.wzj.soopin.order.domain.bo.OrderBo;
@ -31,6 +33,7 @@ import com.wzj.soopin.order.emum.AftersaleStatus;
import com.wzj.soopin.order.emum.OrderStatusEnum;
import com.wzj.soopin.order.mapper.*;
import com.wzj.soopin.order.service.OrderService;
import com.wzj.soopin.order.service.VerificationCodeService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
@ -41,6 +44,7 @@ import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.SysTenant;
import org.dromara.system.mapper.SysTenantMapper;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
@ -68,20 +72,22 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
private final SkuMapper skuMapper;
private final SkuSnapshotMapper skuSnapshotMapper;
private final ProductMapper productMapper;
private final OrderOperateHistoryMapper orderOperateHistoryMapper;
private final MemberMapper memberMapper;
private final OrderDeliveryHistoryMapper orderDeliveryHistoryMapper;
private final SysTenantMapper sysTenantMapper;
private final AftersaleMapper aftersaleMapper;
private final AftersaleItemMapper aftersaleItemMapper;
private final VerificationCodeService verificationCodeService;
/**
* 订单前缀
*/
@ -100,7 +106,6 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
Order order = orderMapper.selectById(id);
Assert.notNull(order, () -> new ServiceException("无该订单信息"));
ManagerOrderDetailVO managerOrderDetailVO = new ManagerOrderDetailVO();
//封装订单信息
managerOrderDetailVO.setOrderId(id);
@ -139,11 +144,8 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
}
}
QueryWrapper<OrderItem> qw = new QueryWrapper<>();
qw.eq("order_id", order.getId());
List<OrderItem> orderItemList = orderItemMapper.selectList(qw);
List<ManagerOrderProductVO> productList = new ArrayList<>();
orderItemList.forEach(item -> {
List<OrderItem> orderItemList = orderItemMapper.findByOrderId(id);
List<ManagerOrderProductVO> productList = orderItemList.stream().map(item -> {
ManagerOrderProductVO productVO = new ManagerOrderProductVO();
productVO.setProductId(item.getProductId());
productVO.setBuyNum(item.getQuantity());
@ -151,8 +153,8 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
productVO.setProductName(item.getProductName());
productVO.setSalePrice(item.getSalePrice());
productVO.setSpData(item.getSpData());
productList.add(productVO);
});
return productVO;
}).toList();
managerOrderDetailVO.setProductInfo(productList);
return managerOrderDetailVO;
@ -281,6 +283,8 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
.payAmount(order.getPayAmount().subtract(refundBO.getTotalRefundAmount()))
.status(OrderStatusEnum.REFUNDED_SUCCESS.getValue())
.build());
// 修改核销码记录
verificationCodeService.Invalid(orderId, refundItemBoList.stream().map(RefundBO.RefundItemBO::getOrderItemId).toList());
// 更新售后记录
Aftersale aftersale = aftersaleMapper.selectOne(Wrappers.lambdaQuery(Aftersale.class)
.eq(Aftersale::getOrderId, orderId).last("limit 1"));
@ -409,23 +413,43 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
}
}
/**
* 商户所属订单列表
* @param query
* @return
*/
@Override
public IPage<OrderVO> merchantOrderList(OrderBo query) {
return null;
Assert.isTrue(query.getCurrent() != null && query.getSize() != null, "分页参数不能为空");
if(StrUtil.isBlank(query.getTenantId())){
query.setTenantId(TenantHelper.getTenantId());
}
IPage<OrderVO> resultPage = TenantHelper.ignore(() -> orderMapper.getListByOrderItemTenant(Page.of(query.getCurrent(), query.getSize()), query));
return getOrderVOIPage(resultPage);
}
/**
* 团长所属订单列表
* @param query
* @return
*/
@Override
public IPage<OrderVO> referenceMemberOrderList(OrderBo query) {
Assert.isTrue(query.getCurrent() != null && query.getSize() != null, "分页参数不能为空");
if(StrUtil.isBlank(query.getTenantId())){
query.setTenantId(TenantHelper.getTenantId());
}
IPage<OrderVO> resultPage = TenantHelper.ignore(() -> orderMapper.getlist(Page.of(query.getCurrent(), query.getSize()), query));
IPage<OrderVO> resultPage = TenantHelper.ignore(() -> orderMapper.getListByOrderTenant(Page.of(query.getCurrent(), query.getSize()), query));
return getOrderVOIPage(resultPage);
}
@NotNull
private IPage<OrderVO> getOrderVOIPage(IPage<OrderVO> resultPage) {
List<OrderVO> orderVOList = resultPage.getRecords();
if (orderVOList.isEmpty()) {
return resultPage;
}
// 获取所有订单id
List<Long> orderIds = orderVOList.stream()
.map(OrderVO::getId)
@ -440,42 +464,37 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
Map<Long, List<OrderItem>> orderItemMapByOrderId = orderItemList.stream()
.collect(Collectors.groupingBy(OrderItem::getOrderId));
// 获取所有sku_id
List<Long> skuIds = orderItemList.stream()
.map(OrderItem::getSkuId)
// 获取所有sku的快照id
List<Long> skuSnapshotIds = orderItemList.stream()
.map(OrderItem::getSkuSnapshotId)
.collect(Collectors.toList());
// 根据sku_id查询pms_sku表数据
QueryWrapper<Sku> skuQueryWrapper = new QueryWrapper<>();
skuQueryWrapper.in("id", skuIds);
List<Sku> skuList = skuMapper.selectList(skuQueryWrapper);
Map<Long, Sku> skuMapById = skuList.stream()
.collect(Collectors.toMap(Sku::getId, sku -> sku));
Map<Long, SkuSnapshot> skuSnapshotMap = skuSnapshotMapper.selectByIds(skuSnapshotIds).stream().collect(Collectors.toMap(SkuSnapshot::getId, ss -> ss));
for (OrderVO orderVO : orderVOList) {
List<Map<String, Object>> productList = new ArrayList<>();
List<OrderItem> orderItems = orderItemMapByOrderId.get(orderVO.getId());
if (orderItems != null) {
for (OrderItem orderItem : orderItems) {
Sku sku = skuMapById.get(orderItem.getSkuId());
if (sku != null) {
SkuSnapshot skuSnapshot = skuSnapshotMap.get(orderItem.getSkuSnapshotId());
if (skuSnapshot != null) {
Map<String, Object> productMap = new HashMap<>();
productMap.put("price", sku.getPrice());
productMap.put("pic", sku.getPic());
productMap.put("stock", sku.getStock());
productMap.put("spData", sku.getSpData());
productMap.put("price", skuSnapshot.getPrice());
productMap.put("pic", skuSnapshot.getPic());
productMap.put("spData", skuSnapshot.getSpData());
productList.add(productMap);
}
}
}
orderVO.setProductList(productList);
}
return resultPage;
}
@Override
public Order getByNo(String orderNo) {
return baseMapper.selectOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderSn, orderNo));

View File

@ -1,22 +1,25 @@
package com.wzj.soopin.order.service.impl;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.text.StrBuilder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wzj.soopin.order.domain.entity.Order;
import com.wzj.soopin.order.domain.entity.OrderItem;
import com.wzj.soopin.order.domain.entity.VerificationCodes;
import com.wzj.soopin.order.domain.vo.ManagerOrderDetailVO;
import com.wzj.soopin.order.emum.OrderStatusEnum;
import com.wzj.soopin.order.emum.VerificationCodeStatus;
import com.wzj.soopin.order.mapper.OrderItemMapper;
import com.wzj.soopin.order.mapper.OrderMapper;
import com.wzj.soopin.order.mapper.VerificationCodesMapper;
import com.wzj.soopin.order.mapper.VerificationLogsMapper;
import com.wzj.soopin.order.service.OrderService;
import com.wzj.soopin.order.service.VerificationCodeService;
import com.wzj.soopin.order.utils.QrCodeGenerator;
import com.wzj.soopin.order.utils.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.enums.CodePrefix;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@ -31,196 +34,105 @@ import java.util.Objects;
@RequiredArgsConstructor
public class VerificationCodeServiceImpl extends ServiceImpl<VerificationCodesMapper, VerificationCodes> implements VerificationCodeService{
private final VerificationCodesMapper codeMapper;
private final OrderMapper orderMapper;
private final QrCodeGenerator qrCodeGenerator;
private final VerificationLogsMapper logMapper;
private final OrderService orderService;
private final OrderMapper orderMapper;
private final OrderItemMapper orderItemMapper;
/**
* 生成核销码并关联订单
*
* @return
* 生成关联订单的核销码
*/
@Override
public R generateVerificationCode(Long orderId) {
// 校验订单状态
Order order = orderMapper.selectById(orderId);
if (order == null || !Objects.equals(order.getStatus(), OrderStatusEnum.VERIFY.getValue())) {
return R.fail("订单状态异常");
}
// 生成唯一核销码
String codeValue = generateUniqueCode();
order.setCode(codeValue);
// 核销码过期时间7天后过期
public void generateVerificationCode(Order order) {
List<OrderItem> orderItems = orderItemMapper.findByOrderId(order.getId());
LocalDateTime expireTime = LocalDateTime.now().plusDays(7);
// 保存核销码到数据库
VerificationCodes code = new VerificationCodes();
code.setMemberId(order.getMemberId());
code.setMemberName(order.getMemberUsername());
code.setCode(codeValue);
code.setOrderId(orderId);
code.setExpireTime(expireTime);
code.setCreateTime(LocalDateTime.now());
// 未使用状态
code.setStatus(0);
int insertResult = codeMapper.insert(code);
if (insertResult != 1) {
return R.fail("核销码生成失败");
}
// // 构建保存路径
// String filePath = "qrcode/" + orderId + ".png";
// String qrCodeUrl = qrCodeGenerator.generateQrCode(
// "核销码:" + codeValue + ",订单ID:" + orderId,
// 200,
// 200,
// filePath
// );
// // 打印生成后的二维码URL
// log.info("生成的二维码URL: {}", qrCodeUrl);
// if (qrCodeUrl == null) {
// log.error("二维码生成失败");
// }
// // 将二维码URL保存到订单表中
// order.setCodeUrl(qrCodeUrl);
orderMapper.updateById(order);
return R.ok("核销码生成成功", code);
List<VerificationCodes> verificationCodesList = orderItems.stream().map(orderItem -> VerificationCodes
.builder()
.orderId(order.getId())
.orderItemId(orderItem.getId())
.code(StrBuilder.create(CodePrefix.HXM.name()).append("-").append(UUID.fastUUID().toString(true).toUpperCase()).toString())
.expireTime(expireTime)
.memberId(order.getMemberId())
.memberName(order.getMemberUsername())
.status(VerificationCodeStatus.TO_BE_USED.getCode())
.build()
).toList();
codeMapper.insert(verificationCodesList);
}
/**
* 核销订单
* 查询核销码对应的订单
* @param code
*/
@Override
public Order scan(String code) {
// 查询核销码
// 参数校验
if (StringUtils.isBlank(code) ) {
throw new IllegalArgumentException("参数不能为空");
}
// 查询核销码
VerificationCodes verificationCodes = codeMapper.selectOne(
new LambdaQueryWrapper<VerificationCodes>().eq(VerificationCodes::getCode, code)
);
if (verificationCodes == null) {
throw new IllegalArgumentException("核销码不存在");
}
// 检查核销码状态
if (verificationCodes.getStatus() == 1) {
throw new IllegalArgumentException("核销码已使用");
}
if (verificationCodes.getStatus() == 2) {
throw new IllegalArgumentException("核销码已过期");
}
VerificationCodes verificationCodes = codeMapper.getByCode(code);
Assert.notNull(verificationCodes, () -> ServiceException.of("核销码不存在"));
OrderItem orderItem = orderItemMapper.selectById(verificationCodes.getOrderItemId());
Assert.notNull(orderItem, () -> ServiceException.of("订单项不存在"));
// 仅所属商家有资格查询该核销码的订单
String tenantId = LoginHelper.getTenantId();
Assert.isTrue(orderItem.getTenantId().equals(tenantId), () -> ServiceException.of("无权查询该核销码的订单"));
Assert.isTrue(verificationCodes.getStatus().equals(VerificationCodeStatus.TO_BE_USED.getCode()), () -> ServiceException.of("核销码" + VerificationCodeStatus.getByCode(verificationCodes.getStatus()).getDescription()));
// 检查核销码是否过期
if (LocalDateTime.now().isAfter(verificationCodes.getExpireTime())) {
// 更新状态为已过期
verificationCodes.setStatus(3);
verificationCodes.setStatus(VerificationCodeStatus.EXPIRED.getCode());
verificationCodes.setReason("核销码已过期");
codeMapper.updateById(verificationCodes);
throw new IllegalArgumentException("核销码已过期");
throw ServiceException.of("核销码已过期");
}
// 检查订单状态
Order order = orderMapper.selectById(verificationCodes.getOrderId());
if (order == null || !Objects.equals(order.getStatus(), OrderStatusEnum.VERIFY.getValue())) {
throw new IllegalArgumentException("订单状态异常,无法核销");
}
if (verificationCodes.getStatus() == 1 || verificationCodes.getVerificationTime() != null) {
throw new IllegalArgumentException("该订单已被核销,核销时间:" + verificationCodes.getVerificationTime());
}
Assert.isTrue(order != null && Objects.equals(order.getStatus(), OrderStatusEnum.VERIFY.getValue()), () -> ServiceException.of("订单状态异常,无法核销"));
return order;
}
@Override
public void verifyCode(String code,String tenantId) {
// 参数校验
if (StringUtils.isBlank(code) || tenantId == null) {
throw new IllegalArgumentException("参数不能为空");
}
// 查询核销码
VerificationCodes verificationCodes = codeMapper.selectOne(
new LambdaQueryWrapper<VerificationCodes>().eq(VerificationCodes::getCode, code)
);
if (verificationCodes == null) {
throw new IllegalArgumentException("核销码不存在");
}
// 检查核销码状态
if (verificationCodes.getStatus() == 1) {
throw new IllegalArgumentException("核销码已使用");
}
if (verificationCodes.getStatus() == 2) {
throw new IllegalArgumentException("核销码已过期");
}
// 检查订单状态
Order order = orderMapper.selectById(verificationCodes.getOrderId());
if (order == null || order.getStatus() != 6) {
throw new IllegalArgumentException("订单状态异常,无法核销");
}
public void verifyCode(String code) {
VerificationCodes verificationCodes = codeMapper.getByCode(code);
Assert.notNull(verificationCodes, () -> ServiceException.of("核销码不存在"));
OrderItem orderItem = orderItemMapper.selectById(verificationCodes.getOrderItemId());
Assert.notNull(orderItem, () -> ServiceException.of("订单项不存在"));
// 仅所属商家有资格查询该核销码的订单
String tenantId = LoginHelper.getTenantId();
Assert.isTrue(orderItem.getTenantId().equals(tenantId), () -> ServiceException.of("无权查询该核销码的订单"));
Assert.isTrue(verificationCodes.getStatus().equals(VerificationCodeStatus.TO_BE_USED.getCode()), () -> ServiceException.of("核销码" + VerificationCodeStatus.getByCode(verificationCodes.getStatus()).getDescription()));
// 检查核销码是否过期
if (LocalDateTime.now().isAfter(verificationCodes.getExpireTime())) {
// 更新状态为已过期
verificationCodes.setStatus(3);
verificationCodes.setStatus(VerificationCodeStatus.EXPIRED.getCode());
verificationCodes.setReason("核销码已过期");
codeMapper.updateById(verificationCodes);
throw new IllegalArgumentException("核销码已过期");
throw ServiceException.of("核销码已过期");
}
if (verificationCodes.getStatus() == 1 || verificationCodes.getVerificationTime() != null) {
throw new IllegalArgumentException("该订单已被核销,核销时间:" + verificationCodes.getVerificationTime());
}
// 执行核销
verificationCodes.setStatus(1);
verificationCodes.setStatus(VerificationCodeStatus.USED.getCode());
verificationCodes.setUsedTime(LocalDateTime.now());
verificationCodes.setUsedMerchantId(tenantId);
verificationCodes.setVerificationTime(LocalDateTime.now());
verificationCodes.setResult(1);
int result = codeMapper.updateById(verificationCodes);
if (result != 1) {
throw new IllegalArgumentException("核销失败,请重试");
}
// 更新订单状态
order.setStatus(7);
order.setUsedTime(LocalDateTime.now());
orderMapper.updateById(order);
codeMapper.updateById(verificationCodes);
}
/**
* 生成唯一核销码
*/
private String generateUniqueCode() {
String uuid = java.util.UUID.randomUUID().toString().replaceAll("-", "");
return CodePrefix.HXM.name()+"-" + uuid.substring(0, 16).toUpperCase();
}
@Override
public List<VerificationCodes> getByOrderId(Long orderId) {
LambdaQueryWrapper<VerificationCodes> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(VerificationCodes::getOrderId, orderId);
return codeMapper.selectList(wrapper);
}
/**
* 作废核销码
* @param orderId 订单id
* @param orderItemIdList 订单项id列表
*/
@Override
public void Invalid(Long orderId, List<Long> orderItemIdList) {
codeMapper.update(Wrappers.lambdaUpdate(VerificationCodes.class)
.eq(VerificationCodes::getOrderId, orderId)
.in(VerificationCodes::getOrderItemId, orderItemIdList)
.set(VerificationCodes::getStatus, VerificationCodeStatus.CANCELLED.getCode()));
}
}

View File

@ -1,14 +0,0 @@
package com.wzj.soopin.order.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wzj.soopin.order.domain.entity.VerificationLogs;
import com.wzj.soopin.order.mapper.VerificationLogsMapper;
import com.wzj.soopin.order.service.VerificationLogsService;
import org.springframework.stereotype.Service;
/**
* @author fengxiaohang
*/
@Service
public class VerificationLogsServiceImpl extends ServiceImpl<VerificationLogsMapper, VerificationLogs> implements VerificationLogsService {
}

View File

@ -257,7 +257,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select>
<select id="getlist" resultType="com.wzj.soopin.order.domain.vo.OrderVO">
<select id="getListByOrderTenant" resultType="com.wzj.soopin.order.domain.vo.OrderVO">
SELECT
o.id,
o.member_id,
@ -287,7 +287,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
LEFT JOIN ums_member um ON o.member_id = um.id
LEFT JOIN sys_tenant st ON o.tenant_id = st.tenant_id
LEFT JOIN oms_order_item oi ON o.id = oi.order_id
LEFT JOIN pms_product pp ON oi.product_id = pp.id AND pp.publish_status = 1
LEFT JOIN pms_product_snapshot pp ON oi.product_id = pp.id
<where>
<if test="query.tenantId != null and query.tenantId != ''">
AND o.tenant_id = #{query.tenantId}
@ -322,4 +322,69 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</where>
</select>
<select id="getListByOrderItemTenant" resultType="com.wzj.soopin.order.domain.vo.OrderVO">
SELECT
o.id,
o.member_id,
o.order_sn,
o.total_amount,
o.pay_amount,
o.status,
o.aftersale,
o.create_time,
o.payment_time,
o.receive_time,
o.receiver_province,
o.receiver_city,
o.receiver_district,
o.receiver_detail_address,
um.nickname AS memberNickname,
um.phone_encrypted AS memberPhoneEncrypted,
st.contact_user_name AS tenantContactName,
st.contact_phone AS tenantContactPhone,
oi.id AS itemId,
oi.product_id AS productId,
pp.brand_id AS productBrandId,
pp.name AS productName,
pp.price AS productPrice
FROM oms_order_item oi
LEFT JOIN oms_order o ON oi.order_id = o.id
LEFT JOIN ums_member um ON o.member_id = um.id
LEFT JOIN sys_tenant st ON o.tenant_id = st.tenant_id
LEFT JOIN pms_product_snapshot pp ON oi.product_id = pp.id
<where>
<if test="query.tenantId != null and query.tenantId != ''">
AND oi.tenant_id = #{query.tenantId}
</if>
<if test="query.orderSn != null and query.orderSn != ''">
AND o.order_sn LIKE CONCAT('%', #{query.orderSn}, '%')
</if>
<if test="query.memberId != null and query.memberId != ''">
AND o.member_id LIKE CONCAT('%', #{query.memberId}, '%')
</if>
<if test="query.memberPhoneEncrypted != null and query.memberPhoneEncrypted != ''">
AND um.phone_encrypted LIKE CONCAT('%', #{query.memberPhoneEncrypted}, '%')
</if>
<if test="query.status != null">
AND o.status = #{query.status}
</if>
<if test="query.type != null">
AND o.type = #{query.type}
</if>
<if test="query.withdrawStatus != null and query.withdrawStatus != ''">
AND o.withdraw_status LIKE CONCAT('%', #{query.withdrawStatus}, '%')
</if>
<if test="query.aftersale != null and query.aftersale != ''">
AND o.aftersale LIKE CONCAT('%', #{query.aftersale}, '%')
</if>
<if test="query.startTime != null">
AND o.create_time >= #{query.startTime, jdbcType=TIMESTAMP}
</if>
<if test="query.endTime != null">
AND o.create_time &lt;= #{query.endTime, jdbcType=TIMESTAMP}
</if>
</where>
</select>
</mapper>

View File

@ -59,7 +59,7 @@ public class TransEasypayController {
@SaIgnore
@Operation(summary = "易生支付-发起支付")
public R<EasypayPrePayVO> trade(@RequestBody PaymentBO paymentBO) throws ServerException {
EasypayPrePayVO easypayPrePayVO = easypayService.payment(paymentBO);
EasypayPrePayVO easypayPrePayVO = easypayService.prePayment(paymentBO);
return R.ok(easypayPrePayVO);
}

View File

@ -24,7 +24,7 @@ public interface IEasypayService {
* @param paymentBO
* @return
*/
EasypayPrePayVO payment(PaymentBO paymentBO) throws ServerException;
EasypayPrePayVO prePayment(PaymentBO paymentBO) throws ServerException;
/**
* 查询订单支付结果

View File

@ -25,6 +25,7 @@ import com.wzj.soopin.order.emum.OrderStatusEnum;
import com.wzj.soopin.order.service.AftersaleService;
import com.wzj.soopin.order.service.OrderItemService;
import com.wzj.soopin.order.service.OrderService;
import com.wzj.soopin.order.service.VerificationCodeService;
import com.wzj.soopin.transaction.config.EasypayConfig;
import com.wzj.soopin.transaction.config.WechatMiniProgramConfig;
import com.wzj.soopin.transaction.domain.bo.*;
@ -108,6 +109,8 @@ public class EasypayServiceImpl implements IEasypayService {
private final OrderService orderService;
private final VerificationCodeService verificationCodeService;
private final OrderItemService orderItemService;
private final AftersaleService aftersaleService;
@ -211,7 +214,7 @@ public class EasypayServiceImpl implements IEasypayService {
*/
@Override
@Transactional(rollbackFor = Exception.class)
public EasypayPrePayVO payment(PaymentBO paymentBO) throws ServerException {
public EasypayPrePayVO prePayment(PaymentBO paymentBO) throws ServerException {
checkPaymentParamByPayType(paymentBO);
EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
@ -261,10 +264,10 @@ public class EasypayServiceImpl implements IEasypayService {
payOrderMapper.updateById(payOrder);
//关联订单的最新支付单
orderService.updateById(Order.builder()
.id(payOrder.getOrderId())
.id(paymentBO.getOrderId())
.payId(payOrder.getId())
.payAmount(BigDecimal.valueOf(payOrder.getTransAmount()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP))
.status(OrderStatusEnum.VERIFY.getValue())
.status(OrderStatusEnum.PAYMENT.getValue())
.build());
// 生成返回前端的预支付信息
return generatePrePayVO(paymentBO.getPayType(), jsApiRespBody);
@ -449,22 +452,11 @@ public class EasypayServiceImpl implements IEasypayService {
if (StrUtil.equals(RSP_BODY_RESP_OK, respStateInfo.getRespCode())) {
// 支付完成 进入终态
if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_OK_WETCAT, RSP_BODY_TRANS_PARTIALLY_OK)) {
TradeQueryRespOrderInfo respOrderInfo = tradeQueryRespBody.getRespOrderInfo();
// 更新支付订单信息到数据库
PayOrder payOrderUpdate = PayOrder.builder()
.id(payOrder.getId())
.endTransDate(DateUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()).toString()))
.easypayTrace(respOrderInfo.getOutTrace())
.pcTrace(respOrderInfo.getPcTrace())
.unTrace(respOrderInfo.getUnTrace())
.transState(TransState.PAID.getCode())
.build();
payOrderMapper.updateById(payOrderUpdate);
orderService.updateById(Order.builder()
.id(order.getId())
.status(OrderStatusEnum.PAID.getValue())
.build());
paymentResultVO.setTransState(TransState.PAID.getCode());
updateOrderPaid(payOrder, tradeQueryRespBody.getRespOrderInfo(), order);
} else if (!StrUtil.equals(respStateInfo.getTransState(), RSP_BODY_TRANS_PROCESSING)) {
// 不是成功且不是支付中统一认为支付失败支付状态重置为待支付
orderService.updateById(Order.builder().id(order.getId()).status(OrderStatusEnum.UNPAID.getValue()).build());
payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.PENDING.getCode()).build());
}
} else {
log.error("查询支付结果失败:{}", respStateInfo.getRespDesc());
@ -492,24 +484,10 @@ public class EasypayServiceImpl implements IEasypayService {
if (order != null) {
// 支付完成 进入终态
if (StrUtil.equalsAny(respStateInfo.getTransState(), RSP_BODY_TRANS_OK, RSP_BODY_TRANS_OK_WETCAT, RSP_BODY_TRANS_PARTIALLY_OK)) {
// 更新支付订单信息
payOrderMapper.updateById(PayOrder.builder()
.id(payOrder.getId())
.endTransDate(DateUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()).toString()))
.easypayTrace(respOrderInfo.getOutTrace())
.pcTrace(respOrderInfo.getPcTrace())
.unTrace(respOrderInfo.getUnTrace())
.transState(TransState.PAID.getCode())
.build());
// 更新订单信息
orderService.updateById(Order.builder()
.id(order.getId())
.status(OrderStatusEnum.PAID.getValue())
.paymentTime(LocalDateTimeUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()).toString(), "yyyyMMddHHmmss"))
.payType(PayType.getByValue(payOrder.getPayType()).getChannel())
.build());
} else {
// 支付失败支付状态重置为待支付
updateOrderPaid(payOrder, respOrderInfo, order);
} else if (!StrUtil.equals(respStateInfo.getTransState(), RSP_BODY_TRANS_PROCESSING)) {
// 不是成功且不是支付中统一认为支付失败支付状态重置为待支付
orderService.updateById(Order.builder().id(order.getId()).status(OrderStatusEnum.UNPAID.getValue()).build());
payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.PENDING.getCode()).build());
}
} else {
@ -524,6 +502,27 @@ public class EasypayServiceImpl implements IEasypayService {
}
}
private void updateOrderPaid(PayOrder payOrder, TradeQueryRespOrderInfo respOrderInfo, Order order) {
// 更新支付订单信息
payOrderMapper.updateById(PayOrder.builder()
.id(payOrder.getId())
.endTransDate(DateUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()).toString()))
.easypayTrace(respOrderInfo.getOutTrace())
.pcTrace(respOrderInfo.getPcTrace())
.unTrace(respOrderInfo.getUnTrace())
.transState(TransState.PAID.getCode())
.build());
// 生成订单核销码信息
verificationCodeService.generateVerificationCode(order);
// 更新订单信息
orderService.updateById(Order.builder()
.id(order.getId())
.status(OrderStatusEnum.VERIFY.getValue())
.paymentTime(LocalDateTimeUtil.parse(StrBuilder.create(respOrderInfo.getDateEnd()).append(respOrderInfo.getTimeEnd()).toString(), "yyyyMMddHHmmss"))
.payType(PayType.getByValue(payOrder.getPayType()).getChannel())
.build());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void refund(RefundBO refundBO) {
@ -534,7 +533,7 @@ public class EasypayServiceImpl implements IEasypayService {
Assert.notNull(payOrder, () -> new ServiceException("订单不存在"));
Assert.isTrue(payOrder.getTransState() == TransState.PAID.getCode(), () -> new ServiceException("订单未支付,不可发起退款"));
// 订单已支付或已分账状态才可发起退款
boolean orderDivided = Objects.equals(order.getStatus(), OrderStatusEnum.PAID.getValue()) || Objects.equals(order.getStatus(), OrderStatusEnum.DIVIDED.getValue());
boolean orderDivided = Objects.equals(order.getStatus(), OrderStatusEnum.VERIFY.getValue()) || Objects.equals(order.getStatus(), OrderStatusEnum.DIVIDED.getValue());
// 更新商品订单和支付订单状态为退款中
orderService.updateById(Order.builder().id(orderId).status(OrderStatusEnum.REFUNDED.getValue()).build());
payOrderMapper.updateById(PayOrder.builder().id(payOrder.getId()).transState(TransState.REFUND_PENDING.getCode()).build());
@ -1044,7 +1043,6 @@ public class EasypayServiceImpl implements IEasypayService {
}
@Override
public void addSubAppidConfig(String subMchtCode) {
EasyPayRequestHeader reqHeader = generateEasyPayRequestHeader();
@ -1084,8 +1082,6 @@ public class EasypayServiceImpl implements IEasypayService {
}
@Override
public EasypayAccountVO getEasypayAccount(Long memberId) {
return EasypayAccountVO.builder().balance(new BigDecimal(1000)).build();