提货柜代码提交

This commit is contained in:
zhangji committed 2025-07-14 09:11:35 +08:00
1 parent f378572ee6
commit 81f46a161d
38 files changed
+842 -10

No files matched your search

@@ -51,6 +51,15 @@ public class ProductSkuRespDTO {
* 库存
*/
private Integer stock;
/**
* 货道或柜门编号
*/
private String cabinetChannel;
/**
* 通道类型
*/
private String cabinetType;
/**
* 商品重量,单位:kg 千克
*/
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.product.api.spu.dto;
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuTypesEnum;
import lombok.Data;
import java.util.List;
@@ -42,6 +43,18 @@ public class ProductSpuRespDTO {
*/
private Integer status;
/**
* 商品类型
*
* 枚举 {@link ProductSpuTypesEnum}
*/
private Integer types;
/**
* 商品创建者
*/
private String creator;
// ========== SKU 相关字段 =========
/**
@@ -53,4 +53,7 @@ public interface ErrorCodeConstants {
ErrorCode FAVORITE_EXISTS = new ErrorCode(1_008_008_000, "该商品已经被收藏");
ErrorCode FAVORITE_NOT_EXISTS = new ErrorCode(1_008_008_001, "商品收藏不存在");
// ========== 贩卖机信息 1-008-009-000 ==========
ErrorCode DEVICE_NOT_EXISTS = new ErrorCode(1_008_009_000, "贩卖机信息不存在");
}
@@ -0,0 +1,95 @@
package cn.iocoder.yudao.module.product.controller.admin.device;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.product.controller.admin.device.vo.*;
import cn.iocoder.yudao.module.product.dal.dataobject.device.DeviceDO;
import cn.iocoder.yudao.module.product.service.device.DeviceService;
@Tag(name = "管理后台 - 贩卖机信息")
@RestController
@RequestMapping("/product/device")
@Validated
public class DeviceController {
@Resource
private DeviceService deviceService;
@PostMapping("/create")
@Operation(summary = "创建贩卖机信息")
@PreAuthorize("@ss.hasPermission('product:device:create')")
public CommonResult<Long> createDevice(@Valid @RequestBody DeviceSaveReqVO createReqVO) {
return success(deviceService.createDevice(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新贩卖机信息")
@PreAuthorize("@ss.hasPermission('product:device:update')")
public CommonResult<Boolean> updateDevice(@Valid @RequestBody DeviceSaveReqVO updateReqVO) {
deviceService.updateDevice(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除贩卖机信息")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('product:device:delete')")
public CommonResult<Boolean> deleteDevice(@RequestParam("id") Long id) {
deviceService.deleteDevice(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得贩卖机信息")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('product:device:query')")
public CommonResult<DeviceRespVO> getDevice(@RequestParam("id") Long id) {
DeviceDO device = deviceService.getDevice(id);
return success(BeanUtils.toBean(device, DeviceRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得贩卖机信息分页")
@PreAuthorize("@ss.hasPermission('product:device:query')")
public CommonResult<PageResult<DeviceRespVO>> getDevicePage(@Valid DevicePageReqVO pageReqVO) {
PageResult<DeviceDO> pageResult = deviceService.getDevicePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, DeviceRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出贩卖机信息 Excel")
@PreAuthorize("@ss.hasPermission('product:device:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportDeviceExcel(@Valid DevicePageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<DeviceDO> list = deviceService.getDevicePage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "贩卖机信息.xls", "数据", DeviceRespVO.class,
BeanUtils.toBean(list, DeviceRespVO.class));
}
}
@@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.product.controller.admin.device.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 贩卖机信息分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class DevicePageReqVO extends PageParam {
@Schema(description = "设备名称", example = "王五")
private String name;
@Schema(description = "设备IMEI", example = "371")
private String deviceId;
@Schema(description = "访问商品地址", example = "https://www.iocoder.cn")
private String productUrl;
@Schema(description = "文件地址", example = "https://www.iocoder.cn")
private String fileUrl;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.product.controller.admin.device.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 贩卖机信息 Response VO")
@Data
@ExcelIgnoreUnannotated
public class DeviceRespVO {
@Schema(description = "表编号,自增", requiredMode = Schema.RequiredMode.REQUIRED, example = "25232")
@ExcelProperty("表编号,自增")
private Long id;
@Schema(description = "设备名称", example = "王五")
@ExcelProperty("设备名称")
private String name;
@Schema(description = "设备IMEI", requiredMode = Schema.RequiredMode.REQUIRED, example = "371")
@ExcelProperty("设备IMEI")
private String deviceId;
@Schema(description = "访问商品地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn")
@ExcelProperty("访问商品地址")
private String productUrl;
@Schema(description = "文件地址", example = "https://www.iocoder.cn")
@ExcelProperty("文件地址")
private String fileUrl;
@Schema(description = "创建时间")
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}
@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.product.controller.admin.device.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
@Schema(description = "管理后台 - 贩卖机信息新增/修改 Request VO")
@Data
public class DeviceSaveReqVO {
@Schema(description = "表编号,自增", requiredMode = Schema.RequiredMode.REQUIRED, example = "25232")
private Long id;
@Schema(description = "设备名称", example = "王五")
private String name;
@Schema(description = "设备IMEI", requiredMode = Schema.RequiredMode.REQUIRED, example = "371")
@NotEmpty(message = "设备IMEI不能为空")
private String deviceId;
@Schema(description = "访问商品地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn")
@NotEmpty(message = "访问商品地址不能为空")
private String productUrl;
@Schema(description = "文件地址", example = "https://www.iocoder.cn")
private String fileUrl;
}
@@ -91,6 +91,7 @@ public class ProductSpuController {
return success(ProductSpuConvert.INSTANCE.convert(spu, skus));
}
@GetMapping("/list-all-simple")
@Operation(summary = "获得商品 SPU 精简列表")
@PreAuthorize("@ss.hasPermission('product:spu:query')")
@@ -45,6 +45,12 @@ public class ProductSkuRespVO {
@Schema(description = "二级分销的佣金,单位:分", example = "19")
private Integer secondBrokeragePrice;
@Schema(description = "货道或柜门编号", example = "1")
private String cabinetChannel;
@Schema(description = "通道类型", example = "1")
private String cabinetType;
@Schema(description = "属性数组")
private List<ProductSkuSaveReqVO.Property> properties;
@@ -50,6 +50,12 @@ public class ProductSkuSaveReqVO {
@Schema(description = "二级分销的佣金,单位:分", example = "19")
private Integer secondBrokeragePrice;
@Schema(description = "货道或柜门编号", example = "1")
private String cabinetChannel;
@Schema(description = "通道类型", example = "1")
private String cabinetType;
@Schema(description = "属性数组")
private List<Property> properties;
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuSimpleRespVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuDetailRespVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuRespVO;
@@ -13,9 +14,12 @@ import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
import cn.iocoder.yudao.module.product.service.history.ProductBrowseHistoryService;
import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -48,6 +52,28 @@ public class AppProductSpuController {
@Resource
private ProductBrowseHistoryService productBrowseHistoryService;
@Resource
private AdminUserApi adminUserApi;
@GetMapping("/get-spu-url")
@Operation(summary = "获取商品请求地址")
@Parameter(name = "deviceId", description = "设备IMEI", required = true)
@PermitAll
public String getUrl(String deviceId) {
//查询用户表
AdminUserRespDTO user = adminUserApi.getUserByDeviceid(Long.valueOf(deviceId));
return "/app-api/product/spu/list-all?"+user.getId();
}
@GetMapping("/list-all")
@Operation(summary = "获取商品列表")
@PreAuthorize("@ss.hasPermission('product:spu:query')")
public CommonResult<List<AppProductSpuRespVO>> getSpuList(String creator) {
List<ProductSpuDO> list = productSpuService.getSpuListByCreator(creator);
return success(BeanUtils.toBean(list, AppProductSpuRespVO.class));
}
@GetMapping("/list-by-ids")
@Operation(summary = "获得商品 SPU 列表")
@Parameter(name = "ids", description = "编号列表", required = true)
@@ -27,6 +27,12 @@ public class AppProductSpuPageReqVO extends PageParam {
@Schema(description = "创建人id", example = "1")
private String creator;
/**
* 设备IMEI
*/
@Schema(description = "设备IMEI", example = "123456")
private String deviceId;
@Schema(description = "商品类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer types;
@@ -0,0 +1,47 @@
package cn.iocoder.yudao.module.product.dal.dataobject.device;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 贩卖机信息 DO
*
* @author 小如之家
*/
@TableName("product_device")
@KeySequence("product_device_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeviceDO extends BaseDO {
/**
* 表编号,自增
*/
@TableId
private Long id;
/**
* 设备名称
*/
private String name;
/**
* 设备IMEI
*/
private String deviceId;
/**
* 访问商品地址
*/
private String productUrl;
/**
* 文件地址
*/
private String fileUrl;
}
@@ -94,6 +94,15 @@ public class ProductSkuDO extends BaseDO {
*/
private Integer salesCount;
/**
* 货道或柜门编号
*/
private String cabinetChannel;
/**
* 通道类型
*/
private String cabinetType;
/**
* 商品属性
*/
@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.product.dal.mysql.device;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.product.dal.dataobject.device.DeviceDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.product.controller.admin.device.vo.*;
/**
* 贩卖机信息 Mapper
*
* @author 小如之家
*/
@Mapper
public interface DeviceMapper extends BaseMapperX<DeviceDO> {
default PageResult<DeviceDO> selectPage(DevicePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<DeviceDO>()
.likeIfPresent(DeviceDO::getName, reqVO.getName())
.eqIfPresent(DeviceDO::getDeviceId, reqVO.getDeviceId())
.eqIfPresent(DeviceDO::getProductUrl, reqVO.getProductUrl())
.eqIfPresent(DeviceDO::getFileUrl, reqVO.getFileUrl())
.betweenIfPresent(DeviceDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(DeviceDO::getId));
}
}
@@ -17,6 +17,7 @@ import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import org.apache.ibatis.annotations.Mapper;
import org.apache.poi.ss.formula.functions.T;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -204,4 +205,13 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> {
query.eqIfPresent(ProductSpuDO::getStatus, ProductSpuStatusEnum.RECYCLE.getStatus());
}
}
default List<ProductSpuDO> getSpuListByCreator(String creator){
LambdaQueryWrapperX<ProductSpuDO> query = new LambdaQueryWrapperX<ProductSpuDO>()
//根据创建人查询商品
.eqIfPresent(ProductSpuDO::getCreator, creator);
// 上架状态 且有库存
query.eq(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus());
return selectList(query);
}
}
@@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.product.service.device;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.product.controller.admin.device.vo.*;
import cn.iocoder.yudao.module.product.dal.dataobject.device.DeviceDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
/**
* 贩卖机信息 Service 接口
*
* @author 小如之家
*/
public interface DeviceService {
/**
* 创建贩卖机信息
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createDevice(@Valid DeviceSaveReqVO createReqVO);
/**
* 更新贩卖机信息
*
* @param updateReqVO 更新信息
*/
void updateDevice(@Valid DeviceSaveReqVO updateReqVO);
/**
* 删除贩卖机信息
*
* @param id 编号
*/
void deleteDevice(Long id);
/**
* 获得贩卖机信息
*
* @param id 编号
* @return 贩卖机信息
*/
DeviceDO getDevice(Long id);
/**
* 获得贩卖机信息分页
*
* @param pageReqVO 分页查询
* @return 贩卖机信息分页
*/
PageResult<DeviceDO> getDevicePage(DevicePageReqVO pageReqVO);
}
@@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.product.service.device;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.product.controller.admin.device.vo.*;
import cn.iocoder.yudao.module.product.dal.dataobject.device.DeviceDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.product.dal.mysql.device.DeviceMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;
/**
* 贩卖机信息 Service 实现类
*
* @author 小如之家
*/
@Service
@Validated
public class DeviceServiceImpl implements DeviceService {
@Resource
private DeviceMapper deviceMapper;
@Override
public Long createDevice(DeviceSaveReqVO createReqVO) {
// 插入
DeviceDO device = BeanUtils.toBean(createReqVO, DeviceDO.class);
deviceMapper.insert(device);
// 返回
return device.getId();
}
@Override
public void updateDevice(DeviceSaveReqVO updateReqVO) {
// 校验存在
validateDeviceExists(updateReqVO.getId());
// 更新
DeviceDO updateObj = BeanUtils.toBean(updateReqVO, DeviceDO.class);
deviceMapper.updateById(updateObj);
}
@Override
public void deleteDevice(Long id) {
// 校验存在
validateDeviceExists(id);
// 删除
deviceMapper.deleteById(id);
}
private void validateDeviceExists(Long id) {
if (deviceMapper.selectById(id) == null) {
throw exception(DEVICE_NOT_EXISTS);
}
}
@Override
public DeviceDO getDevice(Long id) {
return deviceMapper.selectById(id);
}
@Override
public PageResult<DeviceDO> getDevicePage(DevicePageReqVO pageReqVO) {
return deviceMapper.selectPage(pageReqVO);
}
}
@@ -145,4 +145,6 @@ public interface ProductSpuService {
void updateBrowseCount(Long id, int incrCount);
PageResult<ProductSpuCreatorDO> getSpuPageWithCreator(ProductSpuPageReqVO pageVO);
List<ProductSpuDO> getSpuListByCreator(String creator);
}
@@ -20,6 +20,8 @@ import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
import cn.iocoder.yudao.module.product.service.brand.ProductBrandService;
import cn.iocoder.yudao.module.product.service.category.ProductCategoryService;
import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.google.common.collect.Maps;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@@ -54,6 +56,9 @@ public class ProductSpuServiceImpl implements ProductSpuService {
@Resource
private ProductCategoryService categoryService;
@Resource
private AdminUserApi adminUserApi;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createSpu(ProductSpuSaveReqVO createReqVO) {
@@ -163,6 +168,11 @@ public class ProductSpuServiceImpl implements ProductSpuService {
return productSpuMapper.selectPageWithCreator(pageVO);
}
@Override
public List<ProductSpuDO> getSpuListByCreator(String creator) {
return productSpuMapper.getSpuListByCreator(creator);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteSpu(Long id) {
@@ -231,6 +241,11 @@ public class ProductSpuServiceImpl implements ProductSpuService {
.setStatus(CommonStatusEnum.ENABLE.getStatus()).setParentIds(pageReqVO.getCategoryIds()));
categoryIds.addAll(convertList(categoryChildren, ProductCategoryDO::getId));
}
if (null!=pageReqVO.getDeviceId()){
//查询用户
AdminUserRespDTO user = adminUserApi.getUserByDeviceid(Long.valueOf(pageReqVO.getDeviceId()));
pageReqVO.setCreator(user.getId().toString());
}
// 分页查询
return productSpuMapper.selectPage(pageReqVO, categoryIds);
}
@@ -38,6 +38,7 @@ public interface ErrorCodeConstants {
ErrorCode ORDER_CANCEL_PAID_FAIL = new ErrorCode(1_011_000_033, "交易订单取消支付失败,原因:订单不是【{}】状态");
ErrorCode ORDER_PICK_UP_FAIL_NOT_VERIFY_USER = new ErrorCode(1_011_000_034, "交易订单自提失败,原因:你没有核销该门店订单的权限");
ErrorCode ORDER_CREATE_FAIL_INSUFFICIENT_USER_POINTS = new ErrorCode(1_011_000_035, "交易订单创建失败,原因:用户积分不足");
ErrorCode ORDER_RECEIVE_FAIL_STATUS_NOT_OVERPAY = new ErrorCode(1_011_000_036, "提货柜订单完成失败,订单不是【已支付】状态");
// ========== After Sale 模块 1-011-000-100 ==========
ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "售后单不存在");
@@ -48,6 +48,17 @@ tenant-id: {{appTenantId}}
"receiverMobile": "15601691300"
}
### /trade-order/update-paid 更新订单为已支付
POST {{appApi}}/trade/order/update-paid
Content-Type: application/json
Authorization: Bearer {{appToken}}
tenant-id: {{appTenantId}}
{
"merchantOrderId": "241",
"payOrderId": 20
}
### 获得订单交易的分页
GET {{appApi}}/trade/order/page?pageNo=1&pageSize=10
Authorization: Bearer {{appToken}}
@@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.trade.service.order.bo;
import lombok.Data;
/**
* 货道机发货请求
*/
@Data
public class DispenseRequestBo {
//设备IMEI
private String deviceId;
//参数格式:T,C&N,C&N,C&N
// T:通道类型(1:货道,2:货道带调货检测:0x30:柜门:0x31:柜门带反馈)
// C:货道或柜门编号,多个货道同时出货逗号分隔 =(1--80)指定货道出货
// N:出货数量
private String message;
public DispenseRequestBo(String deviceId, String message) {
this.deviceId = deviceId;
this.message = message;
}
public DispenseRequestBo() {
}
}
@@ -0,0 +1,13 @@
package cn.iocoder.yudao.module.trade.service.order.bo;
import lombok.Data;
/**
* 货道机发货请求
*/
@Data
public class DispenseResponseBo {
private int code;
private String msg;
private String data;
}
@@ -0,0 +1,230 @@
package cn.iocoder.yudao.module.trade.service.order.handler;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuTypesEnum;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
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.dal.mysql.order.TradeOrderMapper;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
import cn.iocoder.yudao.module.trade.service.order.bo.DispenseRequestBo;
import cn.iocoder.yudao.module.trade.service.order.bo.DispenseResponseBo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_RECEIVE_FAIL_STATUS_NOT_OVERPAY;
/**
* 优惠劵的 {@link TradeOrderHandler} 实现类
*
* @author 芋道源码
*/
@Component
@Slf4j
public class TradeDeviceOrderHandler implements TradeOrderHandler {
@Resource
@Lazy // 延迟加载,避免循环依赖
private TradeOrderUpdateService orderUpdateService;
@Resource
private TradeOrderQueryService orderQueryService;
@Resource
private ProductSpuApi productSpuApi;
@Resource
private ProductSkuApi productSkuApi;
@Resource
private AdminUserApi adminUserApi;
@Resource
private TradeOrderMapper tradeOrderMapper;
// TOKEN
@Value("${xr.cabinet.token}")
private String token;
//API_URL
@Value("${xr.cabinet.api_url}")
private String api_url;
@Override
public void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
//商品SPU ID列表
List<Long> spuIds = orderItems.stream()
.map(TradeOrderItemDO::getSpuId)
.distinct()
.collect(Collectors.toList());
// if(null==spuIds && spuIds.size()<=0){
// System.out.println("商品SPU ID列表 为空");
// return;
// }
//查询spu表
List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(spuIds);
//判断商品类型是否有不是提货柜商品的
if (CollUtil.isNotEmpty(spuList)) {
boolean hasNonDevice = spuList.stream()
.anyMatch(spu -> !ProductSpuTypesEnum.ENABLE.getTypes().equals(spu.getTypes()));
if (hasNonDevice) {
//订单中包含非提货柜商品
log.error("订单中包含非提货柜商品");
}else {
//判断是不是有不同机器的订单(一个创建人是一台机器)
boolean isSingleCreator = spuList.stream()
.map(ProductSpuRespDTO::getCreator) //获取每个商品的Creator
.distinct() //去重Creator
.count() == 1; // 判断是否只有一个唯一的创建人
if (isSingleCreator) {
// 只有一个Creator的逻辑处理
System.out.println("订单明细中只有一个商品创建人");
//查询商品创建人的设备IMEI
AdminUserRespDTO user = adminUserApi.getUser(Long.valueOf(spuList.get(0).getCreator()));
if (user == null|| user.getDeviceId() == null){
return;
}
//获取商品通道
List<Long> skuIds = orderItems.stream()
.map(TradeOrderItemDO::getSkuId)
.distinct()
.collect(Collectors.toList());
if(CollUtil.isEmpty(skuIds)){
return;
}
List<ProductSkuRespDTO> skuList = productSkuApi.getSkuList(skuIds);
//判断是不是有不同通道类型的订单
boolean isSingleCabinetType = skuList.stream()
.map(ProductSkuRespDTO::getCabinetType) //获取每个商品的Creator
.distinct() //去重Creator
.count() == 1; // 判断是否只有一个唯一的创建人
if (isSingleCabinetType) {
DispenseRequestBo request = new DispenseRequestBo();
request.setDeviceId(user.getDeviceId());
StringBuilder message= new StringBuilder(skuList.get(0).getCabinetType() + ",");
//遍历订单明细
for (int i = 0; i < orderItems.size(); i++) {
TradeOrderItemDO orderItem = orderItems.get(i);
ProductSkuRespDTO sku = productSkuApi.getSku(orderItem.getSkuId());
message.append(sku.getCabinetChannel()).append("&").append(orderItem.getCount());
if (i != orderItems.size() - 1) {
message.append(",");
}
}
request.setMessage(message.toString());
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", token);
// 创建请求实体
HttpEntity<DispenseRequestBo> requestEntity = new HttpEntity<>(request, headers);
// 创建RestTemplate
RestTemplate restTemplate = new RestTemplate();
try {
// 发送请求并解析响应
ResponseEntity<DispenseResponseBo> response = restTemplate.exchange(
api_url,
HttpMethod.POST,
requestEntity,
DispenseResponseBo.class
);
DispenseResponseBo body = response.getBody();
if (body != null) {
System.out.println("响应结果: " + body);
if (body.getCode() == 0) {
System.out.println("出货成功!详情: " + body.getData());
} else {
System.out.println("操作失败!原因: " + body.getMsg());
log.error("订单--{}:[出货失败]:原因: {}",order.getNo(), body.getMsg());
}
}
// 3. 更新 TradeOrderDO 状态为已完成
TradeOrderDO tradeOrderDO = tradeOrderMapper.selectById(order.getId());
int updateCount = tradeOrderMapper.updateByIdAndStatus(tradeOrderDO.getId(), tradeOrderDO.getStatus(),
new TradeOrderDO().setStatus(TradeOrderStatusEnum.COMPLETED.getStatus()).setReceiveTime(LocalDateTime.now()));
if (updateCount == 0) {
throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_OVERPAY);
}
} catch (Exception e) {
System.err.println("请求异常: " + e.getMessage());
log.error("订单--{}:[出货异常]:原因: {}",order.getNo(), e.getMessage());
}
}else {
System.out.println("订单明细中包含多个通道类型");
log.error("订单:{},明细中包含多个通道类型",order.getNo());
}
} else {
System.out.println("订单明细中包含多个商品创建人");
log.error("订单:{},明细中包含多个商品创建人",order.getNo());
}
}
}else {
System.out.println("提货柜订单spu列表为空");
}
}
public static void main(String[] args) {
String API_URL = "http://116.148.200.224:9527/ots/multiDispense";
String TOKEN = "Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhcHAxIiwiaWF0IjoxNzQ5MTMzNzE5LCJleHAiOjIwNjAxNzM3MTl9.u2VUKxPxE2H8xldabMVRYSJKf6LN-XZfbcfyj1T7czI";
// 创建请求对象
DispenseRequestBo request = new DispenseRequestBo("869667070337387", "1,11&1,12&1,13&1");
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", TOKEN);
// 创建请求实体
HttpEntity<DispenseRequestBo> requestEntity = new HttpEntity<>(request, headers);
// 创建RestTemplate
RestTemplate restTemplate = new RestTemplate();
try {
// 发送请求并解析响应
ResponseEntity<DispenseResponseBo> response = restTemplate.exchange(
API_URL,
HttpMethod.POST,
requestEntity,
DispenseResponseBo.class
);
DispenseResponseBo body = response.getBody();
if (body != null) {
System.out.println("响应结果: " + body);
if (body.getCode() == 0) {
System.out.println("出货成功!详情: " + body.getData());
} else {
System.out.println("操作失败!原因: " + body.getMsg());
}
}
} catch (Exception e) {
System.err.println("请求异常: " + e.getMessage());
}
}
}
@@ -86,4 +86,5 @@ public interface AdminUserApi {
*/
void validateUserList(Collection<Long> ids);
AdminUserRespDTO getUserByDeviceid(Long deviceid);
}
@@ -21,6 +21,10 @@ public class AdminUserRespDTO {
* 用户昵称
*/
private String nickname;
/**
* 设备IMEI
*/
private String deviceId;
/**
* 帐号状态
*
@@ -83,4 +83,10 @@ public class AdminUserApiImpl implements AdminUserApi {
userService.validateUserList(ids);
}
@Override
public AdminUserRespDTO getUserByDeviceid(Long deviceid) {
AdminUserDO user = userService.getUserByDeviceid(deviceid);
return BeanUtils.toBean(user, AdminUserRespDTO.class);
}
}
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.system.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@@ -43,4 +44,7 @@ public class UserImportExcelVO {
@DictFormat(DictTypeConstants.COMMON_STATUS)
private Integer status;
@ExcelProperty("设备IMEI")
private String deviceId;
}
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.system.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.mzt.logapi.starter.annotation.DiffLogField;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -56,6 +57,9 @@ public class UserRespVO{
@Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png")
private String avatar;
@Schema(description = "设备IMEI", example = "12345678910")
private String deviceId;
@Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty(value = "帐号状态", converter = DictConvert.class)
@DictFormat(DictTypeConstants.COMMON_STATUS)
@@ -64,6 +64,10 @@ public class UserSaveReqVO {
@DiffLogField(name = "用户头像")
private String avatar;
@Schema(description = "设备IMEI", example = "12345678910")
@DiffLogField(name = "设备IMEI")
private String deviceId;
// ========== 仅【创建】时,需要传递的字段 ==========
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
@@ -78,6 +78,11 @@ public class AdminUserDO extends TenantBaseDO {
* 用户头像
*/
private String avatar;
/**
* 设备IMEI
*/
private String deviceId;
/**
* 帐号状态
*
@@ -48,4 +48,7 @@ public interface AdminUserMapper extends BaseMapperX<AdminUserDO> {
return selectList(AdminUserDO::getDeptId, deptIds);
}
default AdminUserDO getUserByDeviceid(Long deviceid){
return selectOne(AdminUserDO::getDeviceId, deviceid);
}
}
@@ -216,4 +216,5 @@ public interface AdminUserService {
*/
boolean isPasswordMatch(String rawPassword, String encodedPassword);
AdminUserDO getUserByDeviceid(Long deviceid);
}
@@ -518,6 +518,11 @@ public class AdminUserServiceImpl implements AdminUserService {
return passwordEncoder.matches(rawPassword, encodedPassword);
}
@Override
public AdminUserDO getUserByDeviceid(Long deviceid) {
return userMapper.getUserByDeviceid(deviceid);
}
/**
* 对密码进行加密
*
@@ -46,7 +46,7 @@ spring:
primary: master
datasource:
master:
url: jdbc:mysql://121.40.41.196:3306/xr?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
url: jdbc:mysql://123.157.250.162:11294/xr?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
# url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例
# url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例
# url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
@@ -64,7 +64,7 @@ spring:
# password: Yudao@2024 # OpenGauss 连接的示例
slave: # 模拟从库,可根据自己需要修改
lazy: true # 开启懒加载,保证启动速度
url: jdbc:mysql://121.40.41.196:3306/xr?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true
url: jdbc:mysql://123.157.250.162:11294/xr?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true
username: xr
password: zd5ecNxeXHZxkWs8
# tdengine: # IoT 数据库(需要 IoT 物联网再开启噢!)
@@ -45,7 +45,7 @@ spring:
primary: master
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/xr?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
url: jdbc:mysql://127.0.0.1:11294/xr?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
# url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例
# url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例
# url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
@@ -63,7 +63,7 @@ spring:
# password: Yudao@2024 # OpenGauss 连接的示例
slave: # 模拟从库,可根据自己需要修改
lazy: true # 开启懒加载,保证启动速度
url: jdbc:mysql://127.0.0.1:3306/xr?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true
url: jdbc:mysql://127.0.0.1:11294/xr?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true
username: xr
password: zd5ecNxeXHZxkWs8
@@ -199,8 +199,8 @@ wx:
# secret: 6f270509224a7ae1296bbf1c8cb97aed
# appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的)
# secret: 4a1a04e07f6a4a0751b39c3064a92c8b
appid: wxf6854db3b11da676 # 测试号(puhui 提供的)
secret: 0de4b956c64885468e8c06e824134e72
appid: wx741ef9b42243284a # 测试号(puhui 提供的)
secret: 3b211e4bf0eeaaf27c8a369bcd10c9fb
config-storage:
type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
key-prefix: wa # Redis Key 的前缀
@@ -211,7 +211,7 @@ wx:
# 芋道配置项,设置当前项目所有自定义的配置
yudao:
captcha:
enable: true # 本地环境,暂时关闭图片验证码,方便登录等接口的测试;
enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试;
security:
mock-enable: true
pay:
@@ -3,8 +3,8 @@ spring:
name: yudao-server
profiles:
active: prod
# active: local
# active: prod
active: local
main:
allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。
@@ -362,4 +362,9 @@ yudao:
debug: false
# 插件配置 TODO 芋艿:【IOT】需要处理下
pf4j:
pluginsDir: /Users/anhaohao/code/gitee/ruoyi-vue-pro/plugins # 插件目录
pluginsDir: /Users/anhaohao/code/gitee/ruoyi-vue-pro/plugins # 插件目录
xr:
cabinet:
token: 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhcHAxIiwiaWF0IjoxNzQ5MTMzNzE5LCJleHAiOjIwNjAxNzM3MTl9.u2VUKxPxE2H8xldabMVRYSJKf6LN-XZfbcfyj1T7czI'
api_url: 'http://116.148.200.224:9527/ots/multiDispense'