feat: 订单

pull/1121/head
梁宇奇 2025-04-16 22:47:23 +08:00
parent dd42483eae
commit 02a16a6590
9 changed files with 269 additions and 9 deletions

View File

@ -3,19 +3,20 @@ package com.ruoyi.web.controller.xkt;
import cn.hutool.core.bean.BeanUtil;
import com.ruoyi.common.core.controller.XktBaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.web.controller.xkt.vo.express.ExpressAddressParseReqVO;
import com.ruoyi.web.controller.xkt.vo.express.ExpressRegionTreeNodeVO;
import com.ruoyi.web.controller.xkt.vo.express.ExpressStructAddressVO;
import com.ruoyi.xkt.dto.express.ExpressRegionTreeNodeDTO;
import com.ruoyi.xkt.dto.express.ExpressStructAddressDTO;
import com.ruoyi.xkt.manager.ExpressManager;
import com.ruoyi.xkt.manager.impl.ZtoExpressManagerImpl;
import com.ruoyi.xkt.service.IExpressService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
/**
@ -31,8 +32,6 @@ public class ExpressController extends XktBaseController {
private IExpressService expressService;
@Autowired
private List<ExpressManager> expressManagers;
@Autowired
private ZtoExpressManagerImpl ztoExpressManager;
@PreAuthorize("@ss.hasPermi('system:express:query')")
@ApiOperation("获取行政规划树")
@ -42,4 +41,12 @@ public class ExpressController extends XktBaseController {
return success(BeanUtil.copyToList(dtoList, ExpressRegionTreeNodeVO.class));
}
@PreAuthorize("@ss.hasPermi('system:express:query')")
@ApiOperation("智能解析 - 对地址、姓名、电话等,进行智能识别")
@PostMapping("parseNamePhoneAddress")
public R<ExpressStructAddressVO> parseNamePhoneAddress(@Valid @RequestBody ExpressAddressParseReqVO vo) {
ExpressStructAddressDTO dto = expressService.parseNamePhoneAddress(vo.getAddress());
return success(BeanUtil.toBean(dto, ExpressStructAddressVO.class));
}
}

View File

@ -0,0 +1,20 @@
package com.ruoyi.web.controller.xkt.vo.express;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
/**
* @author liangyq
* @date 2025-04-16 22:31
*/
@ApiModel
@Data
public class ExpressAddressParseReqVO {
@NotEmpty
@ApiModelProperty(value = "地址,包含地址、姓名、电话等")
private String address;
}

View File

@ -0,0 +1,50 @@
package com.ruoyi.web.controller.xkt.vo.express;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author liangyq
* @date 2025-04-16 16:18
*/
@ApiModel
@Data
public class ExpressStructAddressVO {
/**
*
*/
@ApiModelProperty(value = "名称")
private String contactName;
/**
*
*/
@ApiModelProperty(value = "电话")
private String contactPhoneNumber;
/**
*
*/
@ApiModelProperty(value = "省")
private String provinceCode;
@ApiModelProperty(value = "省")
private String provinceName;
/**
*
*/
@ApiModelProperty(value = "市")
private String cityCode;
@ApiModelProperty(value = "市")
private String cityName;
/**
*
*/
@ApiModelProperty(value = "区县")
private String countyCode;
@ApiModelProperty(value = "区县")
private String countyName;
/**
*
*/
@ApiModelProperty(value = "详细地址")
private String detailAddress;
}

View File

@ -1,9 +1,12 @@
package com.ruoyi.xkt.dto.express;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author liangyq
* @date 2025-04-16 15:24
@ -70,5 +73,48 @@ public class ExpressShipReqDTO {
* -
*/
private String destinationDetailAddress;
/**
*
*/
private List<OrderItem> orderItems;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class OrderItem {
/**
*
*/
private String name;
/**
*
*/
private String category;
/**
*
*/
private String material;
/**
* ,,(),
*/
private String size;
/**
* )
*/
private Long weight;
/**
* (:)
*/
private Integer unitprice;
/**
*
*/
private Integer quantity;
/**
*
*/
private String remark;
}
}

View File

@ -0,0 +1,44 @@
package com.ruoyi.xkt.dto.express;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author liangyq
* @date 2025-04-16 16:18
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ExpressStructAddressDTO {
/**
*
*/
private String contactName;
/**
*
*/
private String contactPhoneNumber;
/**
*
*/
private String provinceCode;
private String provinceName;
/**
*
*/
private String cityCode;
private String cityName;
/**
*
*/
private String countyCode;
private String countyName;
/**
*
*/
private String detailAddress;
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.xkt.manager.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
@ -72,6 +73,34 @@ public class ZtoExpressManagerImpl implements ExpressManager {
throw new ServiceException("中通订单创建失败");
}
/**
*
*
* @param str
* @return
*/
public JSONObject structureNamePhoneAddress(String str) {
ZopClient client = new ZopClient(appKey, appSecret);
ZopPublicRequest request = new ZopPublicRequest();
JSONObject body = new JSONObject();
body.set("address", str);
request.setBody(body.toString());
request.setUrl(gatewayUrl + STRUCTURE_ADDRESS_URI);
request.setEncryptionType(EncryptionType.MD5);
try {
String bodyStr = client.execute(request);
log.info("中通智能解析返回信息: {}", bodyStr);
JSONObject bodyJson = JSONUtil.parseObj(bodyStr);
boolean success = bodyJson.getBool("status");
if (success) {
return bodyJson.getJSONObject("result");
}
} catch (Exception e) {
log.error("中通智能解析异常", e);
}
throw new ServiceException("中通智能解析失败");
}
private ZtoCreateOrderReqDTO trans2CreateOrderReq(ExpressShipReqDTO expressShipReqDTO) {
ZtoCreateOrderReqDTO reqDTO = new ZtoCreateOrderReqDTO();
//合作模式 1集团客户2非集团客户
@ -113,6 +142,10 @@ public class ZtoExpressManagerImpl implements ExpressManager {
receiveInfo.setReceiverDistrict(expressShipReqDTO.getDestinationCountyName());
receiveInfo.setReceiverAddress(expressShipReqDTO.getDestinationDetailAddress());
//货物信息
reqDTO.setOrderItems(BeanUtil.copyToList(expressShipReqDTO.getOrderItems(),
ZtoCreateOrderReqDTO.OrderItem.class));
return reqDTO;
}

View File

@ -6,6 +6,7 @@ import com.ruoyi.xkt.domain.ExpressRegion;
import com.ruoyi.xkt.dto.express.ExpressContactDTO;
import com.ruoyi.xkt.dto.express.ExpressRegionDTO;
import com.ruoyi.xkt.dto.express.ExpressRegionTreeNodeDTO;
import com.ruoyi.xkt.dto.express.ExpressStructAddressDTO;
import java.util.Collection;
import java.util.List;
@ -80,5 +81,13 @@ public interface IExpressService {
*/
List<ExpressRegionTreeNodeDTO> getRegionTreeCache();
/**
*
*
* @param str
* @return
*/
ExpressStructAddressDTO parseNamePhoneAddress(String str);
}

View File

@ -5,6 +5,7 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.redis.RedisCache;
@ -16,6 +17,8 @@ import com.ruoyi.xkt.domain.Store;
import com.ruoyi.xkt.dto.express.ExpressContactDTO;
import com.ruoyi.xkt.dto.express.ExpressRegionDTO;
import com.ruoyi.xkt.dto.express.ExpressRegionTreeNodeDTO;
import com.ruoyi.xkt.dto.express.ExpressStructAddressDTO;
import com.ruoyi.xkt.manager.impl.ZtoExpressManagerImpl;
import com.ruoyi.xkt.mapper.*;
import com.ruoyi.xkt.service.IExpressService;
import org.springframework.beans.factory.annotation.Autowired;
@ -36,13 +39,13 @@ import java.util.stream.Collectors;
@Service
public class ExpressServiceImpl implements IExpressService {
private ExpressRegionDTO emptyRegion = new ExpressRegionDTO();
@Value("${express.default.province:510000}")
private String expressDefaultProvince;
@Value("${express.default.city:510100}")
private String expressDefaultCity;
@Value("${express.default.county:510114}")
private String expressDefaultCounty;
@Autowired
private ExpressMapper expressMapper;
@Autowired
@ -55,6 +58,8 @@ public class ExpressServiceImpl implements IExpressService {
private StoreMapper storeMapper;
@Autowired
private RedisCache redisCache;
@Autowired
private ZtoExpressManagerImpl ztoExpressManager;
@Override
public void checkExpress(Long expressId) {
@ -173,4 +178,39 @@ public class ExpressServiceImpl implements IExpressService {
}
return treeNodeList;
}
@Override
public ExpressStructAddressDTO parseNamePhoneAddress(String str) {
/**
* {"address":{"province":"重庆","town":"","city":"重庆市","countyId":"500107","county":"九龙坡区",
* "cityId":"500100","detail":"杨九路志龙·观江岭1号","provinceId":"500000"},"phone":"15888888888","name":"张三丰"}
*/
JSONObject rtn = ztoExpressManager.structureNamePhoneAddress(str);
JSONObject address = rtn.getJSONObject("address");
Assert.notNull(address, "获取行政区划失败");
String provinceCode = address.getStr("province");
String cityCode = address.getStr("cityId");
String countyCode = address.getStr("countyId");
String name = rtn.getStr("name");
String phone = rtn.getStr("phone");
String detailAddress = rtn.getStr("detail");
// Assert.notEmpty(provinceCode, "获取省失败");
// Assert.notEmpty(cityCode, "获取市失败");
// Assert.notEmpty(countyCode, "获取区县失败");
// Assert.notEmpty(detailAddress, "获取详细地址失败");
// Assert.notEmpty(name, "获取联系人失败");
// Assert.isTrue(PhoneUtil.isPhone(phone), "获取联系电话失败");
Map<String, ExpressRegionDTO> regionMap = getRegionMapCache();
return ExpressStructAddressDTO.builder()
.contactName(name)
.contactPhoneNumber(phone)
.provinceCode(provinceCode)
.provinceName(regionMap.getOrDefault(provinceCode, emptyRegion).getRegionName())
.cityCode(cityCode)
.cityName(regionMap.getOrDefault(cityCode, emptyRegion).getRegionName())
.countyCode(countyCode)
.countyName(regionMap.getOrDefault(countyCode, emptyRegion).getRegionName())
.detailAddress(detailAddress)
.build();
}
}

View File

@ -614,7 +614,7 @@ public class StoreOrderServiceImpl implements IStoreOrderService {
}
}
//发货
ExpressShipReqDTO shipReq = trans2ShipReq(order, orderDetails);
ExpressShipReqDTO shipReq = createShipReq(order, orderDetails);
String expressWaybillNo = expressManager.shipStoreOrder(shipReq);
List<Long> orderDetailIdList = new ArrayList<>(orderDetails.size());
@ -904,7 +904,7 @@ public class StoreOrderServiceImpl implements IStoreOrderService {
throw new ServiceException("未知物流渠道");
}
private ExpressShipReqDTO trans2ShipReq(StoreOrder order, List<StoreOrderDetail> orderDetails) {
private ExpressShipReqDTO createShipReq(StoreOrder order, List<StoreOrderDetail> orderDetails) {
ExpressShipReqDTO reqDTO = BeanUtil.toBean(order, ExpressShipReqDTO.class);
//生成请求号
reqDTO.setExpressReqNo(IdUtil.simpleUUID());
@ -922,6 +922,17 @@ public class StoreOrderServiceImpl implements IStoreOrderService {
.map(ExpressRegionDTO::getParentRegionName).orElse(null));
reqDTO.setOriginCountyName(Optional.ofNullable(regionMap.get(order.getOriginCountyCode()))
.map(ExpressRegionDTO::getParentRegionName).orElse(null));
//货物信息
List<ExpressShipReqDTO.OrderItem> orderItems = CollUtil.emptyIfNull(orderDetails).stream()
.map(o -> ExpressShipReqDTO.OrderItem
.builder()
//TODO 其他信息?
.name(o.getProdTitle())
.quantity(o.getGoodsQuantity())
.build())
.collect(Collectors.toList());
reqDTO.setOrderItems(orderItems);
return reqDTO;
}