修改订单和商品

This commit is contained in:
fengxiaohang 2025-06-14 16:58:01 +08:00
parent eacad6e370
commit 05d3c17acd
31 changed files with 765 additions and 85 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

View File

@ -159,6 +159,8 @@ tenant:
- pms_product_snapshot
- pms_sku
- pms_sku_snapshot
- oms_verification_codes
- oms_verification_logs
# MyBatisPlus配置

View File

@ -97,7 +97,6 @@
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-sse</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-member</artifactId>
@ -137,6 +136,28 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-system</artifactId>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.22</version>
</dependency>
</dependencies>

View File

@ -57,11 +57,17 @@ public class OrderController extends BaseController {
// @ApiOperation("查询订单列表")
// @PostMapping("/list")
// public R<Page<OrderVO>> list(@RequestBody OrderBo query, Page<Order> page) {
// Page<Order> list = service.page(page,query.toWrapper() );
// return R.ok(convert.toVO(list));
// }
@ApiOperation("查询订单列表")
@PostMapping("/list")
public R<Page<OrderVO>> list(@RequestBody OrderBo query, Page<Order> page) {
Page<Order> list = service.page(page,query.toWrapper() );
return R.ok(convert.toVO(list));
public R<IPage<OrderVO>> list1(@RequestBody OrderBo query, Page<Order> page){
return R.ok(orderService.getlist(page,query));
}
// @ApiOperation("查询订单表列表")
@ -81,7 +87,7 @@ public class OrderController extends BaseController {
@ApiOperation("导出订单表列表")
@Log(title = "订单售后", businessType = BusinessType.EXPORT)
@GetMapping("export")
@PostMapping("export")
public ResponseEntity<String> export(OrderBo query) {
List<Order> list = service.list(query.toWrapper());
ExcelUtil<OrderVO> util = new ExcelUtil<>(OrderVO.class);
@ -137,7 +143,7 @@ public class OrderController extends BaseController {
throw new RuntimeException("发货失败");
}finally {
try{
redisService.unLock(redisKey, redisValue);;
redisService.unLock(redisKey, redisValue);
}catch (Exception e){
log.error("", e);
}

View File

@ -0,0 +1,44 @@
package com.wzj.soopin.order.controller;
import com.wzj.soopin.order.service.VerificationCodeService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.dromara.common.core.domain.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* 核销码相关接口
*/
@Api(tags = "核销码接口")
@RestController
@RequestMapping("/oms/verification")
public class VerificationCodeController {
@Autowired
private VerificationCodeService verificationCodeService;
/**
* 生成核销码
*/
@GetMapping("/generate/{orderId}")
@ApiOperation("生成核销码")
public R generateCode(@PathVariable Long orderId) {
return verificationCodeService.generateVerificationCode(orderId);
}
/**
* 扫码核销接口
* @param codeValue 核销码
* @param usedMerchantId 使用商家ID
* @return 核销结果z
*/
@PostMapping("/verify")
@ApiOperation("扫码核销接口")
public R verifyCode(@RequestParam String codeValue,
@RequestParam Long usedMerchantId) {
return verificationCodeService.verifyCode(codeValue, usedMerchantId);
}
}

View File

@ -25,6 +25,9 @@ public class OrderBo {
@ApiModelProperty("订单编号")
private String orderSn;
@ApiModelProperty("租户id")
private String tenantId;
@ApiModelProperty("用户帐号 精确匹配")
private String memberUsernameLike;
@ -125,20 +128,8 @@ public class OrderBo {
if (orderSn != null) {
queryWrapper.eq(Order::getOrderSn, orderSn);
}
if (totalAmount != null) {
queryWrapper.eq(Order::getTotalAmount, totalAmount);
}
if (purchasePrice != null) {
queryWrapper.eq(Order::getPurchasePrice, purchasePrice);
}
if (payAmount != null) {
queryWrapper.eq(Order::getPayAmount, payAmount);
}
if (merchantNote != null) {
queryWrapper.eq(Order::getMerchantNote, merchantNote);
}
if (freightAmount != null) {
queryWrapper.eq(Order::getFreightAmount, freightAmount);
if (tenantId != null) {
queryWrapper.eq(Order::getTenantId, tenantId);
}
if (payType != null) {
queryWrapper.eq(Order::getPayType, payType);
@ -149,61 +140,12 @@ public class OrderBo {
if (aftersaleStatus != null) {
queryWrapper.eq(Order::getAftersaleStatus, aftersaleStatus);
}
if (deliveryCompany != null &&!deliveryCompany.isEmpty()) {
queryWrapper.eq(Order::getDeliveryCompany, deliveryCompany);
}
if (deliverySn != null &&!deliverySn.isEmpty()) {
queryWrapper.eq(Order::getDeliverySn, deliverySn);
}
if (autoConfirmDay != null) {
queryWrapper.eq(Order::getAutoConfirmDay, autoConfirmDay);
}
if (receiverPhone != null &&!receiverPhone.isEmpty()) {
queryWrapper.eq(Order::getReceiverPhone, receiverPhone);
}
if (receiverPostCode != null &&!receiverPostCode.isEmpty()) {
queryWrapper.eq(Order::getReceiverPostCode, receiverPostCode);
}
if (receiverProvince != null &&!receiverProvince.isEmpty()) {
queryWrapper.eq(Order::getReceiverProvince, receiverProvince);
}
if (receiverCity != null &&!receiverCity.isEmpty()) {
queryWrapper.eq(Order::getReceiverCity, receiverCity);
}
if (receiverDistrict != null &&!receiverDistrict.isEmpty()) {
queryWrapper.eq(Order::getReceiverDistrict, receiverDistrict);
}
if (receiverProvinceId != null) {
queryWrapper.eq(Order::getReceiverProvinceId, receiverProvinceId);
}
if (receiverCityId != null) {
queryWrapper.eq(Order::getReceiverCityId, receiverCityId);
}
if (receiverDistrictId != null) {
queryWrapper.eq(Order::getReceiverDistrictId, receiverDistrictId);
}
if (receiverDetailAddress != null &&!receiverDetailAddress.isEmpty()) {
queryWrapper.eq(Order::getReceiverDetailAddress, receiverDetailAddress);
}
if (note != null &&!note.isEmpty()) {
queryWrapper.eq(Order::getNote, note);
}
if (confirmStatus != null) {
queryWrapper.eq(Order::getConfirmStatus, confirmStatus);
}
if (deleteStatus != null) {
queryWrapper.eq(Order::getDeleteStatus, deleteStatus);
}
if (paymentTime != null) {
queryWrapper.eq(Order::getPaymentTime, paymentTime);
}
if (deliveryTime != null) {
queryWrapper.eq(Order::getDeliveryTime, deliveryTime);
}
if (receiveTime != null) {
queryWrapper.eq(Order::getReceiveTime, receiveTime);
}
// 时间范围查询
if (startTime != null && endTime != null) {

View File

@ -34,6 +34,9 @@ public class Order extends BaseAudit {
@ApiModelProperty("支付id")
private Long payId;
@ApiModelProperty("租户id")
private String tenantId;
@ApiModelProperty("订单编号")
@Excel(name = "订单编号")
private String orderSn;
@ -164,5 +167,11 @@ public class Order extends BaseAudit {
@ApiModelProperty("优惠券金额")
private BigDecimal couponAmount;
@ApiModelProperty("核销时间")
private LocalDateTime usedTime;
@ApiModelProperty("核销码url")
private String codeUrl;
}

View File

@ -24,8 +24,7 @@ public class OrderItem extends BaseAudit {
@ApiModelProperty("ID")
@TableId(type = IdType.ASSIGN_ID)
private Long id;
@Excel(name = "商家备注")
private String merchantNote;
@ApiModelProperty("订单id")
@Excel(name = "订单id")
private Long orderId;

View File

@ -0,0 +1,48 @@
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.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.dromara.common.core.domain.model.BaseAudit;
import java.time.LocalDateTime;
@ApiModel(description="核销码表对象")
@Data
@TableName("oms_verification_codes")
public class VerificationCodes extends BaseAudit {
@ApiModelProperty("ID")
@TableId(type = IdType.ASSIGN_ID)
private Long id;
@ApiModelProperty("核销码")
private String code;
@ApiModelProperty("关联订单id")
private Long orderId;
@ApiModelProperty("是否使用")
private Integer status;
@ApiModelProperty("用户id")
private Long memberId;
@ApiModelProperty("用户昵称")
private String memberName ;
@ApiModelProperty("使用时间")
private LocalDateTime usedTime;
@ApiModelProperty("使用商家id")
private Long usedMerchantId;
@ApiModelProperty("过期时间")
private LocalDateTime expireTime;
}

View File

@ -0,0 +1,40 @@
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.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.dromara.common.core.domain.model.BaseAudit;
import java.time.LocalDateTime;
@ApiModel(description="核销日志表对象")
@Data
@TableName("oms_verification_logs")
public class VerificationLogs extends BaseAudit {
@ApiModelProperty("ID")
@TableId(type = IdType.ASSIGN_ID)
private Long id;
@ApiModelProperty("核销码id")
private Long codeId;
@ApiModelProperty("订单id")
private Long orderId;
@ApiModelProperty("商家ID")
private Long merchantId;
@ApiModelProperty("核销时间")
private LocalDateTime verificationTime;
@ApiModelProperty("核销结果")
private Integer result;
@ApiModelProperty("失败原因")
private String reason;
}

View File

@ -28,6 +28,8 @@ public class OrderVO extends BaseAudit {
/** MEMBER_ID */
@Excel(name = "MEMBER_ID")
private Long memberId;
/** 租户id */
private String tenantId;
/** 用户帐号 */
@Excel(name = "用户帐号")
private String memberUsername;
@ -118,4 +120,26 @@ public class OrderVO extends BaseAudit {
@Excel(name = "确认收货时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime receiveTime;
private List<OrderItem> items;
@Excel(name = "用户昵称")
private String memberNickname;
@Excel(name = "用户手机号")
private String memberPhoneEncrypted;
@Excel(name = "卖家昵称")
private String tenantContactName;
@Excel(name = "卖家手机号")
private String tenantContactPhone;
@Excel(name = "item ID")
private Long itemId;
@Excel(name = "商品id")
private Long productId;
@Excel(name = "商品品牌")
private Long productBrandId;
@Excel(name = "商品名称")
private String productName;
@Excel(name = "商品价格")
private BigDecimal productPrice;
}

View File

@ -16,7 +16,7 @@ import java.util.List;
*/
public interface OrderItemMapper extends BaseMapper<OrderItem> {
IPage<OrderItem> selectOrderItem(@Param("query") OrderItemQuery query, IPage<OrderItem> pageParam);
// IPage<OrderItem> selectOrderItem(@Param("query") OrderItemQuery query, IPage<OrderItem> pageParam);
/**
* 查询订单中所包含的商品列表
*

View File

@ -3,6 +3,9 @@ package com.wzj.soopin.order.mapper;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wzj.soopin.order.domain.bo.OrderBo;
import com.wzj.soopin.order.domain.entity.Order;
import com.wzj.soopin.order.domain.entity.SystemStatistics;
import com.wzj.soopin.order.domain.form.ManagerOrderQueryForm;
@ -47,4 +50,10 @@ public interface OrderMapper extends BaseMapper<Order> {
SystemStatistics statNewAndDeal(@Param("startTime") LocalDateTime startTime, @Param("endTime") LocalDateTime endTime);
int statDealMember(@Param("startTime") LocalDateTime startTime,@Param("endTime") LocalDateTime endTime);
IPage<OrderVO> getlist(@Param("page") Page<Order> page,@Param("query") OrderBo query);
// IPage<OrderVO> getlist(@Param("page") Page<Object> objectPage, @Param("query") OrderBo query);
}

View File

@ -0,0 +1,12 @@
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.Select;
import java.time.LocalDateTime;
import java.util.List;
public interface VerificationCodesMapper extends BaseMapper<VerificationCodes> {
}

View File

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

View File

@ -1,7 +1,18 @@
package com.wzj.soopin.order.service;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.wzj.soopin.order.domain.bo.OrderBo;
import com.wzj.soopin.order.domain.entity.Order;
import com.wzj.soopin.order.domain.vo.OrderVO;
import com.wzj.soopin.order.domain.vo.PageVO;
public interface OrderService extends IService<Order> {
IPage<OrderVO> getlist(Page<Order> page, OrderBo query);
// IPage<OrderVO> getlist(PageVO pageVO, OrderBo query);
}

View File

@ -0,0 +1,13 @@
package com.wzj.soopin.order.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.wzj.soopin.order.domain.entity.VerificationCodes;
import org.dromara.common.core.domain.R;
public interface VerificationCodeService extends IService<VerificationCodes> {
R generateVerificationCode(Long orderId);
R verifyCode(String codeValue, Long usedMerchantId);
}

View File

@ -0,0 +1,7 @@
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

@ -174,8 +174,8 @@ public class OrderItemServiceImpl extends ServiceImpl<OrderItemMapper, OrderItem
}
}
public IPage<OrderItem> pagelist(OrderItemQuery query, PageVO page) {
IPage<OrderItem> pageParam = PageUtil.initPage(page);
return orderItemMapper.selectOrderItem(query, pageParam);
}
// public IPage<OrderItem> pagelist(OrderItemQuery query, PageVO page) {
// IPage<OrderItem> pageParam = PageUtil.initPage(page);
// return orderItemMapper.selectOrderItem(query, pageParam);
// }
}

View File

@ -1,7 +1,9 @@
package com.wzj.soopin.order.service.impl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
@ -13,6 +15,7 @@ import com.wzj.soopin.goods.mapper.SkuMapper;
import com.wzj.soopin.member.domain.po.Member;
import com.wzj.soopin.order.convert.OrderConvert;
import com.wzj.soopin.order.convert.OrderOperateHistoryConvert;
import com.wzj.soopin.order.domain.bo.OrderBo;
import com.wzj.soopin.order.domain.entity.Order;
import com.wzj.soopin.order.domain.entity.OrderDeliveryHistory;
import com.wzj.soopin.order.domain.entity.OrderItem;
@ -26,12 +29,14 @@ import com.wzj.soopin.order.mapper.OrderItemMapper;
import com.wzj.soopin.order.mapper.OrderMapper;
import com.wzj.soopin.order.mapper.OrderOperateHistoryMapper;
import com.wzj.soopin.order.service.OrderService;
import com.wzj.soopin.order.utils.PageUtil;
import com.wzj.soopin.order.utils.SecurityUtils;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.domain.event.Constants;
import org.dromara.common.core.utils.PhoneUtils;
import org.dromara.system.domain.SysOperLog;
import org.dromara.system.mapper.SysOperLogMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
@ -80,6 +85,9 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
@Autowired
private OrderOperateHistoryConvert historyConvert;
@Autowired
private SysOperLogMapper sysOperLogMapper;
/**
* 查询订单表
*
@ -234,7 +242,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
return affectedRows > 0 ? R.ok(order) : R.fail("删除失败");
}
public Page<OrderVO> queryOrderPage(OrderH5Query query, Pageable pageReq) {
public PageImpl<Object> queryOrderPage(OrderH5Query query, Pageable pageReq) {
QueryWrapper<Order> qw = new QueryWrapper<>();
qw.eq("member_id", SecurityUtils.getUserId());
IPage<Order> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>();
@ -388,13 +396,18 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
* @param orderId 订单id
* @return 结果
*/
public List<OrderOperateHistory> log(Long orderId) {
LambdaQueryWrapper<OrderOperateHistory> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(OrderOperateHistory::getOrderId, orderId);
wrapper.in(OrderOperateHistory::getOrderStatus, Arrays.asList(0, 1, 2, 3, 4));
wrapper.orderByDesc(OrderOperateHistory::getCreateTime);
List<OrderOperateHistory> historyList = orderOperateHistoryMapper.selectList(wrapper);
return historyList;
public List<SysOperLog> log(Long orderId) {
// LambdaQueryWrapper<OrderOperateHistory> wrapper = new LambdaQueryWrapper<>();
// wrapper.eq(OrderOperateHistory::getOrderId, orderId);
// wrapper.in(OrderOperateHistory::getOrderStatus, Arrays.asList(0, 1, 2, 3, 4));
// wrapper.orderByDesc(OrderOperateHistory::getCreateTime);
// List<OrderOperateHistory> historyList = orderOperateHistoryMapper.selectList(wrapper);
// return historyList;
LambdaQueryWrapper<SysOperLog> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysOperLog::getTitle, "订单表");
wrapper.eq(SysOperLog::getOperatorType, 1);
return sysOperLogMapper.selectList(wrapper);
}
public R decryptPhone(Long orderId) {
@ -430,4 +443,15 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
}
}
@Override
public IPage<OrderVO> getlist(Page<Order> page, OrderBo query) {
IPage<OrderVO> resultPage = orderMapper.getlist(page,query);
return resultPage;
}
// @Override
// public IPage<OrderVO> getlist(PageVO pageVO, OrderBo query) {
// IPage<OrderVO> resultPage = orderMapper.getlist(PageUtil.initPage(pageVO),query);
// return resultPage;
// }
}

View File

@ -0,0 +1,208 @@
package com.wzj.soopin.order.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wzj.soopin.order.domain.entity.Order;
import com.wzj.soopin.order.domain.entity.VerificationCodes;
import com.wzj.soopin.order.domain.entity.VerificationLogs;
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.VerificationCodeService;
import com.wzj.soopin.order.utils.QrCodeGenerator;
import com.wzj.soopin.order.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.dromara.common.core.domain.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@Service
@Slf4j
public class VerificationCodeServiceImpl extends ServiceImpl<VerificationCodesMapper, VerificationCodes> implements VerificationCodeService{
@Autowired
private VerificationCodesMapper codeMapper;
@Autowired
private OrderMapper orderMapper;
@Autowired
private QrCodeGenerator qrCodeGenerator;
@Autowired
private VerificationLogsMapper logMapper;
/**
* 生成核销码并关联订单
*
* @return
*/
@Override
public R generateVerificationCode(Long orderId) {
// 校验订单状态
Order order = orderMapper.selectById(orderId);
if (order == null || order.getStatus() != 6) {
return R.fail("订单状态异常");
}
// 生成唯一核销码
String codeValue = generateUniqueCode();
// 核销码过期时间7天后过期
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);
}
@Override
public R verifyCode(String codeValue, Long usedMerchantId) {
// 参数校验
if (StringUtils.isBlank(codeValue) || usedMerchantId == null) {
recordVerificationLog(null, null, usedMerchantId, 0, "参数不能为空");
return R.fail("参数不能为空");
}
// 查询核销码
VerificationCodes code = codeMapper.selectOne(
new LambdaQueryWrapper<VerificationCodes>().eq(VerificationCodes::getCode, codeValue)
);
if (code == null) {
recordVerificationLog(null, null, usedMerchantId, 0, "核销码不存在");
return R.fail("核销码不存在");
}
// 检查核销码状态
if (code.getStatus() == 1) {
recordVerificationLog(code.getId(), code.getOrderId(), usedMerchantId, 0, "核销码已使用");
return R.fail("核销码已使用");
}
if (code.getStatus() == 2) {
recordVerificationLog(code.getId(), code.getOrderId(), usedMerchantId, 0, "核销码已过期");
return R.fail("核销码已过期");
}
// 检查订单状态
Order order = orderMapper.selectById(code.getOrderId());
if (order == null || order.getStatus() != 6) {
recordVerificationLog(code.getId(), code.getOrderId(), usedMerchantId, 0, "订单状态异常,无法核销");
return R.fail("订单状态异常,无法核销");
}
// 检查核销码是否过期
if (LocalDateTime.now().isAfter(code.getExpireTime())) {
// 更新状态为已过期
code.setStatus(3);
codeMapper.updateById(code);
recordVerificationLog(code.getId(), code.getOrderId(), usedMerchantId, 0, "核销码已过期");
return R.fail("核销码已过期");
}
// 检查该订单是否已经核销过
VerificationLogs existingLog = logMapper.selectOne(
new LambdaQueryWrapper<VerificationLogs>()
.eq(VerificationLogs::getOrderId, code.getOrderId())
.eq(VerificationLogs::getResult, 1) // 只检查成功核销的记录
);
if (existingLog != null) {
recordVerificationLog(code.getId(), code.getOrderId(), usedMerchantId, 0,
"该订单已被核销,核销时间:" + existingLog.getVerificationTime());
return R.fail("该订单已被核销,核销时间:" + existingLog.getVerificationTime());
}
// 执行核销
code.setStatus(1); // 已使用
code.setUsedTime(LocalDateTime.now());
code.setUsedMerchantId(usedMerchantId);
int result = codeMapper.updateById(code);
if (result != 1) {
recordVerificationLog(code.getId(), code.getOrderId(), usedMerchantId, 0, "核销失败,请重试");
return R.fail("核销失败,请重试");
}
// 更新订单状态
order.setStatus(7); // 已核销
order.setUsedTime(LocalDateTime.now());
orderMapper.updateById(order);
// 记录核销日志成功
recordVerificationLog(code.getId(), code.getOrderId(), usedMerchantId, 1, null);
// 返回核销成功信息
Map<String, Object> resultData = new HashMap<>();
resultData.put("orderId", order.getId());
resultData.put("orderSn", order.getOrderSn());
resultData.put("used_time", order.getUsedTime());
resultData.put("code_url", order.getCodeUrl());
return R.ok("核销成功", resultData);
}
/**
* 记录核销日志
* @param codeId 核销码ID
* @param orderId 订单ID
* @param merchantId 商户ID
* @param result 结果1成功0失败
* @param reason 失败原因
*/
private void recordVerificationLog(Long codeId, Long orderId, Long merchantId, Integer result, String reason) {
VerificationLogs log = new VerificationLogs();
log.setCodeId(codeId);
log.setOrderId(orderId);
log.setMerchantId(merchantId);
log.setVerificationTime(LocalDateTime.now());
log.setResult(result);
log.setReason(reason);
logMapper.insert(log);
}
/**
* 生成唯一核销码
*/
private String generateUniqueCode() {
String uuid = java.util.UUID.randomUUID().toString().replaceAll("-", "");
return uuid.substring(0, 16).toUpperCase();
}
}

View File

@ -0,0 +1,11 @@
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;
@Service
public class VerificationLogsServiceImpl extends ServiceImpl<VerificationLogsMapper, VerificationLogs> implements VerificationLogsService {
}

View File

@ -0,0 +1,105 @@
package com.wzj.soopin.order.utils;
import com.google.zxing.*;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.QRCodeReader;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.annotations.Comment;
import org.springframework.stereotype.Component;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.io.File;
@Component
@Slf4j
public class QrCodeGenerator {
/**
* 生成二维码图片并保存到指定路径
* @param content 二维码内容
* @param width 图片宽度
* @param height 图片高度
* @param filePath 保存路径
*/
public String generateQrCode(String content, int width, int height, String filePath) {
try {
// 检查并创建目录
File dir = new File(filePath).getParentFile();
if (!dir.exists()) {
dir.mkdirs();
}
QRCodeWriter qrCodeWriter = new QRCodeWriter();
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
Path path = Paths.get(filePath);
MatrixToImageWriter.writeToPath(bitMatrix, "png", path);
return filePath;
} catch (WriterException | IOException e) {
// 记录异常信息
log.error("生成二维码时出现异常: {}", e.getMessage());
e.printStackTrace();
return null;
}
}
/**
* 生成二维码图片字节数组
* @param content 二维码内容
* @param width 图片宽度
* @param height 图片高度
* @return 二维码图片字节数组
*/
public byte[] generateQrCodeImage(String content, int width, int height) {
try {
QRCodeWriter qrCodeWriter = new QRCodeWriter();
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
javax.imageio.ImageIO.write(bufferedImage, "png", bos);
return bos.toByteArray();
} catch (WriterException | IOException e) {
e.printStackTrace();
return null;
}
}
/**
* 解析二维码图片
* @param filePath 二维码图片路径
* @return 二维码内容
*/
public String decodeQrCode(String filePath) {
try {
BufferedImage bufferedImage = ImageIO.read(new File(filePath));
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
int[] pixels = new int[width * height];
bufferedImage.getRGB(0, 0, width, height, pixels, 0, width);
LuminanceSource source = new RGBLuminanceSource(width, height, pixels);
HybridBinarizer hybridBinarizer = new HybridBinarizer(source);
BinaryBitmap binaryBitmap = new BinaryBitmap(hybridBinarizer);
MultiFormatReader reader = new MultiFormatReader();
Result result = reader.decode(binaryBitmap);
return result.getText();
} catch (IOException | com.google.zxing.NotFoundException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,95 @@
package com.wzj.soopin.order.utils;
public class SnowflakeIdGenerator {
// 起始时间戳可以设置项目上线时间等固定时间单位毫秒
private static final long START_TIMESTAMP = 1609459200000L; // 2021-01-01 00:00:00
// 机器ID所占位数
private static final long WORKER_ID_BITS = 5L;
// 数据中心ID所占位数
private static final long DATA_CENTER_ID_BITS = 5L;
// 序列号所占位数
private static final long SEQUENCE_BITS = 12L;
// 最大的机器ID2的WORKER_ID_BITS次方减1
private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
// 最大的数据中心ID2的DATA_CENTER_ID_BITS次方减1
private static final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);
// 序列号最大值2的SEQUENCE_BITS次方减1
private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
// 时间戳左移WORKER_ID_BITS + DATA_CENTER_ID_BITS + SEQUENCE_BITS
private static final long TIMESTAMP_LEFT_SHIFT = WORKER_ID_BITS + DATA_CENTER_ID_BITS + SEQUENCE_BITS;
// 机器ID左移DATA_CENTER_ID_BITS + SEQUENCE_BITS
private static final long WORKER_ID_SHIFT = DATA_CENTER_ID_BITS + SEQUENCE_BITS;
// 数据中心ID左移SEQUENCE_BITS位
private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS;
// 当前机器ID
private long workerId;
// 当前数据中心ID
private long dataCenterId;
// 序列号
private long sequence = 0L;
// 上次生成ID的时间戳
private long lastTimestamp = -1L;
/**
* 构造函数
* @param workerId 机器ID取值范围0 - MAX_WORKER_ID
* @param dataCenterId 数据中心ID取值范围0 - MAX_DATA_CENTER_ID
*/
public SnowflakeIdGenerator(long workerId, long dataCenterId) {
if (workerId > MAX_WORKER_ID || workerId < 0) {
throw new IllegalArgumentException(String.format("Worker ID can't be greater than %d or less than 0", MAX_WORKER_ID));
}
if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) {
throw new IllegalArgumentException(String.format("Data center ID can't be greater than %d or less than 0", MAX_DATA_CENTER_ID));
}
this.workerId = workerId;
this.dataCenterId = dataCenterId;
}
/**
* 生成唯一ID
* @return 生成的唯一ID
*/
public synchronized long nextId() {
long timestamp = timeGen();
// 若当前时间小于上一次生成ID的时间戳说明系统时钟回退过抛出异常
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
// 同一毫秒内生成则序列号递增
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & SEQUENCE_MASK;
// 序列号溢出等待到下一毫秒
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
// 不同毫秒生成序列号重置为0
sequence = 0L;
}
// 记录上一次生成ID的时间戳
lastTimestamp = timestamp;
// 生成唯一ID
return ((timestamp - START_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT) |
(workerId << WORKER_ID_SHIFT) |
(dataCenterId << DATA_CENTER_ID_SHIFT) |
sequence;
}
/**
* 获取当前时间戳单位毫秒
* @return 当前时间戳
*/
private long timeGen() {
return System.currentTimeMillis();
}
/**
* 等待直到下一个毫秒
* @param lastTimestamp 上一次生成ID的时间戳
* @return 下一个毫秒的时间戳
*/
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
}

View File

@ -264,4 +264,47 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where status in (1,2,3) and create_time between #{startTime} and #{endTime}
</select>
<select id="getlist" resultType="com.wzj.soopin.order.domain.vo.OrderVO">
SELECT
o.*,
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 o
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
WHERE 1=1
<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.status != null and query.status != ''">
AND o.status LIKE CONCAT('%', #{query.status}, '%')
</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>
</select>
</mapper>