Merge remote-tracking branch 'yudao/feature/mall_product' into feature/mall_product

This commit is contained in:
puhui999
2023-10-20 21:04:47 +08:00
96 changed files with 1384 additions and 359 deletions

View File

@ -46,7 +46,7 @@ public class AppActivityController {
@Parameter(name = "spuId", description = "商品编号", required = true)
public CommonResult<List<AppActivityRespVO>> getActivityListBySpuId(@RequestParam("spuId") Long spuId) {
// 每种活动,只返回一个
return success(getAppActivityRespVOList(Collections.singletonList(spuId)));
return success(getAppActivityList(Collections.singletonList(spuId)));
}
@GetMapping("/list-by-spu-ids")
@ -57,16 +57,17 @@ public class AppActivityController {
return success(MapUtil.empty());
}
// 每种活动只返回一个key 为 SPU 编号
return success(convertMultiMap(getAppActivityRespVOList(spuIds), AppActivityRespVO::getSpuId));
return success(convertMultiMap(getAppActivityList(spuIds), AppActivityRespVO::getSpuId));
}
private List<AppActivityRespVO> getAppActivityRespVOList(Collection<Long> spuIds) {
private List<AppActivityRespVO> getAppActivityList(Collection<Long> spuIds) {
if (CollUtil.isEmpty(spuIds)) {
return new ArrayList<>();
}
LocalDateTime now = LocalDateTime.now();
List<AppActivityRespVO> activityList = new ArrayList<>();
// 拼团活动-获取开启的且开始的且没有结束的活动
// 1. 拼团活动 - 获取开启的且开始的且没有结束的活动
List<CombinationActivityDO> combinationActivities = combinationActivityService.getCombinationActivityBySpuIdsAndStatusAndDateTimeLt(
spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
if (CollUtil.isNotEmpty(combinationActivities)) {
@ -76,7 +77,8 @@ public class AppActivityController {
.setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));
});
}
// 秒杀活动-获取开启的且开始的且没有结束的活动
// 2. 秒杀活动 - 获取开启的且开始的且没有结束的活动
List<SeckillActivityDO> seckillActivities = seckillActivityService.getSeckillActivityBySpuIdsAndStatusAndDateTimeLt(
spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
if (CollUtil.isNotEmpty(seckillActivities)) {
@ -86,7 +88,8 @@ public class AppActivityController {
.setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));
});
}
// 砍价活动-获取开启的且开始的且没有结束的活动
// 3. 砍价活动 - 获取开启的且开始的且没有结束的活动
List<BargainActivityDO> bargainActivities = bargainActivityService.getBargainActivityBySpuIdsAndStatusAndDateTimeLt(
spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
if (CollUtil.isNotEmpty(bargainActivities)) {

View File

@ -32,7 +32,7 @@ public class AppArticleCategoryController {
public CommonResult<List<AppArticleCategoryRespVO>> getArticleCategoryList() {
List<ArticleCategoryDO> categoryList = articleCategoryService.getArticleCategoryListByStatus(
CommonStatusEnum.ENABLE.getStatus());
categoryList.sort(Comparator.comparing(ArticleCategoryDO::getSort).reversed()); // 按 sort 降序排列
categoryList.sort(Comparator.comparing(ArticleCategoryDO::getSort)); // 按 sort 降序排列
return success(ArticleCategoryConvert.INSTANCE.convertList04(categoryList));
}

View File

@ -56,10 +56,12 @@ public class AppArticleController {
return success(ArticleConvert.INSTANCE.convert01(articleService.getArticle(id)));
}
// TODO @puhui999add-browse-count 噢;前端 uniapp 也要接下;就是打开文章的时候,调用下这个接口;
@PutMapping("/add-browseCount")
@Operation(summary = "增加文章浏览量")
@Parameter(name = "id", description = "文章编号", example = "1024")
public CommonResult<Boolean> addBrowseCount(@RequestParam("id") Long id) {
// TODO @puhui999addArticleBrowseCount
articleService.addBrowseCount(id);
return success(true);
}

View File

@ -204,9 +204,6 @@ public interface CombinationActivityConvert {
return respVO;
}
@Mapping(target = "id", ignore = true)
CombinationRecordDO convert5(CombinationRecordDO headRecord);
/**
* 转换生成虚拟成团虚拟记录
*
@ -214,21 +211,19 @@ public interface CombinationActivityConvert {
* @return 虚拟记录列表
*/
default List<CombinationRecordDO> convertVirtualRecordList(CombinationRecordDO headRecord) {
List<CombinationRecordDO> createRecords = new ArrayList<>();
// 计算需要创建的虚拟成团记录数量
int count = headRecord.getUserSize() - headRecord.getUserCount();
List<CombinationRecordDO> createRecords = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
// 基础信息和团长保持一致
CombinationRecordDO newRecord = convert5(headRecord);
// 虚拟信息
newRecord.setCount(0);
newRecord.setUserId(0L);
newRecord.setNickname("");
newRecord.setAvatar("");
newRecord.setOrderId(0L);
newRecord.setCount(0) // 会单独更新下,在后续的 Service 逻辑里
.setUserId(0L).setNickname("").setAvatar("").setOrderId(0L);
createRecords.add(newRecord);
}
return createRecords;
}
@Mapping(target = "id", ignore = true)
CombinationRecordDO convert5(CombinationRecordDO headRecord);
}

View File

@ -102,6 +102,7 @@ public interface BargainActivityMapper extends BaseMapperX<BargainActivityDO> {
.groupBy("spu_id"));
}
// TODO @puhui999是不是只要 endTime 小于就可以啦;
/**
* 获取指定活动编号的活动列表且
* 开始时间和结束时间小于给定时间 dateTime 的活动列表

View File

@ -10,7 +10,6 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleCategoryD
import cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.article.ArticleMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
@ -112,12 +111,10 @@ public class ArticleServiceImpl implements ArticleService {
}
@Override
@Transactional(rollbackFor = Exception.class)
public void addBrowseCount(Long id) {
// 校验文章是否存在
validateArticleExists(id);
// 增加浏览次数 TODO 先简单做,用户规模不大,只 +1
// 增加浏览次数
articleMapper.updateBrowseCount(id);
}

View File

@ -369,6 +369,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
keyValue.setValue(keyValue.getValue() + 1);
}
} catch (Exception ignored) { // 处理异常继续循环
// TODO @puhui999拼团过期 or 虚拟成团 可以改成 expireCombinationRecord因为找方法更容易一些
log.error("[拼团过期 or 虚拟成团][record({}) 处理异常请进行处理record 数据是:{}]",
record.getId(), JsonUtils.toJsonString(record));
}
@ -383,10 +384,10 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
*/
@Transactional(rollbackFor = Exception.class)
public void handleExpireRecord(CombinationRecordDO headRecord) {
// 1.更新拼团记录
// 1. 更新拼团记录
List<CombinationRecordDO> headAndRecords = updateBatchCombinationRecords(headRecord,
CombinationRecordStatusEnum.FAILED);
// 2.订单取消
// 2. 订单取消
headAndRecords.forEach(item -> tradeOrderApi.cancelPaidOrder(item.getUserId(), item.getOrderId()));
}

View File

@ -24,8 +24,11 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - 交易订单")
@ -56,7 +59,9 @@ public class TradeOrderController {
}
// 查询用户信息
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(convertSet(pageResult.getList(), TradeOrderDO::getUserId));
Set<Long> userIds = CollUtil.unionDistinct(convertList(pageResult.getList(), TradeOrderDO::getUserId),
convertList(pageResult.getList(), TradeOrderDO::getBrokerageUserId, Objects::nonNull));
Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(userIds);
// 查询订单项
List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(
convertSet(pageResult.getList(), TradeOrderDO::getId));
@ -64,6 +69,13 @@ public class TradeOrderController {
return success(TradeOrderConvert.INSTANCE.convertPage(pageResult, orderItems, userMap));
}
@GetMapping("/summary")
@Operation(summary = "获得交易订单统计")
@PreAuthorize("@ss.hasPermission('trade:order:query')")
public CommonResult<TradeOrderSummaryRespVO> getOrderSummary(TradeOrderPageReqVO reqVO) {
return success(tradeOrderQueryService.getOrderSummary(reqVO));
}
@GetMapping("/get-detail")
@Operation(summary = "获得交易订单详情")
@Parameter(name = "id", description = "订单编号", required = true, example = "1")

View File

@ -145,4 +145,7 @@ public class TradeOrderBaseVO {
@Schema(description = "VIP 减免金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "888")
private Integer vipPrice;
@Schema(description = "推广人编号", example = "1")
private Long brokerageUserId;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@ -14,24 +14,20 @@ public class TradeOrderPageItemRespVO extends TradeOrderBaseVO {
@Schema(description = "收件人地区名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "上海 上海市 普陀区")
private String receiverAreaName;
/**
* 订单项列表
*/
@Schema(description = "订单项列表", requiredMode = Schema.RequiredMode.REQUIRED)
private List<Item> items;
// TODO @xiaobai使用 MemberUserRespVO 返回哈DTO 不直接给前端
/**
* 用户信息
*/
private MemberUserRespDTO user;
@Schema(description = "用户信息", requiredMode = Schema.RequiredMode.REQUIRED)
private MemberUserRespVO user;
@Schema(description = "推广人信息")
private MemberUserRespVO brokerageUser;
@Schema(description = "管理后台 - 交易订单的分页项的订单项目")
@Data
public static class Item extends TradeOrderItemBaseVO {
/**
* 属性数组
*/
@Schema(description = "属性列表", requiredMode = Schema.RequiredMode.REQUIRED)
private List<ProductPropertyValueDetailRespVO> properties;
}

View File

@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 交易订单统计 Response VO")
@Data
public class TradeOrderSummaryRespVO {
@Schema(description = "订单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long orderCount;
@Schema(description = "订单金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long orderPayPrice;
@Schema(description = "退款单数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long afterSaleCount;
@Schema(description = "退款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long afterSalePrice;
}

View File

@ -124,13 +124,17 @@ public interface TradeOrderConvert {
TradeOrderPageItemRespVO orderVO = convert(order, xOrderItems);
// 处理收货地址
orderVO.setReceiverAreaName(AreaUtils.format(order.getReceiverAreaId()));
// 增加用户昵称
orderVO.setUser(memberUserMap.get(orderVO.getUserId()));
// 增加用户信息
orderVO.setUser(convertUser(memberUserMap.get(orderVO.getUserId())));
// 增加推广人信息
orderVO.setBrokerageUser(convertUser(memberUserMap.get(orderVO.getBrokerageUserId())));
return orderVO;
});
return new PageResult<>(orderVOs, pageResult.getTotal());
}
MemberUserRespVO convertUser(MemberUserRespDTO memberUserRespDTO);
TradeOrderPageItemRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> items);
ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean);

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.dal.mysql.order;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
@ -11,6 +12,7 @@ import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Mapper
@ -42,6 +44,27 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
.orderByDesc(TradeOrderDO::getId));
}
// TODO @疯狂:如果用 map 返回,要不这里直接用 TradeOrderSummaryRespVO 返回?也算合理,就当 sql 查询出这么个玩意~~
default List<Map<String, Object>> selectOrderSummaryGroupByRefundStatus(TradeOrderPageReqVO reqVO, Set<Long> userIds) {
return selectMaps(new MPJLambdaWrapperX<TradeOrderDO>()
.selectAs(TradeOrderDO::getRefundStatus, TradeOrderDO::getRefundStatus) // 售后状态
.selectCount(TradeOrderDO::getId, "count") // 售后状态对应的数量
.selectSum(TradeOrderDO::getPayPrice, "price") // 售后状态对应的支付金额
.likeIfPresent(TradeOrderDO::getNo, reqVO.getNo())
.eqIfPresent(TradeOrderDO::getUserId, reqVO.getUserId())
.eqIfPresent(TradeOrderDO::getDeliveryType, reqVO.getDeliveryType())
.inIfPresent(TradeOrderDO::getUserId, userIds)
.eqIfPresent(TradeOrderDO::getType, reqVO.getType())
.eqIfPresent(TradeOrderDO::getStatus, reqVO.getStatus())
.eqIfPresent(TradeOrderDO::getPayChannelCode, reqVO.getPayChannelCode())
.eqIfPresent(TradeOrderDO::getTerminal, reqVO.getTerminal())
.eqIfPresent(TradeOrderDO::getLogisticsId, reqVO.getLogisticsId())
.inIfPresent(TradeOrderDO::getPickUpStoreId, reqVO.getPickUpStoreIds())
.likeIfPresent(TradeOrderDO::getPickUpVerifyCode, reqVO.getPickUpVerifyCode())
.betweenIfPresent(TradeOrderDO::getCreateTime, reqVO.getCreateTime())
.groupBy(TradeOrderDO::getRefundStatus)); // 按售后状态分组
}
default PageResult<TradeOrderDO> selectPage(AppTradeOrderPageReqVO reqVO, Long userId) {
return selectPage(reqVO, new LambdaQueryWrapperX<TradeOrderDO>()
.eq(TradeOrderDO::getUserId, userId)
@ -92,7 +115,7 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
default TradeOrderDO selectOneByPickUpVerifyCode(String pickUpVerifyCode) {
return selectOne(TradeOrderDO::getPickUpVerifyCode, pickUpVerifyCode);
}
default TradeOrderDO selectByUserIdAndCombinationActivityIdAndStatus(Long userId, Long combinationActivityId, Integer status) {
return selectOne(new LambdaQueryWrapperX<TradeOrderDO>()
.eq(TradeOrderDO::getUserId, userId)

View File

@ -58,10 +58,6 @@ public class TradeOrderLogAspect {
*/
private static final ThreadLocal<Map<String, Object>> EXTS = new ThreadLocal<>();
public TradeOrderLogAspect() {
System.out.println();
}
@Resource
private TradeOrderLogService orderLogService;

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.trade.service.order;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderSummaryRespVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
@ -64,6 +65,14 @@ public interface TradeOrderQueryService {
*/
PageResult<TradeOrderDO> getOrderPage(TradeOrderPageReqVO reqVO);
/**
* 获得订单统计
*
* @param reqVO 请求参数
* @return 订单统计
*/
TradeOrderSummaryRespVO getOrderSummary(TradeOrderPageReqVO reqVO);
/**
* 【会员】获得交易订单分页
*

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.trade.service.order;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
@ -8,6 +9,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderSummaryRespVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
@ -15,6 +17,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
import cn.iocoder.yudao.module.trade.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClientFactory;
import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
@ -85,24 +88,57 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
@Override
public PageResult<TradeOrderDO> getOrderPage(TradeOrderPageReqVO reqVO) {
// 根据用户查询条件构建用户编号列表
Set<Long> userIds = buildQueryConditionUserIds(reqVO);
if (userIds == null) { // 没查询到用户,说明肯定也没他的订单
return PageResult.empty();
}
// 分页查询
return tradeOrderMapper.selectPage(reqVO, userIds);
}
private Set<Long> buildQueryConditionUserIds(TradeOrderPageReqVO reqVO) {
// 获得 userId 相关的查询
Set<Long> userIds = new HashSet<>();
if (StrUtil.isNotEmpty(reqVO.getUserMobile())) {
MemberUserRespDTO user = memberUserApi.getUserByMobile(reqVO.getUserMobile());
if (user == null) { // 没查询到用户,说明肯定也没他的订单
return new PageResult<>();
return null;
}
userIds.add(user.getId());
}
if (StrUtil.isNotEmpty(reqVO.getUserNickname())) {
List<MemberUserRespDTO> users = memberUserApi.getUserListByNickname(reqVO.getUserNickname());
if (CollUtil.isEmpty(users)) { // 没查询到用户,说明肯定也没他的订单
return new PageResult<>();
return null;
}
userIds.addAll(convertSet(users, MemberUserRespDTO::getId));
}
// 分页查询
return tradeOrderMapper.selectPage(reqVO, userIds);
return userIds;
}
@Override
public TradeOrderSummaryRespVO getOrderSummary(TradeOrderPageReqVO reqVO) {
// 根据用户查询条件构建用户编号列表
Set<Long> userIds = buildQueryConditionUserIds(reqVO);
if (userIds == null) { // 没查询到用户,说明肯定也没他的订单
return new TradeOrderSummaryRespVO();
}
// 查询每个售后状态对应的数量、金额
List<Map<String, Object>> list = tradeOrderMapper.selectOrderSummaryGroupByRefundStatus(reqVO, null);
TradeOrderSummaryRespVO vo = new TradeOrderSummaryRespVO().setAfterSaleCount(0L).setAfterSalePrice(0L);
for (Map<String, Object> map : list) {
Long count = MapUtil.getLong(map, "count", 0L);
Long price = MapUtil.getLong(map, "price", 0L);
// 未退款的计入订单,部分退款、全部退款计入售后
if (TradeOrderRefundStatusEnum.NONE.getStatus().equals(MapUtil.getInt(map, "refundStatus"))) {
vo.setOrderCount(count).setOrderPayPrice(price);
} else {
vo.setAfterSaleCount(vo.getAfterSaleCount() + count).setAfterSalePrice(vo.getAfterSalePrice() + price);
}
}
return vo;
}
@Override