商品加入购买状态 商品详情加入水印

This commit is contained in:
litianxiang
2026-06-01 15:53:02 +08:00
parent 3ddf4051e3
commit afdb90e196
14 changed files with 190 additions and 34 deletions

View File

@@ -17,7 +17,9 @@ public class WatermarkProperties {
private int rotationDegrees = -30; private int rotationDegrees = -30;
private float spacingRatio = 1.5f; private float spacingRatioX = 1.67f;
private float spacingRatioY = 0.9f;
private int ttlDays = 30; private int ttlDays = 30;

View File

@@ -52,4 +52,15 @@ public class UserContext {
} }
return holder.getId(); return holder.getId();
} }
public static Long getBuyerIdSafely() {
AuthPrincipalVo holder = userHolder.get();
if (holder == null) {
return null;
}
if (!"BUYER".equals(holder.getSource())) {
return null;
}
return holder.getId();
}
} }

View File

@@ -13,6 +13,8 @@ import com.aida.seller.module.listing.enums.ProductCategoryEnum;
import com.aida.seller.module.listing.mapper.ListingImageMapper; import com.aida.seller.module.listing.mapper.ListingImageMapper;
import com.aida.seller.module.listing.mapper.ListingMapper; import com.aida.seller.module.listing.mapper.ListingMapper;
import com.aida.seller.module.listing.vo.ListingPageVO; import com.aida.seller.module.listing.vo.ListingPageVO;
import com.aida.seller.module.order.mapper.OrderItemMapper;
import com.aida.seller.module.order.entity.OrderItemEntity;
import com.aida.seller.util.MinioUtil; import com.aida.seller.util.MinioUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -27,6 +29,7 @@ import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@@ -41,6 +44,7 @@ import java.util.stream.Collectors;
public class ListingServiceImpl extends ServiceImpl<ListingMapper, ListingEntity> implements ListingService { public class ListingServiceImpl extends ServiceImpl<ListingMapper, ListingEntity> implements ListingService {
private final ListingImageMapper listingImageMapper; private final ListingImageMapper listingImageMapper;
private final OrderItemMapper orderItemMapper;
private final RedisTemplate<String, Object> redisTemplate; private final RedisTemplate<String, Object> redisTemplate;
private final MinioUtil minioUtil; private final MinioUtil minioUtil;
@@ -323,6 +327,51 @@ public class ListingServiceImpl extends ServiceImpl<ListingMapper, ListingEntity
vo.setCover(minioUtil.processMinioResource(vo.getCover(), CommonConstants.MINIO_PATH_TIMEOUT)); vo.setCover(minioUtil.processMinioResource(vo.getCover(), CommonConstants.MINIO_PATH_TIMEOUT));
return vo; return vo;
}).collect(Collectors.toList())); }).collect(Collectors.toList()));
Long buyerId = UserContext.getBuyerIdSafely();
if (buyerId == null) {
result.getRecords().forEach(vo -> vo.setProductStatus(2));
return result;
}
Map<Long, Integer> orderStatusMap = getListingOrderStatusMap(
result.getRecords().stream().map(ListingPageVO::getId).collect(Collectors.toList()),
buyerId
);
for (ListingPageVO vo : result.getRecords()) {
Integer orderStatus = orderStatusMap.get(vo.getId());
if (orderStatus == null) {
vo.setProductStatus(2);
} else if (orderStatus == 1) {
vo.setProductStatus(1);
} else if (orderStatus == 0) {
vo.setProductStatus(0);
} else {
vo.setProductStatus(2);
}
}
return result;
}
private Map<Long, Integer> getListingOrderStatusMap(List<Long> listingIds, Long buyerId) {
if (CollectionUtils.isEmpty(listingIds) || buyerId == null) {
return Collections.emptyMap();
}
LambdaQueryWrapper<OrderItemEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(OrderItemEntity::getListingId, listingIds);
queryWrapper.eq(OrderItemEntity::getBuyerId, buyerId);
queryWrapper.eq(OrderItemEntity::getDeleted, 0);
List<OrderItemEntity> items = orderItemMapper.selectList(queryWrapper);
if (CollectionUtils.isEmpty(items)) {
return Collections.emptyMap();
}
Map<Long, Integer> result = new HashMap<>();
for (OrderItemEntity item : items) {
Integer current = result.get(item.getListingId());
if (current == null || (item.getStatus() == 1 && current != 1)) {
result.put(item.getListingId(), item.getStatus());
}
}
return result; return result;
} }
} }

View File

@@ -1,6 +1,7 @@
package com.aida.seller.module.listing.service.impl; package com.aida.seller.module.listing.service.impl;
import com.aida.seller.common.constants.CommonConstants; import com.aida.seller.common.constants.CommonConstants;
import com.aida.seller.common.context.UserContext;
import com.aida.seller.common.exception.BusinessException; import com.aida.seller.common.exception.BusinessException;
import com.aida.seller.module.designer.entity.DesignerEntity; import com.aida.seller.module.designer.entity.DesignerEntity;
import com.aida.seller.module.designer.mapper.DesignerMapper; import com.aida.seller.module.designer.mapper.DesignerMapper;
@@ -13,8 +14,10 @@ import com.aida.seller.module.listing.mapper.ListingImageMapper;
import com.aida.seller.module.listing.mapper.ListingMallMapper; import com.aida.seller.module.listing.mapper.ListingMallMapper;
import com.aida.seller.module.listing.vo.ListingDetailVO; import com.aida.seller.module.listing.vo.ListingDetailVO;
import com.aida.seller.module.listing.vo.ListingMallVO; import com.aida.seller.module.listing.vo.ListingMallVO;
import com.aida.seller.module.order.entity.OrderItemEntity;
import com.aida.seller.module.order.entity.OrderItemImageEntity; import com.aida.seller.module.order.entity.OrderItemImageEntity;
import com.aida.seller.module.order.mapper.OrderItemImageMapper; import com.aida.seller.module.order.mapper.OrderItemImageMapper;
import com.aida.seller.module.order.mapper.OrderItemMapper;
import com.aida.seller.util.ImageWatermarkUtil; import com.aida.seller.util.ImageWatermarkUtil;
import com.aida.seller.util.MinioUtil; import com.aida.seller.util.MinioUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -26,8 +29,10 @@ import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Collections;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.aida.seller.module.listing.vo.ListingMallVO; import com.aida.seller.module.listing.vo.ListingMallVO;
@@ -43,14 +48,16 @@ public class ListingMallServiceImpl extends ServiceImpl<ListingMallMapper, Listi
private final ListingImageMapper listingImageMapper; private final ListingImageMapper listingImageMapper;
private final DesignerMapper designerMapper; private final DesignerMapper designerMapper;
private final OrderItemImageMapper orderItemImageMapper; private final OrderItemImageMapper orderItemImageMapper;
private final OrderItemMapper orderItemMapper;
public ListingMallServiceImpl(MinioUtil minioUtil, ImageWatermarkUtil imageWatermarkUtil, ListingImageMapper listingImageMapper, DesignerMapper designerMapper, public ListingMallServiceImpl(MinioUtil minioUtil, ImageWatermarkUtil imageWatermarkUtil, ListingImageMapper listingImageMapper, DesignerMapper designerMapper,
OrderItemImageMapper orderItemImageMapper) { OrderItemImageMapper orderItemImageMapper, OrderItemMapper orderItemMapper) {
this.minioUtil = minioUtil; this.minioUtil = minioUtil;
this.imageWatermarkUtil = imageWatermarkUtil; this.imageWatermarkUtil = imageWatermarkUtil;
this.listingImageMapper = listingImageMapper; this.listingImageMapper = listingImageMapper;
this.designerMapper = designerMapper; this.designerMapper = designerMapper;
this.orderItemImageMapper = orderItemImageMapper; this.orderItemImageMapper = orderItemImageMapper;
this.orderItemMapper = orderItemMapper;
} }
@Override @Override
@@ -114,7 +121,7 @@ public class ListingMallServiceImpl extends ServiceImpl<ListingMallMapper, Listi
)); ));
Page<ListingMallVO> result = new Page<>(page.getCurrent(), page.getSize(), page.getTotal()); Page<ListingMallVO> result = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
result.setRecords(records.stream().map(entity -> { List<ListingMallVO> voList = records.stream().map(entity -> {
DesignerEntity designer = designerMap.get(entity.getSellerId()); DesignerEntity designer = designerMap.get(entity.getSellerId());
ListingMallVO vo = new ListingMallVO(); ListingMallVO vo = new ListingMallVO();
vo.setId(entity.getId()); vo.setId(entity.getId());
@@ -123,10 +130,54 @@ public class ListingMallServiceImpl extends ServiceImpl<ListingMallMapper, Listi
vo.setPrice(entity.getPrice()); vo.setPrice(entity.getPrice());
vo.setShopName(designer != null ? designer.getShopName() : null); vo.setShopName(designer != null ? designer.getShopName() : null);
return vo; return vo;
}).toList()); }).toList();
Long buyerId = UserContext.getBuyerIdSafely();
if (buyerId != null) {
List<Long> listingIds = voList.stream().map(ListingMallVO::getId).collect(Collectors.toList());
Map<Long, Integer> orderStatusMap = getListingOrderStatusMap(listingIds, buyerId);
for (ListingMallVO vo : voList) {
Integer orderStatus = orderStatusMap.get(vo.getId());
if (orderStatus == null) {
vo.setProductStatus(2);
} else if (orderStatus == 1) {
vo.setProductStatus(1);
} else if (orderStatus == 0) {
vo.setProductStatus(0);
} else {
vo.setProductStatus(2);
}
}
} else {
voList.forEach(vo -> vo.setProductStatus(2));
}
result.setRecords(voList);
return result; return result;
} }
private Map<Long, Integer> getListingOrderStatusMap(List<Long> listingIds, Long buyerId) {
if (CollectionUtils.isEmpty(listingIds) || buyerId == null) {
return Collections.emptyMap();
}
LambdaQueryWrapper<OrderItemEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(OrderItemEntity::getListingId, listingIds);
queryWrapper.eq(OrderItemEntity::getBuyerId, buyerId);
queryWrapper.eq(OrderItemEntity::getDeleted, 0);
List<OrderItemEntity> items = orderItemMapper.selectList(queryWrapper);
if (CollectionUtils.isEmpty(items)) {
return Collections.emptyMap();
}
Map<Long, Integer> resultMap = new HashMap<>();
for (OrderItemEntity item : items) {
Integer current = resultMap.get(item.getListingId());
if (current == null || (item.getStatus() == 1 && current != 1)) {
resultMap.put(item.getListingId(), item.getStatus());
}
}
return resultMap;
}
private void applySorting(LambdaQueryWrapper<ListingEntity> queryWrapper, String sortField, String sortOrder) { private void applySorting(LambdaQueryWrapper<ListingEntity> queryWrapper, String sortField, String sortOrder) {
if (!StringUtils.hasText(sortField)) { if (!StringUtils.hasText(sortField)) {
queryWrapper.orderByDesc(ListingEntity::getUpdateTime); queryWrapper.orderByDesc(ListingEntity::getUpdateTime);
@@ -219,6 +270,28 @@ public class ListingMallServiceImpl extends ServiceImpl<ListingMallMapper, Listi
vo.setAvatar(minioUtil.processMinioResource(designer != null ? designer.getAvatar() : null, CommonConstants.MINIO_PATH_TIMEOUT)); vo.setAvatar(minioUtil.processMinioResource(designer != null ? designer.getAvatar() : null, CommonConstants.MINIO_PATH_TIMEOUT));
vo.setSellerId(entity.getSellerId()); vo.setSellerId(entity.getSellerId());
Long buyerId = UserContext.getBuyerIdSafely();
if (buyerId == null) {
vo.setProductStatus(2);
} else {
OrderItemEntity orderItem = orderItemMapper.selectOne(
new LambdaQueryWrapper<OrderItemEntity>()
.eq(OrderItemEntity::getListingId, id)
.eq(OrderItemEntity::getBuyerId, buyerId)
.eq(OrderItemEntity::getDeleted, 0)
.last("LIMIT 1"));
Integer orderStatus = orderItem != null ? orderItem.getStatus() : null;
if (orderStatus == null) {
vo.setProductStatus(2);
} else if (orderStatus == 1) {
vo.setProductStatus(1);
} else if (orderStatus == 0) {
vo.setProductStatus(0);
} else {
vo.setProductStatus(2);
}
}
List<String> apparelUrls = imageMap.get("apparel"); List<String> apparelUrls = imageMap.get("apparel");
if (!CollectionUtils.isEmpty(apparelUrls)) { if (!CollectionUtils.isEmpty(apparelUrls)) {
List<String> watermarkedUrls = apparelUrls.parallelStream() List<String> watermarkedUrls = apparelUrls.parallelStream()

View File

@@ -55,4 +55,7 @@ public class ListingDetailVO implements Serializable {
/** 卖家ID */ /** 卖家ID */
private Long sellerId; private Long sellerId;
/** 商品状态0-已下单未付款1-已购买2-未下单 */
private Integer productStatus;
} }

View File

@@ -39,4 +39,7 @@ public class ListingMallVO implements Serializable {
/** 销量 */ /** 销量 */
private Integer salesVolume; private Integer salesVolume;
/** 商品状态0-已下单未付款1-已购买2-未下单 */
private Integer productStatus;
} }

View File

@@ -37,4 +37,7 @@ public class ListingPageVO implements Serializable {
/** 浏览量 */ /** 浏览量 */
private Integer viewCount; private Integer viewCount;
/** 商品状态0-已下单未付款1-已购买2-未下单 */
private Integer productStatus;
} }

View File

@@ -59,4 +59,7 @@ public class OrderItemEntity implements Serializable {
/** 商品分类列表 */ /** 商品分类列表 */
@TableField(typeHandler = JacksonTypeHandler.class) @TableField(typeHandler = JacksonTypeHandler.class)
private List<String> productCategory; private List<String> productCategory;
/** 商品状态0-未支付1-已支付2-已取消 */
private Integer status;
} }

View File

@@ -106,7 +106,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
.like(OrderInfoEntity::getId, keyword) .like(OrderInfoEntity::getId, keyword)
.or() .or()
.inSql(OrderInfoEntity::getId, .inSql(OrderInfoEntity::getId,
"SELECT order_id FROM seller_order_item WHERE deleted = 0 AND product_name LIKE '%" + keyword + "%'") "SELECT order_id FROM seller_order_item WHERE deleted = 0 AND listing_name LIKE '%" + keyword + "%'")
); );
} }
@@ -289,6 +289,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
item.setThumbnailUrl(listing.getCover()); item.setThumbnailUrl(listing.getCover());
item.setPrice(listing.getPrice()); item.setPrice(listing.getPrice());
item.setProductCategory(listing.getProductCategory()); item.setProductCategory(listing.getProductCategory());
item.setStatus(0);
orderItemMapper.insert(item); orderItemMapper.insert(item);
List<ListingImageEntity> images = listingImageMapper.selectList( List<ListingImageEntity> images = listingImageMapper.selectList(
@@ -357,24 +358,30 @@ public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
} else { } else {
log.info("[Order] PaymentId:{} / OrderId:{}, 更新订单状态", dto.getPaymentId(), dto.getOrderIds()); log.info("[Order] PaymentId:{} / OrderId:{}, 更新订单状态", dto.getPaymentId(), dto.getOrderIds());
if (dto.getStatus() != null && dto.getStatus() == 1) { List<Long> targetOrderIds;
List<Long> targetOrderIds; if (dto.getOrderIds() != null && !dto.getOrderIds().isEmpty()) {
if (dto.getOrderIds() != null && !dto.getOrderIds().isEmpty()) { targetOrderIds = dto.getOrderIds();
targetOrderIds = dto.getOrderIds(); } else if (dto.getPaymentId() != null) {
} else if (dto.getPaymentId() != null) { LambdaQueryWrapper<OrderInfoEntity> qw = new LambdaQueryWrapper<>();
LambdaQueryWrapper<OrderInfoEntity> qw = new LambdaQueryWrapper<>(); qw.eq(OrderInfoEntity::getPaymentId, dto.getPaymentId());
qw.eq(OrderInfoEntity::getPaymentId, dto.getPaymentId()); targetOrderIds = this.list(qw).stream()
targetOrderIds = this.list(qw).stream() .map(OrderInfoEntity::getId)
.map(OrderInfoEntity::getId) .collect(Collectors.toList());
.collect(Collectors.toList()); } else {
} else { targetOrderIds = Collections.emptyList();
targetOrderIds = Collections.emptyList(); }
}
if (!targetOrderIds.isEmpty()) { if (!targetOrderIds.isEmpty()) {
orderItemMapper.incrementSalesVolumeByOrderIds(targetOrderIds); LambdaUpdateWrapper<OrderItemEntity> itemUpdateWrapper = new LambdaUpdateWrapper<>();
log.info("[Order] SalesVolume incremented for orderIds: {}", targetOrderIds); itemUpdateWrapper.in(OrderItemEntity::getOrderId, targetOrderIds)
} .set(OrderItemEntity::getStatus, dto.getStatus());
orderItemMapper.update(null, itemUpdateWrapper);
log.info("[Order] Item status synced for orderIds: {}", targetOrderIds);
}
if (dto.getStatus() != null && dto.getStatus() == 1) {
orderItemMapper.incrementSalesVolumeByOrderIds(targetOrderIds);
log.info("[Order] SalesVolume incremented for orderIds: {}", targetOrderIds);
} }
} }
} }

View File

@@ -83,7 +83,7 @@ public class ImageWatermarkUtil {
g2d.drawImage(original, 0, 0, width, height, null); g2d.drawImage(original, 0, 0, width, height, null);
int baseFontSize = Math.max(12, (int) (Math.min(width, height) * watermarkProperties.getFontSizeRatio())); int baseFontSize = Math.max(12, (int) (Math.min(width, height) * watermarkProperties.getFontSizeRatio()));
Font font = new Font("Arial", Font.BOLD, baseFontSize); Font font = new Font("Arial", Font.PLAIN, baseFontSize);
g2d.setFont(font); g2d.setFont(font);
int[] rgba = watermarkProperties.getColorComponents(); int[] rgba = watermarkProperties.getColorComponents();
@@ -92,8 +92,8 @@ public class ImageWatermarkUtil {
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
double radians = Math.toRadians(watermarkProperties.getRotationDegrees()); double radians = Math.toRadians(watermarkProperties.getRotationDegrees());
int stepX = (int) (baseFontSize * watermarkProperties.getSpacingRatio()); int stepX = (int) (baseFontSize * watermarkProperties.getSpacingRatioX());
int stepY = (int) (baseFontSize * watermarkProperties.getSpacingRatio()); int stepY = (int) (baseFontSize * watermarkProperties.getSpacingRatioY());
AffineTransform rotateTransform = new AffineTransform(); AffineTransform rotateTransform = new AffineTransform();
rotateTransform.translate(width / 2.0, height / 2.0); rotateTransform.translate(width / 2.0, height / 2.0);

View File

@@ -33,10 +33,11 @@ minio:
watermark: watermark:
apparel: apparel:
text: "AiDA" text: "AiDA"
font-size-ratio: 0.05 font-size-ratio: 0.053
color: "80,80,80,180" color: "194,194,194,107"
rotation-degrees: -30 rotation-degrees: 45
spacing-ratio: 3.0 spacing-ratio-x: 3.6
spacing-ratio-y: 2.5
ttl-days: 30 ttl-days: 30
bucket-name: "aida-user" bucket-name: "aida-user"

View File

@@ -93,6 +93,7 @@ CREATE TABLE seller_order_item (
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
deleted INT(1) DEFAULT 0 COMMENT '是否删除0-否1-是', deleted INT(1) DEFAULT 0 COMMENT '是否删除0-否1-是',
product_category JSON COMMENT '商品分类列表', product_category JSON COMMENT '商品分类列表',
status TINYINT DEFAULT 0 COMMENT '商品状态0-未支付1-已支付2-已取消',
INDEX idx_order_id (order_id), INDEX idx_order_id (order_id),
INDEX idx_listing_id (listing_id), INDEX idx_listing_id (listing_id),
INDEX idx_deleted (deleted) INDEX idx_deleted (deleted)

View File

@@ -12,12 +12,12 @@
soi.listing_id AS listingId, soi.listing_id AS listingId,
soi.buyer_id AS buyerId soi.buyer_id AS buyerId
FROM seller_order_item_image soi FROM seller_order_item_image soi
INNER JOIN seller_orders so ON soi.order_id = so.id INNER JOIN seller_order_item oi ON soi.order_item_id = oi.id
WHERE soi.listing_id = #{listingId} WHERE soi.listing_id = #{listingId}
AND soi.buyer_id = #{buyerId} AND soi.buyer_id = #{buyerId}
AND soi.deleted = 0 AND soi.deleted = 0
AND so.deleted = 0 AND oi.deleted = 0
AND so.status = 1 AND oi.status = 1
ORDER BY soi.sort_order ASC ORDER BY soi.sort_order ASC
</select> </select>

View File

@@ -15,7 +15,7 @@
WHERE soi.deleted = 0 WHERE soi.deleted = 0
AND so.deleted = 0 AND so.deleted = 0
AND so.buyer_id = #{dto.buyerId} AND so.buyer_id = #{dto.buyerId}
AND so.status = 1 AND soi.status = 1
<if test="dto.categories != null and dto.categories.size() > 0"> <if test="dto.categories != null and dto.categories.size() > 0">
AND ( AND (
<foreach collection="dto.categories" item="cat" separator=" OR "> <foreach collection="dto.categories" item="cat" separator=" OR ">
@@ -38,7 +38,7 @@
WHERE soi.deleted = 0 WHERE soi.deleted = 0
AND so.deleted = 0 AND so.deleted = 0
AND so.buyer_id = #{dto.buyerId} AND so.buyer_id = #{dto.buyerId}
AND so.status = 1 AND soi.status = 1
<if test="dto.categories != null and dto.categories.size() > 0"> <if test="dto.categories != null and dto.categories.size() > 0">
AND ( AND (
<foreach collection="dto.categories" item="cat" separator=" OR "> <foreach collection="dto.categories" item="cat" separator=" OR ">