小如
This commit is contained in:
1 parent
4ae694bf33
commit
d61c4b4a7f
26 files changed
+934
-30
No files matched your search
+2
-2
@@ -91,7 +91,7 @@ public class DeviceController {
|
||||
|
||||
@GetMapping("/list-all-simple")
|
||||
@Operation(summary = "获得贩卖机信息精简列表(用于下拉选择)")
|
||||
@PreAuthorize("@ss.hasPermission('product:device:query')")
|
||||
@PreAuthorize("@ss.hasAnyPermissions('product:device:query','system:user:query','system:user:create','system:user:update')")
|
||||
public CommonResult<List<DeviceSimpleRespVO>> getDeviceSimpleList() {
|
||||
List<DeviceDO> list = deviceService.getDeviceSimpleList();
|
||||
return success(BeanUtils.toBean(list, DeviceSimpleRespVO.class));
|
||||
@@ -99,7 +99,7 @@ public class DeviceController {
|
||||
|
||||
@GetMapping("/list-simple-by-user")
|
||||
@Operation(summary = "获得贩卖机信息精简列表(根据用户绑定过滤,超级管理员返回全部)")
|
||||
@PreAuthorize("@ss.hasPermission('product:device:query')")
|
||||
@PreAuthorize("@ss.hasAnyPermissions('product:device:query','trade:machine-order:query','trade:machine-order:statistics','trade:machine-after-sale:query')")
|
||||
public CommonResult<List<DeviceSimpleRespVO>> getDeviceSimpleListByUser() {
|
||||
List<DeviceDO> list = deviceService.getDeviceSimpleListByUser();
|
||||
return success(BeanUtils.toBean(list, DeviceSimpleRespVO.class));
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package cn.iocoder.yudao.module.trade.enums.order;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* 管理端交易业务场景:商城 / 售货机
|
||||
*/
|
||||
public final class TradeBizSceneEnum {
|
||||
|
||||
public static final String MALL = "mall";
|
||||
public static final String MACHINE = "machine";
|
||||
|
||||
private TradeBizSceneEnum() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 未传或非法时默认商城,避免老前端误拉出售货机数据
|
||||
*/
|
||||
public static String normalize(String bizScene) {
|
||||
if (StrUtil.equalsIgnoreCase(bizScene, MACHINE)) {
|
||||
return MACHINE;
|
||||
}
|
||||
return MALL;
|
||||
}
|
||||
|
||||
public static boolean isMachine(String bizScene) {
|
||||
return MACHINE.equals(normalize(bizScene));
|
||||
}
|
||||
|
||||
public static boolean isMall(String bizScene) {
|
||||
return MALL.equals(normalize(bizScene));
|
||||
}
|
||||
|
||||
}
|
||||
+66
-4
@@ -1,11 +1,15 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.aftersale;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO;
|
||||
import cn.iocoder.yudao.module.product.api.device.DeviceApi;
|
||||
import cn.iocoder.yudao.module.product.api.device.dto.DeviceRespDto;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.*;
|
||||
import cn.iocoder.yudao.module.trade.convert.aftersale.AfterSaleConvert;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
|
||||
@@ -26,8 +30,13 @@ import org.springframework.web.bind.annotation.*;
|
||||
import javax.annotation.Resource;
|
||||
import javax.annotation.security.PermitAll;
|
||||
import javax.validation.Valid;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
@@ -49,10 +58,12 @@ public class AfterSaleController {
|
||||
private AfterSaleLogService afterSaleLogService;
|
||||
@Resource
|
||||
private MemberUserApi memberUserApi;
|
||||
@Resource
|
||||
private DeviceApi deviceApi;
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得售后订单分页")
|
||||
@PreAuthorize("@ss.hasPermission('trade:after-sale:query')")
|
||||
@PreAuthorize("@ss.hasAnyPermissions('trade:after-sale:query','trade:machine-after-sale:query')")
|
||||
public CommonResult<PageResult<AfterSaleRespPageItemVO>> getAfterSalePage(@Valid AfterSalePageReqVO pageVO) {
|
||||
// 查询售后
|
||||
PageResult<AfterSaleDO> pageResult = afterSaleService.getAfterSalePage(pageVO);
|
||||
@@ -63,19 +74,25 @@ public class AfterSaleController {
|
||||
// 查询会员
|
||||
Map<Long, MemberUserRespDTO> memberUsers = memberUserApi.getUserMap(
|
||||
convertSet(pageResult.getList(), AfterSaleDO::getUserId));
|
||||
return success(AfterSaleConvert.INSTANCE.convertPage(pageResult, memberUsers));
|
||||
PageResult<AfterSaleRespPageItemVO> voPage = AfterSaleConvert.INSTANCE.convertPage(pageResult, memberUsers);
|
||||
fillAfterSaleDeviceInfo(voPage.getList(), pageResult.getList());
|
||||
return success(voPage);
|
||||
}
|
||||
|
||||
@GetMapping("/get-detail")
|
||||
@Operation(summary = "获得售后订单详情")
|
||||
@Parameter(name = "id", description = "售后编号", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('trade:after-sale:query')")
|
||||
public CommonResult<AfterSaleDetailRespVO> getOrderDetail(@RequestParam("id") Long id) {
|
||||
@PreAuthorize("@ss.hasAnyPermissions('trade:after-sale:query','trade:machine-after-sale:query')")
|
||||
public CommonResult<AfterSaleDetailRespVO> getOrderDetail(@RequestParam("id") Long id,
|
||||
@RequestParam(value = "bizScene", required = false) String bizScene) {
|
||||
// 查询订单
|
||||
AfterSaleDO afterSale = afterSaleService.getAfterSale(id);
|
||||
if (afterSale == null) {
|
||||
return success(null);
|
||||
}
|
||||
if (!tradeOrderQueryService.hasAdminOrderDevicePermission(afterSale.getOrderId(), bizScene)) {
|
||||
return success(null);
|
||||
}
|
||||
|
||||
// 查询订单
|
||||
TradeOrderDO order = tradeOrderQueryService.getOrder(afterSale.getOrderId());
|
||||
@@ -141,4 +158,49 @@ public class AfterSaleController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充售后关联订单项的设备 IMEI / 名称
|
||||
*/
|
||||
private void fillAfterSaleDeviceInfo(List<AfterSaleRespPageItemVO> voList, List<AfterSaleDO> doList) {
|
||||
if (CollUtil.isEmpty(voList) || CollUtil.isEmpty(doList)) {
|
||||
return;
|
||||
}
|
||||
Set<Long> orderIds = doList.stream()
|
||||
.map(AfterSaleDO::getOrderId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
if (CollUtil.isEmpty(orderIds)) {
|
||||
return;
|
||||
}
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(orderIds);
|
||||
Map<Long, String> itemDeviceMap = orderItems.stream()
|
||||
.filter(item -> StrUtil.isNotEmpty(item.getDeviceId()))
|
||||
.collect(Collectors.toMap(TradeOrderItemDO::getId, TradeOrderItemDO::getDeviceId, (a, b) -> a));
|
||||
if (itemDeviceMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<Long, Long> afterSaleItemIdMap = doList.stream()
|
||||
.filter(d -> d.getId() != null && d.getOrderItemId() != null)
|
||||
.collect(Collectors.toMap(AfterSaleDO::getId, AfterSaleDO::getOrderItemId, (a, b) -> a));
|
||||
Set<String> imeis = new java.util.HashSet<>(itemDeviceMap.values());
|
||||
Map<String, DeviceRespDto> deviceMap = CollectionUtils.convertMap(
|
||||
imeis.isEmpty() ? Collections.emptyList() : deviceApi.getDeviceByDeviceIds(new ArrayList<>(imeis)),
|
||||
DeviceRespDto::getDeviceId);
|
||||
for (AfterSaleRespPageItemVO vo : voList) {
|
||||
Long orderItemId = afterSaleItemIdMap.get(vo.getId());
|
||||
if (orderItemId == null) {
|
||||
continue;
|
||||
}
|
||||
String imei = itemDeviceMap.get(orderItemId);
|
||||
if (StrUtil.isEmpty(imei)) {
|
||||
continue;
|
||||
}
|
||||
vo.setDeviceId(imei);
|
||||
DeviceRespDto device = deviceMap.get(imei);
|
||||
if (device != null) {
|
||||
vo.setDeviceName(device.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+15
@@ -49,4 +49,19 @@ public class AfterSalePageReqVO extends PageParam {
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
/**
|
||||
* 业务场景:mall=商城,machine=售货机;未传默认 mall
|
||||
*/
|
||||
@Schema(description = "业务场景:mall / machine", example = "mall")
|
||||
private String bizScene;
|
||||
|
||||
@Schema(description = "设备 IMEI(售货机筛选)", example = "860000000000001")
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 数据权限:当前用户绑定的设备 IMEI 集合
|
||||
*/
|
||||
@Schema(description = "数据权限用:当前用户绑定的设备 IMEI 集合", hidden = true)
|
||||
private java.util.List<String> bindDeviceImeis;
|
||||
|
||||
}
|
||||
+6
@@ -32,4 +32,10 @@ public class AfterSaleRespPageItemVO extends AfterSaleBaseVO {
|
||||
*/
|
||||
private MemberUserRespVO user;
|
||||
|
||||
@Schema(description = "设备IMEI", example = "860000000000001")
|
||||
private String deviceId;
|
||||
|
||||
@Schema(description = "设备名称", example = "一号机")
|
||||
private String deviceName;
|
||||
|
||||
}
|
||||
+11
-10
@@ -73,7 +73,7 @@ public class TradeOrderController {
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得交易订单分页")
|
||||
@PreAuthorize("@ss.hasPermission('trade:order:query')")
|
||||
@PreAuthorize("@ss.hasAnyPermissions('trade:order:query','trade:machine-order:query')")
|
||||
public CommonResult<PageResult<TradeOrderPageItemRespVO>> getOrderPage(TradeOrderPageReqVO reqVO) {
|
||||
// 查询订单
|
||||
PageResult<TradeOrderDO> pageResult = tradeOrderQueryService.getOrderPage(reqVO);
|
||||
@@ -120,7 +120,7 @@ public class TradeOrderController {
|
||||
|
||||
@GetMapping("/summary")
|
||||
@Operation(summary = "获得交易订单统计")
|
||||
@PreAuthorize("@ss.hasPermission('trade:order:query')")
|
||||
@PreAuthorize("@ss.hasAnyPermissions('trade:order:query','trade:machine-order:query')")
|
||||
public CommonResult<TradeOrderSummaryRespVO> getOrderSummary(TradeOrderPageReqVO reqVO) {
|
||||
return success(tradeOrderQueryService.getOrderSummary(reqVO));
|
||||
}
|
||||
@@ -128,11 +128,12 @@ public class TradeOrderController {
|
||||
@GetMapping("/get-detail")
|
||||
@Operation(summary = "获得交易订单详情")
|
||||
@Parameter(name = "id", description = "订单编号", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('trade:order:query')")
|
||||
public CommonResult<TradeOrderDetailRespVO> getOrderDetail(@RequestParam("id") Long id) {
|
||||
@PreAuthorize("@ss.hasAnyPermissions('trade:order:query','trade:machine-order:query')")
|
||||
public CommonResult<TradeOrderDetailRespVO> getOrderDetail(@RequestParam("id") Long id,
|
||||
@RequestParam(value = "bizScene", required = false) String bizScene) {
|
||||
// 查询订单
|
||||
TradeOrderDO order = tradeOrderQueryService.getOrder(id);
|
||||
if (order == null) {
|
||||
if (order == null || !tradeOrderQueryService.hasAdminOrderDevicePermission(id, bizScene)) {
|
||||
return success(null);
|
||||
}
|
||||
// 查询订单项
|
||||
@@ -149,7 +150,7 @@ public class TradeOrderController {
|
||||
@GetMapping("/get-express-track-list")
|
||||
@Operation(summary = "获得交易订单的物流轨迹")
|
||||
@Parameter(name = "id", description = "交易订单编号")
|
||||
@PreAuthorize("@ss.hasPermission('trade:order:query')")
|
||||
@PreAuthorize("@ss.hasAnyPermissions('trade:order:query','trade:machine-order:query')")
|
||||
public CommonResult<List<?>> getOrderExpressTrackList(@RequestParam("id") Long id) {
|
||||
return success(TradeOrderConvert.INSTANCE.convertList02(
|
||||
tradeOrderQueryService.getExpressTrackList(id)));
|
||||
@@ -208,7 +209,7 @@ public class TradeOrderController {
|
||||
@GetMapping("/get-by-pick-up-verify-code")
|
||||
@Operation(summary = "查询核销码对应的订单")
|
||||
@Parameter(name = "pickUpVerifyCode", description = "自提核销码")
|
||||
@PreAuthorize("@ss.hasPermission('trade:order:query')")
|
||||
@PreAuthorize("@ss.hasAnyPermissions('trade:order:query','trade:machine-order:query')")
|
||||
public CommonResult<TradeOrderDetailRespVO> getByPickUpVerifyCode(@RequestParam("pickUpVerifyCode") String pickUpVerifyCode) {
|
||||
TradeOrderDO tradeOrder = tradeOrderUpdateService.getByPickUpVerifyCode(pickUpVerifyCode);
|
||||
return success(TradeOrderConvert.INSTANCE.convert2(tradeOrder, null));
|
||||
@@ -216,7 +217,7 @@ public class TradeOrderController {
|
||||
|
||||
@GetMapping("/sales-statistics")
|
||||
@Operation(summary = "获取订单销售统计数据")
|
||||
@PreAuthorize("@ss.hasPermission('trade:order:query')")
|
||||
@PreAuthorize("@ss.hasAnyPermissions('trade:order:query','trade:machine-order:query','trade:machine-order:statistics')")
|
||||
public CommonResult<PageResult<TradeOrderSalesStatisticsRespVO>> getSalesStatistics(TradeOrderSalesStatisticsReqVO salesStatisticsReqVO) {
|
||||
PageResult<TradeOrderItemStatisticsRespVO> orderItemStatisticsRespVOList = tradeOrderQueryService.getSalesStatistics(salesStatisticsReqVO);
|
||||
|
||||
@@ -267,14 +268,14 @@ public class TradeOrderController {
|
||||
|
||||
@GetMapping("/statistics-detail")
|
||||
@Operation(summary = "获取订单销售统计详情")
|
||||
@PreAuthorize("@ss.hasPermission('trade:order:query')")
|
||||
@PreAuthorize("@ss.hasAnyPermissions('trade:order:query','trade:machine-order:query','trade:machine-order:statistics')")
|
||||
public CommonResult<PageResult<TradeOrderItemStatisticsDetailRespVO>> getStatisticsDetail(TradeOrderStatisticsDetailReqVO salesStatisticsReqVO) {
|
||||
return success(tradeOrderQueryService.getOrderItemListByBarCode(salesStatisticsReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出订单销售统计详情 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('trade:order:export')")
|
||||
@PreAuthorize("@ss.hasAnyPermissions('trade:order:export','trade:machine-order:statistics')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportStatisticsDetailExcel(@Valid TradeOrderStatisticsDetailReqVO salesStatisticsReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
|
||||
+15
@@ -61,4 +61,19 @@ public class TradeOrderPageReqVO extends PageParam {
|
||||
@InEnum(value = TerminalEnum.class, message = "订单来源 {value}")
|
||||
private Integer terminal;
|
||||
|
||||
/**
|
||||
* 业务场景:mall=商城,machine=售货机;未传默认 mall
|
||||
*/
|
||||
@Schema(description = "业务场景:mall / machine", example = "mall")
|
||||
private String bizScene;
|
||||
|
||||
@Schema(description = "设备 IMEI(售货机筛选)", example = "860000000000001")
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 数据权限:当前用户绑定的设备 IMEI 集合(非空时仅查这些设备相关订单)
|
||||
*/
|
||||
@Schema(description = "数据权限用:当前用户绑定的设备 IMEI 集合", hidden = true)
|
||||
private List<String> bindDeviceImeis;
|
||||
|
||||
}
|
||||
+12
@@ -39,4 +39,16 @@ public class TradeOrderSalesStatisticsReqVO extends PageParam {
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
/**
|
||||
* 业务场景:mall=商城,machine=售货机;未传默认 mall
|
||||
*/
|
||||
@Schema(description = "业务场景:mall / machine", example = "mall")
|
||||
private String bizScene;
|
||||
|
||||
/**
|
||||
* 数据权限:当前用户绑定的设备 IMEI 集合
|
||||
*/
|
||||
@Schema(description = "数据权限用:当前用户绑定的设备 IMEI 集合", hidden = true)
|
||||
private List<String> bindDeviceImeis;
|
||||
|
||||
}
|
||||
+13
@@ -6,6 +6,7 @@ import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@@ -25,4 +26,16 @@ public class TradeOrderStatisticsDetailReqVO extends PageParam {
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
/**
|
||||
* 业务场景:mall=商城,machine=售货机;未传默认 mall
|
||||
*/
|
||||
@Schema(description = "业务场景:mall / machine", example = "mall")
|
||||
private String bizScene;
|
||||
|
||||
/**
|
||||
* 数据权限:当前用户绑定的设备 IMEI 集合
|
||||
*/
|
||||
@Schema(description = "数据权限用:当前用户绑定的设备 IMEI 集合", hidden = true)
|
||||
private List<String> bindDeviceImeis;
|
||||
}
|
||||
+31
-2
@@ -1,21 +1,26 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql.aftersale;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
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.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeBizSceneEnum;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mapper
|
||||
public interface AfterSaleMapper extends BaseMapperX<AfterSaleDO> {
|
||||
|
||||
default PageResult<AfterSaleDO> selectPage(AfterSalePageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<AfterSaleDO>()
|
||||
LambdaQueryWrapperX<AfterSaleDO> wrapper = new LambdaQueryWrapperX<AfterSaleDO>()
|
||||
.eqIfPresent(AfterSaleDO::getUserId, reqVO.getUserId())
|
||||
.likeIfPresent(AfterSaleDO::getNo, reqVO.getNo())
|
||||
.eqIfPresent(AfterSaleDO::getStatus, reqVO.getStatus())
|
||||
@@ -24,7 +29,31 @@ public interface AfterSaleMapper extends BaseMapperX<AfterSaleDO> {
|
||||
.likeIfPresent(AfterSaleDO::getOrderNo, reqVO.getOrderNo())
|
||||
.likeIfPresent(AfterSaleDO::getSpuName, reqVO.getSpuName())
|
||||
.betweenIfPresent(AfterSaleDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(AfterSaleDO::getId));
|
||||
.orderByDesc(AfterSaleDO::getId);
|
||||
appendBizSceneAndDeviceFilter(wrapper, reqVO.getBizScene(), reqVO.getBindDeviceImeis());
|
||||
return selectPage(reqVO, wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 售后按关联订单做商城/售货机拆分 + 设备绑定过滤
|
||||
*/
|
||||
static void appendBizSceneAndDeviceFilter(LambdaQueryWrapperX<AfterSaleDO> wrapper,
|
||||
String bizScene,
|
||||
List<String> bindDeviceImeis) {
|
||||
if (CollUtil.isNotEmpty(bindDeviceImeis)) {
|
||||
String inValues = bindDeviceImeis.stream()
|
||||
.map(imei -> "'" + imei.replace("'", "") + "'")
|
||||
.collect(Collectors.joining(","));
|
||||
wrapper.inSql(AfterSaleDO::getOrderId,
|
||||
"SELECT order_id FROM trade_order_item WHERE deleted = 0 AND device_id IN (" + inValues + ")");
|
||||
return;
|
||||
}
|
||||
String machineOrderSql = TradeOrderMapper.buildMachineOrderIdSql();
|
||||
if (TradeBizSceneEnum.isMachine(bizScene)) {
|
||||
wrapper.inSql(AfterSaleDO::getOrderId, machineOrderSql);
|
||||
} else {
|
||||
wrapper.notInSql(AfterSaleDO::getOrderId, machineOrderSql);
|
||||
}
|
||||
}
|
||||
|
||||
default PageResult<AfterSaleDO> selectPage(Long userId, PageParam pageParam) {
|
||||
|
||||
+26
-2
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.trade.dal.mysql.order;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
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;
|
||||
@@ -13,6 +14,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemCountDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemOrderNoDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeBizSceneEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
@@ -71,10 +73,13 @@ public interface TradeOrderItemMapper extends BaseMapperX<TradeOrderItemDO> {
|
||||
.selectAll(TradeOrderItemDO.class)
|
||||
.selectSum(TradeOrderItemDO::getCount, TradeOrderItemCountDO::getOrderCount)
|
||||
.eqIfPresent(TradeOrderItemDO::getDeviceId, reqVO.getDeviceId())
|
||||
.inIfPresent(TradeOrderItemDO::getDeviceId, reqVO.getBindDeviceImeis())
|
||||
.eqIfPresent(TradeOrderItemDO::getBarCode, reqVO.getBarCode())
|
||||
.likeIfPresent(TradeOrderItemDO::getSpuName, reqVO.getSpuName())
|
||||
.betweenIfPresent(TradeOrderItemDO::getCreateTime, reqVO.getCreateTime())
|
||||
.groupBy(TradeOrderItemDO::getBarCode);
|
||||
appendSalesBizSceneFilter(query, reqVO.getBizScene(), CollUtil.isNotEmpty(reqVO.getBindDeviceImeis())
|
||||
|| StrUtil.isNotEmpty(reqVO.getDeviceId()));
|
||||
query.leftJoin(TradeOrderDO.class,TradeOrderDO::getId, TradeOrderItemDO::getOrderId)
|
||||
.eq(TradeOrderDO::getStatus, TradeOrderStatusEnum.COMPLETED.getStatus());
|
||||
return selectJoinPage(reqVO,TradeOrderItemCountDO.class,query);
|
||||
@@ -85,15 +90,34 @@ public interface TradeOrderItemMapper extends BaseMapperX<TradeOrderItemDO> {
|
||||
.selectAll(TradeOrderItemDO.class)
|
||||
.selectAs(TradeOrderDO::getNo, TradeOrderItemOrderNoDO::getNo)
|
||||
.eqIfPresent(TradeOrderItemDO::getDeviceId, salesStatisticsReqVO.getDeviceId())
|
||||
.inIfPresent(TradeOrderItemDO::getDeviceId, salesStatisticsReqVO.getBindDeviceImeis())
|
||||
.eqIfPresent(TradeOrderItemDO::getBarCode, salesStatisticsReqVO.getBarCode())
|
||||
.likeIfPresent(TradeOrderItemDO::getSpuName, salesStatisticsReqVO.getSpuName())
|
||||
.betweenIfPresent(TradeOrderItemDO::getCreateTime, salesStatisticsReqVO.getCreateTime());
|
||||
appendSalesBizSceneFilter(query, salesStatisticsReqVO.getBizScene(),
|
||||
CollUtil.isNotEmpty(salesStatisticsReqVO.getBindDeviceImeis())
|
||||
|| StrUtil.isNotEmpty(salesStatisticsReqVO.getDeviceId()));
|
||||
query.leftJoin(TradeOrderDO.class,TradeOrderDO::getId, TradeOrderItemDO::getOrderId);
|
||||
// return selectPage(salesStatisticsReqVO,new MPJLambdaWrapperX<TradeOrderItemDO>()
|
||||
// .eq(TradeOrderItemDO::getBarCode, salesStatisticsReqVO.getBarCode()));
|
||||
return selectJoinPage(salesStatisticsReqVO,TradeOrderItemOrderNoDO.class, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销售统计业务场景:machine 仅有 device_id;mall 仅无 device_id。
|
||||
* 若已按 bindDeviceImeis / deviceId 过滤,则不再叠加场景条件。
|
||||
*/
|
||||
static void appendSalesBizSceneFilter(MPJLambdaWrapper<TradeOrderItemDO> query,
|
||||
String bizScene,
|
||||
boolean alreadyDeviceFiltered) {
|
||||
if (alreadyDeviceFiltered) {
|
||||
return;
|
||||
}
|
||||
if (TradeBizSceneEnum.isMachine(bizScene)) {
|
||||
query.isNotNull(TradeOrderItemDO::getDeviceId).ne(TradeOrderItemDO::getDeviceId, "");
|
||||
} else {
|
||||
query.and(w -> w.isNull(TradeOrderItemDO::getDeviceId).or().eq(TradeOrderItemDO::getDeviceId, ""));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户在指定时间范围内购买某个 SPU 的总数量(统计未取消订单,含待支付)
|
||||
*
|
||||
|
||||
+85
-5
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql.order;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
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;
|
||||
@@ -7,6 +8,8 @@ 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;
|
||||
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeBizSceneEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@@ -15,6 +18,7 @@ import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mapper
|
||||
public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
|
||||
@@ -29,7 +33,7 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
|
||||
}
|
||||
|
||||
default PageResult<TradeOrderDO> selectPage(TradeOrderPageReqVO reqVO, Set<Long> userIds) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<TradeOrderDO>()
|
||||
LambdaQueryWrapperX<TradeOrderDO> wrapper = new LambdaQueryWrapperX<TradeOrderDO>()
|
||||
.likeIfPresent(TradeOrderDO::getNo, reqVO.getNo())
|
||||
.eqIfPresent(TradeOrderDO::getUserId, reqVO.getUserId())
|
||||
.eqIfPresent(TradeOrderDO::getDeliveryType, reqVO.getDeliveryType())
|
||||
@@ -42,12 +46,14 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
|
||||
.inIfPresent(TradeOrderDO::getPickUpStoreId, reqVO.getPickUpStoreIds())
|
||||
.likeIfPresent(TradeOrderDO::getPickUpVerifyCode, reqVO.getPickUpVerifyCode())
|
||||
.betweenIfPresent(TradeOrderDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(TradeOrderDO::getId));
|
||||
.orderByDesc(TradeOrderDO::getId);
|
||||
appendBizSceneAndDeviceFilter(wrapper, reqVO.getBizScene(), reqVO.getBindDeviceImeis());
|
||||
return selectPage(reqVO, wrapper);
|
||||
}
|
||||
|
||||
// TODO @疯狂:如果用 map 返回,要不这里直接用 TradeOrderSummaryRespVO 返回?也算合理,就当 sql 查询出这么个玩意~~
|
||||
default List<Map<String, Object>> selectOrderSummaryGroupByRefundStatus(TradeOrderPageReqVO reqVO, Set<Long> userIds) {
|
||||
return selectMaps(new MPJLambdaWrapperX<TradeOrderDO>()
|
||||
MPJLambdaWrapperX<TradeOrderDO> wrapper = new MPJLambdaWrapperX<TradeOrderDO>()
|
||||
.selectAs(TradeOrderDO::getRefundStatus, TradeOrderDO::getRefundStatus) // 售后状态
|
||||
.selectCount(TradeOrderDO::getId, "count") // 售后状态对应的数量
|
||||
.selectSum(TradeOrderDO::getPayPrice, "price") // 售后状态对应的支付金额
|
||||
@@ -62,8 +68,82 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
|
||||
.eqIfPresent(TradeOrderDO::getLogisticsId, reqVO.getLogisticsId())
|
||||
.inIfPresent(TradeOrderDO::getPickUpStoreId, reqVO.getPickUpStoreIds())
|
||||
.likeIfPresent(TradeOrderDO::getPickUpVerifyCode, reqVO.getPickUpVerifyCode())
|
||||
.betweenIfPresent(TradeOrderDO::getCreateTime, reqVO.getCreateTime())
|
||||
.groupBy(TradeOrderDO::getRefundStatus)); // 按售后状态分组
|
||||
.betweenIfPresent(TradeOrderDO::getCreateTime, reqVO.getCreateTime());
|
||||
appendBizSceneAndDeviceFilter(wrapper, reqVO.getBizScene(), reqVO.getBindDeviceImeis());
|
||||
wrapper.groupBy(TradeOrderDO::getRefundStatus); // 按售后状态分组
|
||||
return selectMaps(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务场景 + 设备数据权限
|
||||
* <ul>
|
||||
* <li>bindDeviceImeis 非空:仅这些 IMEI 相关订单(售货机侧)</li>
|
||||
* <li>machine:售货机订单(type/delivery/item.device_id)</li>
|
||||
* <li>mall(默认):排除售货机订单</li>
|
||||
* </ul>
|
||||
*/
|
||||
static void appendBizSceneAndDeviceFilter(LambdaQueryWrapperX<TradeOrderDO> wrapper,
|
||||
String bizScene,
|
||||
List<String> bindDeviceImeis) {
|
||||
if (CollUtil.isNotEmpty(bindDeviceImeis)) {
|
||||
wrapper.inSql(TradeOrderDO::getId, buildOrderIdInSqlByDeviceImeis(bindDeviceImeis));
|
||||
return;
|
||||
}
|
||||
if (TradeBizSceneEnum.isMachine(bizScene)) {
|
||||
wrapper.inSql(TradeOrderDO::getId, buildMachineOrderIdSql());
|
||||
} else {
|
||||
wrapper.notInSql(TradeOrderDO::getId, buildMachineOrderIdSql());
|
||||
}
|
||||
}
|
||||
|
||||
static void appendBizSceneAndDeviceFilter(MPJLambdaWrapperX<TradeOrderDO> wrapper,
|
||||
String bizScene,
|
||||
List<String> bindDeviceImeis) {
|
||||
if (CollUtil.isNotEmpty(bindDeviceImeis)) {
|
||||
wrapper.inSql(TradeOrderDO::getId, buildOrderIdInSqlByDeviceImeis(bindDeviceImeis));
|
||||
return;
|
||||
}
|
||||
if (TradeBizSceneEnum.isMachine(bizScene)) {
|
||||
wrapper.inSql(TradeOrderDO::getId, buildMachineOrderIdSql());
|
||||
} else {
|
||||
wrapper.notInSql(TradeOrderDO::getId, buildMachineOrderIdSql());
|
||||
}
|
||||
}
|
||||
|
||||
static String buildOrderIdInSqlByDeviceImeis(List<String> bindDeviceImeis) {
|
||||
String inValues = bindDeviceImeis.stream()
|
||||
.map(imei -> "'" + imei.replace("'", "") + "'")
|
||||
.collect(Collectors.joining(","));
|
||||
return "SELECT order_id FROM trade_order_item WHERE deleted = 0 AND device_id IN (" + inValues + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* 售货机订单:type=MACHINE 或 delivery_type=DOOR 或订单项有 device_id
|
||||
*/
|
||||
static String buildMachineOrderIdSql() {
|
||||
int machineType = TradeOrderTypeEnum.MACHINE.getType();
|
||||
int doorType = DeliveryTypeEnum.DOOR.getType();
|
||||
return "SELECT DISTINCT o.id FROM trade_order o"
|
||||
+ " LEFT JOIN trade_order_item i ON i.order_id = o.id AND i.deleted = 0"
|
||||
+ " WHERE o.deleted = 0 AND ("
|
||||
+ " o.type = " + machineType
|
||||
+ " OR o.delivery_type = " + doorType
|
||||
+ " OR (i.device_id IS NOT NULL AND i.device_id != '')"
|
||||
+ ")";
|
||||
}
|
||||
|
||||
/** 判断订单是否为售货机订单(详情权限等复用) */
|
||||
static boolean isMachineOrder(TradeOrderDO order, boolean hasDeviceItem) {
|
||||
if (order == null) {
|
||||
return false;
|
||||
}
|
||||
if (TradeOrderTypeEnum.MACHINE.getType().equals(order.getType())) {
|
||||
return true;
|
||||
}
|
||||
if (DeliveryTypeEnum.DOOR.getType().equals(order.getDeliveryType())) {
|
||||
return true;
|
||||
}
|
||||
return hasDeviceItem;
|
||||
}
|
||||
|
||||
default PageResult<TradeOrderDO> selectPage(AppTradeOrderPageReqVO reqVO, Long userId) {
|
||||
|
||||
+3
@@ -83,6 +83,9 @@ public class AfterSaleServiceImpl implements AfterSaleService {
|
||||
|
||||
@Override
|
||||
public PageResult<AfterSaleDO> getAfterSalePage(AfterSalePageReqVO pageReqVO) {
|
||||
if (!tradeOrderQueryService.prepareAfterSalePagePermission(pageReqVO)) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
return tradeAfterSaleMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
|
||||
+26
@@ -1,6 +1,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.aftersale.vo.AfterSalePageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.*;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
@@ -73,6 +74,31 @@ public interface TradeOrderQueryService {
|
||||
*/
|
||||
TradeOrderSummaryRespVO getOrderSummary(TradeOrderPageReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 管理端是否可访问该订单(按绑定设备数据权限)
|
||||
*
|
||||
* @param orderId 订单编号
|
||||
* @return 是否可访问
|
||||
*/
|
||||
boolean hasAdminOrderDevicePermission(Long orderId);
|
||||
|
||||
/**
|
||||
* 管理端是否可访问该订单(按绑定设备 + 业务场景)
|
||||
*
|
||||
* @param orderId 订单编号
|
||||
* @param bizScene mall / machine,可空(默认按设备权限)
|
||||
* @return 是否可访问
|
||||
*/
|
||||
boolean hasAdminOrderDevicePermission(Long orderId, String bizScene);
|
||||
|
||||
/**
|
||||
* 售后分页:写入 bizScene / 绑定设备过滤条件
|
||||
*
|
||||
* @param reqVO 售后分页请求
|
||||
* @return false 表示无权限应返回空
|
||||
*/
|
||||
boolean prepareAfterSalePagePermission(AfterSalePageReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 【会员】获得交易订单分页
|
||||
*
|
||||
|
||||
+266
-1
@@ -9,7 +9,11 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.device.DeviceApi;
|
||||
import cn.iocoder.yudao.module.product.api.device.dto.DeviceRespDto;
|
||||
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.*;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
|
||||
@@ -20,6 +24,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemOrderNoD
|
||||
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.TradeBizSceneEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
@@ -32,9 +37,11 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_NOT_EXISTS;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_NOT_FOUND;
|
||||
|
||||
@@ -60,6 +67,15 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
|
||||
@Resource
|
||||
private MemberUserApi memberUserApi;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@Resource
|
||||
private PermissionApi permissionApi;
|
||||
|
||||
@Resource
|
||||
private DeviceApi deviceApi;
|
||||
|
||||
// =================== Order ===================
|
||||
|
||||
@Override
|
||||
@@ -97,6 +113,10 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
|
||||
if (userIds == null) { // 没查询到用户,说明肯定也没他的订单
|
||||
return PageResult.empty();
|
||||
}
|
||||
// 设备数据权限
|
||||
if (!applyOrderDeviceDataPermission(reqVO)) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
|
||||
// 分页查询
|
||||
return tradeOrderMapper.selectPage(reqVO, userIds);
|
||||
@@ -129,6 +149,9 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
|
||||
if (userIds == null) { // 没查询到用户,说明肯定也没他的订单
|
||||
return new TradeOrderSummaryRespVO();
|
||||
}
|
||||
if (!applyOrderDeviceDataPermission(reqVO)) {
|
||||
return new TradeOrderSummaryRespVO();
|
||||
}
|
||||
// 查询每个售后状态对应的数量、金额
|
||||
List<Map<String, Object>> list = tradeOrderMapper.selectOrderSummaryGroupByRefundStatus(reqVO, userIds);
|
||||
|
||||
@@ -174,6 +197,10 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
|
||||
if (order == null) {
|
||||
throw exception(ORDER_NOT_FOUND);
|
||||
}
|
||||
// 管理端设备数据权限
|
||||
if (!hasAdminOrderDevicePermission(id)) {
|
||||
throw exception(ORDER_NOT_FOUND);
|
||||
}
|
||||
// 查询物流
|
||||
return getExpressTrackList(order);
|
||||
}
|
||||
@@ -253,16 +280,254 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
|
||||
|
||||
@Override
|
||||
public PageResult<TradeOrderItemStatisticsRespVO> getSalesStatistics(TradeOrderSalesStatisticsReqVO salesStatisticsReqVO) {
|
||||
salesStatisticsReqVO.setBizScene(TradeBizSceneEnum.normalize(salesStatisticsReqVO.getBizScene()));
|
||||
if (!applyStatisticsDeviceDataPermission(salesStatisticsReqVO)) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
PageResult<TradeOrderItemCountDO> tradeOrderItemDOS = tradeOrderItemMapper.selectSalesStatistics(salesStatisticsReqVO);
|
||||
return BeanUtils.toBean(tradeOrderItemDOS, TradeOrderItemStatisticsRespVO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<TradeOrderItemStatisticsDetailRespVO> getOrderItemListByBarCode(TradeOrderStatisticsDetailReqVO salesStatisticsReqVO) {
|
||||
salesStatisticsReqVO.setBizScene(TradeBizSceneEnum.normalize(salesStatisticsReqVO.getBizScene()));
|
||||
if (!applyStatisticsDeviceDataPermission(salesStatisticsReqVO)) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
PageResult<TradeOrderItemOrderNoDO> tradeOrderItemDOS = tradeOrderItemMapper.getOrderItemListByBarCode(salesStatisticsReqVO);
|
||||
return BeanUtils.toBean(tradeOrderItemDOS, TradeOrderItemStatisticsDetailRespVO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAdminOrderDevicePermission(Long orderId) {
|
||||
return hasAdminOrderDevicePermission(orderId, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAdminOrderDevicePermission(Long orderId, String bizScene) {
|
||||
TradeOrderDO order = tradeOrderMapper.selectById(orderId);
|
||||
if (order == null) {
|
||||
return false;
|
||||
}
|
||||
List<TradeOrderItemDO> items = tradeOrderItemMapper.selectListByOrderId(orderId);
|
||||
boolean hasDeviceItem = CollUtil.isNotEmpty(items) && items.stream()
|
||||
.anyMatch(i -> StrUtil.isNotEmpty(i.getDeviceId()));
|
||||
boolean isMachine = TradeOrderMapper.isMachineOrder(order, hasDeviceItem);
|
||||
// 显式传入场景时做商城/售货机互斥;未传则仅按设备绑定范围
|
||||
String scene = StrUtil.isEmpty(bizScene) ? null : TradeBizSceneEnum.normalize(bizScene);
|
||||
if (scene != null) {
|
||||
if (TradeBizSceneEnum.isMachine(scene) && !isMachine) {
|
||||
return false;
|
||||
}
|
||||
if (TradeBizSceneEnum.isMall(scene) && isMachine) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
AdminOrderDeviceScope scope = resolveAdminOrderDeviceScope();
|
||||
if (scope.mode == AdminOrderDeviceScope.Mode.ALL) {
|
||||
return true;
|
||||
}
|
||||
if (scope.mode == AdminOrderDeviceScope.Mode.NONE) {
|
||||
return false;
|
||||
}
|
||||
List<String> itemImeis = CollUtil.isEmpty(items) ? Collections.emptyList() : items.stream()
|
||||
.map(TradeOrderItemDO::getDeviceId)
|
||||
.filter(StrUtil::isNotEmpty)
|
||||
.collect(Collectors.toList());
|
||||
if (scope.mode == AdminOrderDeviceScope.Mode.MALL_ONLY) {
|
||||
if (scene != null && TradeBizSceneEnum.isMachine(scene)) {
|
||||
return false;
|
||||
}
|
||||
return !isMachine;
|
||||
}
|
||||
// BOUND
|
||||
if (scene == null) {
|
||||
return CollUtil.isEmpty(itemImeis) || itemImeis.stream().anyMatch(scope.imeis::contains);
|
||||
}
|
||||
if (TradeBizSceneEnum.isMachine(scene)) {
|
||||
return itemImeis.stream().anyMatch(scope.imeis::contains);
|
||||
}
|
||||
return CollUtil.isEmpty(itemImeis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean prepareAfterSalePagePermission(AfterSalePageReqVO reqVO) {
|
||||
String scene = TradeBizSceneEnum.normalize(reqVO.getBizScene());
|
||||
reqVO.setBizScene(scene);
|
||||
AdminOrderDeviceScope scope = resolveAdminOrderDeviceScope();
|
||||
String requestDeviceId = reqVO.getDeviceId();
|
||||
if (TradeBizSceneEnum.isMachine(scene)) {
|
||||
if (scope.mode == AdminOrderDeviceScope.Mode.ALL) {
|
||||
if (StrUtil.isNotEmpty(requestDeviceId)) {
|
||||
reqVO.setBindDeviceImeis(Collections.singletonList(requestDeviceId));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (scope.mode == AdminOrderDeviceScope.Mode.BOUND) {
|
||||
if (StrUtil.isNotEmpty(requestDeviceId)) {
|
||||
if (!scope.imeis.contains(requestDeviceId)) {
|
||||
return false;
|
||||
}
|
||||
reqVO.setBindDeviceImeis(Collections.singletonList(requestDeviceId));
|
||||
} else {
|
||||
reqVO.setBindDeviceImeis(scope.imeis);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return scope.mode != AdminOrderDeviceScope.Mode.NONE;
|
||||
}
|
||||
|
||||
private boolean applyOrderDeviceDataPermission(TradeOrderPageReqVO reqVO) {
|
||||
String scene = TradeBizSceneEnum.normalize(reqVO.getBizScene());
|
||||
reqVO.setBizScene(scene);
|
||||
AdminOrderDeviceScope scope = resolveAdminOrderDeviceScope();
|
||||
String requestDeviceId = reqVO.getDeviceId();
|
||||
if (TradeBizSceneEnum.isMachine(scene)) {
|
||||
if (scope.mode == AdminOrderDeviceScope.Mode.ALL) {
|
||||
if (StrUtil.isNotEmpty(requestDeviceId)) {
|
||||
reqVO.setBindDeviceImeis(Collections.singletonList(requestDeviceId));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (scope.mode == AdminOrderDeviceScope.Mode.BOUND) {
|
||||
if (StrUtil.isNotEmpty(requestDeviceId)) {
|
||||
if (!scope.imeis.contains(requestDeviceId)) {
|
||||
return false;
|
||||
}
|
||||
reqVO.setBindDeviceImeis(Collections.singletonList(requestDeviceId));
|
||||
} else {
|
||||
reqVO.setBindDeviceImeis(scope.imeis);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// 未绑定设备 / 无权限:不能进售货机订单
|
||||
return false;
|
||||
}
|
||||
// mall
|
||||
if (scope.mode == AdminOrderDeviceScope.Mode.NONE) {
|
||||
return false;
|
||||
}
|
||||
// 超管 / 未绑定 / 已绑定:商城页一律只看商城(不按 IMEI 掺售货机)
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean applyStatisticsDeviceDataPermission(TradeOrderSalesStatisticsReqVO reqVO) {
|
||||
return applyStatisticsDeviceDataPermission(reqVO.getBizScene(), reqVO.getDeviceId(),
|
||||
reqVO::setDeviceId, reqVO::setBindDeviceImeis);
|
||||
}
|
||||
|
||||
private boolean applyStatisticsDeviceDataPermission(TradeOrderStatisticsDetailReqVO reqVO) {
|
||||
return applyStatisticsDeviceDataPermission(reqVO.getBizScene(), reqVO.getDeviceId(),
|
||||
reqVO::setDeviceId, reqVO::setBindDeviceImeis);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销售统计设备过滤
|
||||
*/
|
||||
private boolean applyStatisticsDeviceDataPermission(String bizScene,
|
||||
String requestDeviceId,
|
||||
java.util.function.Consumer<String> deviceIdSetter,
|
||||
java.util.function.Consumer<List<String>> bindSetter) {
|
||||
String scene = TradeBizSceneEnum.normalize(bizScene);
|
||||
AdminOrderDeviceScope scope = resolveAdminOrderDeviceScope();
|
||||
if (TradeBizSceneEnum.isMachine(scene)) {
|
||||
if (scope.mode == AdminOrderDeviceScope.Mode.ALL) {
|
||||
return true;
|
||||
}
|
||||
if (scope.mode == AdminOrderDeviceScope.Mode.BOUND) {
|
||||
if (StrUtil.isNotEmpty(requestDeviceId)) {
|
||||
if (!scope.imeis.contains(requestDeviceId)) {
|
||||
return false;
|
||||
}
|
||||
deviceIdSetter.accept(requestDeviceId);
|
||||
return true;
|
||||
}
|
||||
bindSetter.accept(scope.imeis);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// mall:不允许按设备 IMEI 查
|
||||
if (StrUtil.isNotEmpty(requestDeviceId)) {
|
||||
return false;
|
||||
}
|
||||
return scope.mode != AdminOrderDeviceScope.Mode.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析当前管理端用户的订单设备数据权限范围
|
||||
*/
|
||||
private AdminOrderDeviceScope resolveAdminOrderDeviceScope() {
|
||||
Long userId = getLoginUserId();
|
||||
if (userId == null) {
|
||||
return AdminOrderDeviceScope.none();
|
||||
}
|
||||
if (permissionApi.hasAnyRoles(userId, "super_admin")) {
|
||||
return AdminOrderDeviceScope.all();
|
||||
}
|
||||
List<Long> userDeviceIds = adminUserApi.getUserDeviceIds(userId);
|
||||
if (CollUtil.isEmpty(userDeviceIds)) {
|
||||
// 未绑定设备:只能看商城订单,不能看任何售货机订单
|
||||
return AdminOrderDeviceScope.mallOnly();
|
||||
}
|
||||
List<DeviceRespDto> devices = deviceApi.getDeviceListByIds(userDeviceIds);
|
||||
if (CollUtil.isEmpty(devices)) {
|
||||
return AdminOrderDeviceScope.none();
|
||||
}
|
||||
List<String> imeis = devices.stream()
|
||||
.map(DeviceRespDto::getDeviceId)
|
||||
.filter(StrUtil::isNotEmpty)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
if (CollUtil.isEmpty(imeis)) {
|
||||
return AdminOrderDeviceScope.none();
|
||||
}
|
||||
return AdminOrderDeviceScope.bound(imeis);
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理端订单设备数据权限范围
|
||||
*/
|
||||
private static final class AdminOrderDeviceScope {
|
||||
private enum Mode {
|
||||
/** 超管:不限制 */
|
||||
ALL,
|
||||
/** 已绑定设备:仅这些 IMEI */
|
||||
BOUND,
|
||||
/** 未绑定设备:仅无 IMEI 的商城订单 */
|
||||
MALL_ONLY,
|
||||
/** 无权限 */
|
||||
NONE
|
||||
}
|
||||
|
||||
private final Mode mode;
|
||||
private final List<String> imeis;
|
||||
|
||||
private AdminOrderDeviceScope(Mode mode, List<String> imeis) {
|
||||
this.mode = mode;
|
||||
this.imeis = imeis;
|
||||
}
|
||||
|
||||
static AdminOrderDeviceScope all() {
|
||||
return new AdminOrderDeviceScope(Mode.ALL, null);
|
||||
}
|
||||
|
||||
static AdminOrderDeviceScope bound(List<String> imeis) {
|
||||
return new AdminOrderDeviceScope(Mode.BOUND, imeis);
|
||||
}
|
||||
|
||||
static AdminOrderDeviceScope mallOnly() {
|
||||
return new AdminOrderDeviceScope(Mode.MALL_ONLY, null);
|
||||
}
|
||||
|
||||
static AdminOrderDeviceScope none() {
|
||||
return new AdminOrderDeviceScope(Mode.NONE, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得自身的代理对象,解决 AOP 生效问题
|
||||
*
|
||||
|
||||
+8
@@ -63,6 +63,7 @@ import cn.iocoder.yudao.module.trade.service.message.TradeMessageService;
|
||||
import cn.iocoder.yudao.module.trade.service.message.bo.TradeOrderMessageWhenDeliveryOrderReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.order.bo.GiftVoucherCalculateRespBO;
|
||||
import cn.iocoder.yudao.module.trade.service.order.handler.TradeOrderHandler;
|
||||
import cn.iocoder.yudao.module.trade.service.order.TradeWxaOrderShippingService;
|
||||
import cn.iocoder.yudao.module.trade.service.price.TradePriceService;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
@@ -142,6 +143,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
@Resource
|
||||
private GiftVoucherApi giftVoucherApi;
|
||||
|
||||
@Resource
|
||||
private TradeWxaOrderShippingService tradeWxaOrderShippingService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@@ -481,6 +485,10 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
.setOrderId(order.getId()).setUserId(order.getUserId()).setMessage(null));
|
||||
// 4.2 发送订阅消息
|
||||
getSelf().sendDeliveryOrderMessage(order, deliveryReqVO);
|
||||
// 4.3 上传微信小程序发货信息
|
||||
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
|
||||
tradeWxaOrderShippingService.uploadExpressShipping(order, orderItems,
|
||||
deliveryReqVO.getLogisticsId(), deliveryReqVO.getLogisticsNo());
|
||||
}
|
||||
|
||||
@Async
|
||||
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
package cn.iocoder.yudao.module.trade.service.order;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
|
||||
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxOrderShippingUploadReqDTO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 微信小程序发货信息录入(交易结算管理)
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class TradeWxaOrderShippingService {
|
||||
|
||||
/** 物流模式:1-快递 2-同城 3-虚拟 4-用户自提 */
|
||||
private static final int LOGISTICS_TYPE_EXPRESS = 1;
|
||||
private static final int LOGISTICS_TYPE_VIRTUAL = 3;
|
||||
private static final int LOGISTICS_TYPE_PICKUP = 4;
|
||||
|
||||
@Resource
|
||||
private PayOrderApi payOrderApi;
|
||||
@Resource
|
||||
private SocialClientApi socialClientApi;
|
||||
@Resource
|
||||
private DeliveryExpressService deliveryExpressService;
|
||||
|
||||
/**
|
||||
* 快递发货后,上传微信发货信息
|
||||
*/
|
||||
@Async
|
||||
public void uploadExpressShipping(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
|
||||
Long logisticsId, String logisticsNo) {
|
||||
uploadShipping(order, orderItems, DeliveryTypeEnum.EXPRESS.getType(), logisticsId, logisticsNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 售货机出货成功后,上传微信发货信息(用户自提)
|
||||
*/
|
||||
@Async
|
||||
public void uploadMachineShipping(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
|
||||
uploadShipping(order, orderItems, DeliveryTypeEnum.DOOR.getType(), null, null);
|
||||
}
|
||||
|
||||
private void uploadShipping(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
|
||||
Integer deliveryType, Long logisticsId, String logisticsNo) {
|
||||
try {
|
||||
if (order == null || CollUtil.isEmpty(orderItems)) {
|
||||
return;
|
||||
}
|
||||
// 仅微信小程序订单
|
||||
if (!Objects.equals(order.getTerminal(), TerminalEnum.WECHAT_MINI_PROGRAM.getTerminal())) {
|
||||
log.debug("[uploadShipping][order({}) 非微信小程序订单,跳过]", order.getId());
|
||||
return;
|
||||
}
|
||||
if (order.getPayOrderId() == null) {
|
||||
log.debug("[uploadShipping][order({}) 无支付单,跳过]", order.getId());
|
||||
return;
|
||||
}
|
||||
PayOrderRespDTO payOrder = payOrderApi.getOrder(order.getPayOrderId());
|
||||
if (payOrder == null || !PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
|
||||
log.debug("[uploadShipping][order({}) 支付单未成功,跳过]", order.getId());
|
||||
return;
|
||||
}
|
||||
// 仅微信小程序支付
|
||||
if (!StrUtil.equals(payOrder.getChannelCode(), "wx_lite")) {
|
||||
log.debug("[uploadShipping][order({}) 非 wx_lite 支付(channel={}),跳过]",
|
||||
order.getId(), payOrder.getChannelCode());
|
||||
return;
|
||||
}
|
||||
if (StrUtil.isBlank(payOrder.getChannelOrderNo())) {
|
||||
log.warn("[uploadShipping][order({}) 缺少微信 transaction_id,跳过]", order.getId());
|
||||
return;
|
||||
}
|
||||
String openid = payOrder.getChannelUserId();
|
||||
if (StrUtil.isBlank(openid)) {
|
||||
log.warn("[uploadShipping][order({}) 缺少 payer openid,跳过]", order.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
int logisticsType;
|
||||
String trackingNo = null;
|
||||
String expressCompany = null;
|
||||
if (Objects.equals(deliveryType, DeliveryTypeEnum.EXPRESS.getType())) {
|
||||
if (logisticsId != null && logisticsId > 0 && StrUtil.isNotBlank(logisticsNo)) {
|
||||
logisticsType = LOGISTICS_TYPE_EXPRESS;
|
||||
trackingNo = logisticsNo;
|
||||
DeliveryExpressDO express = deliveryExpressService.getDeliveryExpress(logisticsId);
|
||||
if (express != null && StrUtil.isNotBlank(express.getCode())) {
|
||||
expressCompany = express.getCode();
|
||||
}
|
||||
} else {
|
||||
logisticsType = LOGISTICS_TYPE_VIRTUAL;
|
||||
}
|
||||
} else if (Objects.equals(deliveryType, DeliveryTypeEnum.DOOR.getType())
|
||||
|| Objects.equals(deliveryType, DeliveryTypeEnum.PICK_UP.getType())) {
|
||||
logisticsType = LOGISTICS_TYPE_PICKUP;
|
||||
} else {
|
||||
logisticsType = LOGISTICS_TYPE_VIRTUAL;
|
||||
}
|
||||
|
||||
String itemDesc = buildItemDesc(orderItems);
|
||||
SocialWxOrderShippingUploadReqDTO reqDTO = new SocialWxOrderShippingUploadReqDTO();
|
||||
reqDTO.setUserType(UserTypeEnum.MEMBER.getValue());
|
||||
reqDTO.setTransactionId(payOrder.getChannelOrderNo());
|
||||
reqDTO.setOpenid(openid);
|
||||
reqDTO.setLogisticsType(logisticsType);
|
||||
reqDTO.setTrackingNo(trackingNo);
|
||||
reqDTO.setExpressCompany(expressCompany);
|
||||
reqDTO.setItemDesc(itemDesc);
|
||||
|
||||
socialClientApi.uploadWxaOrderShipping(reqDTO);
|
||||
log.info("[uploadShipping][order({}) transactionId={} logisticsType={} 微信发货信息已上传]",
|
||||
order.getId(), payOrder.getChannelOrderNo(), logisticsType);
|
||||
} catch (Exception e) {
|
||||
log.error("[uploadShipping][order({}) 上传微信发货信息失败]", order.getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildItemDesc(List<TradeOrderItemDO> orderItems) {
|
||||
String desc = orderItems.stream()
|
||||
.map(item -> {
|
||||
String name = StrUtil.blankToDefault(item.getSpuName(), "商品");
|
||||
Integer count = item.getCount() != null ? item.getCount() : 1;
|
||||
return name + "*" + count + "个";
|
||||
})
|
||||
.collect(Collectors.joining(","));
|
||||
if (desc.length() > 120) {
|
||||
desc = desc.substring(0, 117) + "...";
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
}
|
||||
+7
@@ -19,6 +19,7 @@ import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.devicelog.DeviceLogService;
|
||||
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
|
||||
import cn.iocoder.yudao.module.trade.service.order.TradeWxaOrderShippingService;
|
||||
import cn.iocoder.yudao.module.trade.service.order.bo.DispenseRequestBo;
|
||||
import cn.iocoder.yudao.module.trade.service.order.bo.DispenseResponseBo;
|
||||
import cn.iocoder.yudao.module.trade.service.order.bo.DispenseResultStatusEnum;
|
||||
@@ -79,6 +80,9 @@ public class TradeDeviceOrderHandler implements TradeOrderHandler {
|
||||
@Resource
|
||||
private DeviceLogService deviceLogService;
|
||||
|
||||
@Resource
|
||||
private TradeWxaOrderShippingService tradeWxaOrderShippingService;
|
||||
|
||||
// TOKEN
|
||||
@Value("${xr.cabinet.token}")
|
||||
private String token;
|
||||
@@ -230,6 +234,9 @@ public class TradeDeviceOrderHandler implements TradeOrderHandler {
|
||||
} catch (Exception e) {
|
||||
log.error("[售货机出货]订单:{} 设备出货失败后回补库存异常: {}", order.getNo(), e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
// 出货成功:上传微信小程序发货信息(用户自提)
|
||||
tradeWxaOrderShippingService.uploadMachineShipping(order, orderItems);
|
||||
}
|
||||
|
||||
// 6. 无论出货成功/失败,尝试将订单状态更新为已完成(保持原有语义)
|
||||
|
||||
+10
@@ -50,4 +50,14 @@ public class PayOrderRespDTO {
|
||||
|
||||
// ========== 渠道相关字段 ==========
|
||||
|
||||
/**
|
||||
* 渠道用户编号(如微信 openid)
|
||||
*/
|
||||
private String channelUserId;
|
||||
|
||||
/**
|
||||
* 渠道订单号(如微信 transaction_id)
|
||||
*/
|
||||
private String channelOrderNo;
|
||||
|
||||
}
|
||||
+7
@@ -66,4 +66,11 @@ public interface SocialClientApi {
|
||||
*/
|
||||
void sendWxaSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 上传微信小程序发货信息(交易结算管理)
|
||||
*
|
||||
* @param reqDTO 请求
|
||||
*/
|
||||
void uploadWxaOrderShipping(SocialWxOrderShippingUploadReqDTO reqDTO);
|
||||
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package cn.iocoder.yudao.module.system.api.social.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 微信小程序 - 发货信息录入 Request DTO
|
||||
*/
|
||||
@Data
|
||||
public class SocialWxOrderShippingUploadReqDTO {
|
||||
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
@NotNull(message = "用户类型不能为空")
|
||||
private Integer userType;
|
||||
|
||||
/**
|
||||
* 微信支付单号(transaction_id)
|
||||
*/
|
||||
@NotEmpty(message = "微信支付单号不能为空")
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* 支付者 openid
|
||||
*/
|
||||
@NotEmpty(message = "openid 不能为空")
|
||||
private String openid;
|
||||
|
||||
/**
|
||||
* 物流模式:1-快递 2-同城 3-虚拟 4-用户自提
|
||||
*/
|
||||
@NotNull(message = "物流模式不能为空")
|
||||
private Integer logisticsType;
|
||||
|
||||
/**
|
||||
* 物流单号(快递发货时必填)
|
||||
*/
|
||||
private String trackingNo;
|
||||
|
||||
/**
|
||||
* 物流公司编码(快递发货时必填,参见微信文档)
|
||||
*/
|
||||
private String expressCompany;
|
||||
|
||||
/**
|
||||
* 商品描述,如:微信红包*1个
|
||||
*/
|
||||
@NotEmpty(message = "商品描述不能为空")
|
||||
private String itemDesc;
|
||||
|
||||
}
|
||||
+5
@@ -94,4 +94,9 @@ public class SocialClientApiImpl implements SocialClientApi {
|
||||
socialClientService.sendSubscribeMessage(reqDTO, template.getPriTmplId(), socialUser.getOpenid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadWxaOrderShipping(SocialWxOrderShippingUploadReqDTO reqDTO) {
|
||||
socialClientService.uploadWxaOrderShipping(reqDTO);
|
||||
}
|
||||
|
||||
}
|
||||
+3
@@ -38,4 +38,7 @@ public class UserPageReqVO extends PageParam {
|
||||
@Schema(description = "角色编号", example = "1024")
|
||||
private Long roleId;
|
||||
|
||||
@Schema(description = "设备IMEI", example = "860000000000001")
|
||||
private String deviceId;
|
||||
|
||||
}
|
||||
+8
-2
@@ -2,8 +2,7 @@ package cn.iocoder.yudao.module.system.service.social;
|
||||
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.*;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO;
|
||||
@@ -93,6 +92,13 @@ public interface SocialClientService {
|
||||
*/
|
||||
void sendSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO, String templateId, String openId);
|
||||
|
||||
/**
|
||||
* 上传微信小程序发货信息
|
||||
*
|
||||
* @param reqDTO 请求
|
||||
*/
|
||||
void uploadWxaOrderShipping(SocialWxOrderShippingUploadReqDTO reqDTO);
|
||||
|
||||
// =================== 客户端管理 ===================
|
||||
|
||||
/**
|
||||
|
||||
+40
-2
@@ -3,6 +3,10 @@ package cn.iocoder.yudao.module.system.service.social;
|
||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.api.WxMaSubscribeService;
|
||||
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
|
||||
import cn.binarywang.wx.miniapp.bean.shop.request.shipping.OrderKeyBean;
|
||||
import cn.binarywang.wx.miniapp.bean.shop.request.shipping.PayerBean;
|
||||
import cn.binarywang.wx.miniapp.bean.shop.request.shipping.ShippingListBean;
|
||||
import cn.binarywang.wx.miniapp.bean.shop.request.shipping.WxMaOrderShippingInfoUploadRequest;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
|
||||
import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl;
|
||||
@@ -18,8 +22,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.*;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO;
|
||||
@@ -54,6 +57,10 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -305,6 +312,37 @@ public class SocialClientServiceImpl implements SocialClientService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadWxaOrderShipping(SocialWxOrderShippingUploadReqDTO reqDTO) {
|
||||
WxMaService service = getWxMaService(reqDTO.getUserType());
|
||||
ShippingListBean shipping = ShippingListBean.builder()
|
||||
.itemDesc(reqDTO.getItemDesc())
|
||||
.trackingNo(reqDTO.getTrackingNo())
|
||||
.expressCompany(reqDTO.getExpressCompany())
|
||||
.build();
|
||||
WxMaOrderShippingInfoUploadRequest request = WxMaOrderShippingInfoUploadRequest.builder()
|
||||
.orderKey(OrderKeyBean.builder()
|
||||
.orderNumberType(2) // 使用微信支付单号
|
||||
.transactionId(reqDTO.getTransactionId())
|
||||
.build())
|
||||
.logisticsType(reqDTO.getLogisticsType())
|
||||
.deliveryMode(1) // 统一发货
|
||||
.shippingList(Collections.singletonList(shipping))
|
||||
.uploadTime(OffsetDateTime.now(ZoneId.of("Asia/Shanghai"))
|
||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")))
|
||||
.payer(PayerBean.builder().openid(reqDTO.getOpenid()).build())
|
||||
.build();
|
||||
try {
|
||||
service.getWxMaOrderShippingService().upload(request);
|
||||
log.info("[uploadWxaOrderShipping][transactionId({}) logisticsType({}) 上传成功]",
|
||||
reqDTO.getTransactionId(), reqDTO.getLogisticsType());
|
||||
} catch (WxErrorException e) {
|
||||
log.error("[uploadWxaOrderShipping][reqDTO({}) 上传发货信息失败 errCode={} errMsg={}]",
|
||||
reqDTO, e.getError().getErrorCode(), e.getError().getErrorMsg(), e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建发送消息请求参数
|
||||
*
|
||||
|
||||
+30
@@ -11,8 +11,11 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
||||
import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
|
||||
import cn.iocoder.yudao.module.infra.api.file.FileApi;
|
||||
import cn.iocoder.yudao.module.product.api.device.DeviceApi;
|
||||
import cn.iocoder.yudao.module.product.api.device.dto.DeviceRespDto;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthRegisterReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO;
|
||||
@@ -88,6 +91,8 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
private FileApi fileApi;
|
||||
@Resource
|
||||
private ConfigApi configApi;
|
||||
@Resource
|
||||
private DeviceApi deviceApi;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -282,6 +287,31 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
Set<Long> userIds = reqVO.getRoleId() != null ?
|
||||
permissionService.getUserRoleIdListByRoleId(singleton(reqVO.getRoleId())) : null;
|
||||
|
||||
// 按设备 IMEI 筛选:用户表 device_id 或绑定关系
|
||||
if (StrUtil.isNotEmpty(reqVO.getDeviceId())) {
|
||||
Set<Long> deviceUserIds = new HashSet<>();
|
||||
List<AdminUserDO> legacyUsers = userMapper.selectList(
|
||||
new LambdaQueryWrapperX<AdminUserDO>()
|
||||
.eq(AdminUserDO::getDeviceId, reqVO.getDeviceId()));
|
||||
deviceUserIds.addAll(convertSet(legacyUsers, AdminUserDO::getId));
|
||||
DeviceRespDto device = deviceApi.getDeviceByDeviceId(reqVO.getDeviceId());
|
||||
if (device != null && device.getId() != null) {
|
||||
deviceUserIds.addAll(convertSet(
|
||||
userDeviceMapper.selectListByDeviceId(device.getId()), UserDeviceDO::getUserId));
|
||||
}
|
||||
if (CollUtil.isEmpty(deviceUserIds)) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
if (userIds != null) {
|
||||
userIds.retainAll(deviceUserIds);
|
||||
if (CollUtil.isEmpty(userIds)) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
} else {
|
||||
userIds = deviceUserIds;
|
||||
}
|
||||
}
|
||||
|
||||
// 分页查询
|
||||
return userMapper.selectPage(reqVO, getDeptCondition(reqVO.getDeptId()), userIds);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user