diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/ExpressController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/ExpressController.java index 8b683c11c..363f4a323 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/ExpressController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/ExpressController.java @@ -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 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 parseNamePhoneAddress(@Valid @RequestBody ExpressAddressParseReqVO vo) { + ExpressStructAddressDTO dto = expressService.parseNamePhoneAddress(vo.getAddress()); + return success(BeanUtil.toBean(dto, ExpressStructAddressVO.class)); + } + } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/vo/express/ExpressAddressParseReqVO.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/vo/express/ExpressAddressParseReqVO.java new file mode 100644 index 000000000..bd2e89d44 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/vo/express/ExpressAddressParseReqVO.java @@ -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; +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/vo/express/ExpressStructAddressVO.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/vo/express/ExpressStructAddressVO.java new file mode 100644 index 000000000..7d69be284 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/xkt/vo/express/ExpressStructAddressVO.java @@ -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; +} diff --git a/xkt/src/main/java/com/ruoyi/xkt/dto/express/ExpressShipReqDTO.java b/xkt/src/main/java/com/ruoyi/xkt/dto/express/ExpressShipReqDTO.java index 5c3c0c0b4..0a90189f8 100644 --- a/xkt/src/main/java/com/ruoyi/xkt/dto/express/ExpressShipReqDTO.java +++ b/xkt/src/main/java/com/ruoyi/xkt/dto/express/ExpressShipReqDTO.java @@ -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 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; + } } diff --git a/xkt/src/main/java/com/ruoyi/xkt/dto/express/ExpressStructAddressDTO.java b/xkt/src/main/java/com/ruoyi/xkt/dto/express/ExpressStructAddressDTO.java new file mode 100644 index 000000000..60dfa8ef1 --- /dev/null +++ b/xkt/src/main/java/com/ruoyi/xkt/dto/express/ExpressStructAddressDTO.java @@ -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; +} diff --git a/xkt/src/main/java/com/ruoyi/xkt/manager/impl/ZtoExpressManagerImpl.java b/xkt/src/main/java/com/ruoyi/xkt/manager/impl/ZtoExpressManagerImpl.java index 1228ed2f8..1f17c57dd 100644 --- a/xkt/src/main/java/com/ruoyi/xkt/manager/impl/ZtoExpressManagerImpl.java +++ b/xkt/src/main/java/com/ruoyi/xkt/manager/impl/ZtoExpressManagerImpl.java @@ -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; } diff --git a/xkt/src/main/java/com/ruoyi/xkt/service/IExpressService.java b/xkt/src/main/java/com/ruoyi/xkt/service/IExpressService.java index 7b364ed36..88674849b 100644 --- a/xkt/src/main/java/com/ruoyi/xkt/service/IExpressService.java +++ b/xkt/src/main/java/com/ruoyi/xkt/service/IExpressService.java @@ -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 getRegionTreeCache(); + /** + * 智能解析地址信息 + * + * @param str + * @return + */ + ExpressStructAddressDTO parseNamePhoneAddress(String str); + } diff --git a/xkt/src/main/java/com/ruoyi/xkt/service/impl/ExpressServiceImpl.java b/xkt/src/main/java/com/ruoyi/xkt/service/impl/ExpressServiceImpl.java index fe4131956..42de58d06 100644 --- a/xkt/src/main/java/com/ruoyi/xkt/service/impl/ExpressServiceImpl.java +++ b/xkt/src/main/java/com/ruoyi/xkt/service/impl/ExpressServiceImpl.java @@ -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 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(); + } } diff --git a/xkt/src/main/java/com/ruoyi/xkt/service/impl/StoreOrderServiceImpl.java b/xkt/src/main/java/com/ruoyi/xkt/service/impl/StoreOrderServiceImpl.java index 8a31882a2..5128ce111 100644 --- a/xkt/src/main/java/com/ruoyi/xkt/service/impl/StoreOrderServiceImpl.java +++ b/xkt/src/main/java/com/ruoyi/xkt/service/impl/StoreOrderServiceImpl.java @@ -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 orderDetailIdList = new ArrayList<>(orderDetails.size()); @@ -904,7 +904,7 @@ public class StoreOrderServiceImpl implements IStoreOrderService { throw new ServiceException("未知物流渠道"); } - private ExpressShipReqDTO trans2ShipReq(StoreOrder order, List orderDetails) { + private ExpressShipReqDTO createShipReq(StoreOrder order, List 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 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; }