feat(mall): 添加订单一键发货和取消发货功能
- 在订单列表页面添加一键发货和取消发货按钮
- 实现订单一键发货和取消发货的后端接口
- 添加发货人信息配置页面和相关功能
- 集成PDF.js用于渲染电子面单
- 优化订单列表页面的显示逻辑
9 files modified
5 files added
| | |
| | | package cc.mrbird.febs.common.enumerates; |
| | | |
| | | import lombok.Data; |
| | | import lombok.Getter; |
| | | |
| | | @Getter |
| | | public enum DataDictionaryEnum { |
| | | |
| | | // 发货人 |
| | | SENDER_NAME("SENDER_SET", "NAME"), |
| | | SENDER_MOBILE("SENDER_SET", "MOBILE"), |
| | | SENDER_ADDRESS("SENDER_SET", "ADDRESS"), |
| | | |
| | | // 发票的通知回调路径 |
| | | FP_CALLBACK_URL("FP_CALLBACK_URL", "FP_CALLBACK_URL"), |
| | | //微信订阅模板ID, |
| | |
| | | } |
| | | |
| | | /** |
| | | * 订单列表-取消发货 |
| | | * |
| | | * @param id |
| | | * @return |
| | | */ |
| | | @GetMapping("cancelDeliver/{id}") |
| | | @ControllerEndpoint(operation = "订单列表-取消发货", exceptionMessage = "操作失败") |
| | | public FebsResponse cancelDeliver(@NotNull(message = "{required}") @PathVariable Long id) { |
| | | return adminMallOrderService.cancelDeliver(id); |
| | | } |
| | | |
| | | /** |
| | | * 订单列表-一键发货 |
| | | * |
| | | * @param id |
| | | * @return |
| | | */ |
| | | @GetMapping("deliverPdfGoods/{id}") |
| | | @ControllerEndpoint(operation = "订单列表-一键发货", exceptionMessage = "操作失败") |
| | | public FebsResponse deliverPdfGoods(@NotNull(message = "{required}") @PathVariable Long id) { |
| | | return adminMallOrderService.deliverPdfGoods(id); |
| | | } |
| | | |
| | | /** |
| | | * 订单退款-列表 |
| | | * |
| | | * @param mallOrderRefundDto |
| | |
| | | public String kefu() { |
| | | return FebsUtil.view("modules/system/kefu"); |
| | | } |
| | | |
| | | @GetMapping("sender") |
| | | public String sender() { |
| | | return FebsUtil.view("modules/system/sender"); |
| | | } |
| | | } |
| | |
| | | IPage<AdminGoodsStatisticsVo> goodsStatistics(MallOrderItem mallOrderItem, QueryRequest request); |
| | | |
| | | FebsResponse deliverGoodsUpdate(DeliverGoodsDto deliverGoodsDto); |
| | | |
| | | FebsResponse deliverPdfGoods(Long id); |
| | | |
| | | FebsResponse cancelDeliver(Long id); |
| | | } |
| | |
| | | |
| | | import cc.mrbird.febs.common.entity.FebsResponse; |
| | | import cc.mrbird.febs.common.entity.QueryRequest; |
| | | import cc.mrbird.febs.common.enumerates.FlowTypeEnum; |
| | | import cc.mrbird.febs.common.enumerates.MoneyFlowTypeEnum; |
| | | import cc.mrbird.febs.common.enumerates.OrderDeliveryStateEnum; |
| | | import cc.mrbird.febs.common.enumerates.OrderStatusEnum; |
| | | import cc.mrbird.febs.common.enumerates.*; |
| | | import cc.mrbird.febs.common.utils.ValidateEntityUtils; |
| | | import cc.mrbird.febs.mall.dto.*; |
| | | import cc.mrbird.febs.mall.entity.*; |
| | | import cc.mrbird.febs.mall.mapper.*; |
| | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.best.javaSdk.ClientParamEnum; |
| | | import com.best.javaSdk.ClientParamService; |
| | | import com.best.javaSdk.kdCancelOrderNotify.request.KdCancelOrderNotifyReq; |
| | | import com.best.javaSdk.kdCancelOrderNotify.response.KdCancelOrderNotifyRsp; |
| | | import com.best.javaSdk.kdCreateWaybillOrderPdfNotify.request.*; |
| | | import com.best.javaSdk.kdCreateWaybillOrderPdfNotify.response.KdCreateWaybillOrderPdfNotifyRsp; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | @Slf4j |
| | |
| | | } |
| | | |
| | | @Override |
| | | public FebsResponse deliverPdfGoods(Long id) { |
| | | MallOrderInfo mallOrderInfo = ValidateEntityUtils.ensureColumnReturnEntity(id, MallOrderInfo::getId, mallOrderInfoMapper::selectOne, "订单不存在"); |
| | | ValidateEntityUtils.ensureEqual(mallOrderInfo.getStatus(), OrderStatusEnum.WAIT_SHIPPING.getValue(), "订单不是待发货状态"); |
| | | KdCreateWaybillOrderPdfNotifyReq kdCreateWaybillOrderPdfNotifyReq = new KdCreateWaybillOrderPdfNotifyReq(); |
| | | |
| | | MallMember mallMember = ValidateEntityUtils.ensureColumnReturnEntity(mallOrderInfo.getMemberId(), MallMember::getId, mallMemberMapper::selectOne, "会员不存在"); |
| | | |
| | | kdCreateWaybillOrderPdfNotifyReq.setTxLogisticId(mallOrderInfo.getOrderNo()); |
| | | kdCreateWaybillOrderPdfNotifyReq.setServiceType("1");//服务类型(0-线下下单,1-线上下单) |
| | | kdCreateWaybillOrderPdfNotifyReq.setSpecial("1"); |
| | | |
| | | //发货人信息 |
| | | Sender sender = new Sender(); |
| | | sender.setName(dataDictionaryCustomMapper.selectDicDataByTypeAndCode( |
| | | DataDictionaryEnum.SENDER_NAME.getType(), |
| | | DataDictionaryEnum.SENDER_NAME.getCode() |
| | | ).getValue()); |
| | | sender.setMobile(dataDictionaryCustomMapper.selectDicDataByTypeAndCode( |
| | | DataDictionaryEnum.SENDER_MOBILE.getType(), |
| | | DataDictionaryEnum.SENDER_MOBILE.getCode() |
| | | ).getValue()); |
| | | sender.setAddress(dataDictionaryCustomMapper.selectDicDataByTypeAndCode( |
| | | DataDictionaryEnum.SENDER_ADDRESS.getType(), |
| | | DataDictionaryEnum.SENDER_ADDRESS.getCode() |
| | | ).getValue()); |
| | | kdCreateWaybillOrderPdfNotifyReq.setSender(sender); |
| | | |
| | | //收件人 |
| | | Receiver receiver = new Receiver(); |
| | | receiver.setName(mallMember.getRealName()); |
| | | receiver.setMobile(mallMember.getPhone()); |
| | | receiver.setAddress(mallOrderInfo.getAddress()); |
| | | kdCreateWaybillOrderPdfNotifyReq.setReceiver(receiver); |
| | | |
| | | //包裹 |
| | | Items items = new Items(); |
| | | ArrayList<Item> itemList = new ArrayList<>(); |
| | | List<MallOrderItem> mallOrderItemList = ValidateEntityUtils |
| | | .ensureColumnReturnEntityList(id, MallOrderItem::getOrderId, mallOrderItemMapper::selectList, "订单不存在"); |
| | | StringBuffer itemName = new StringBuffer(); |
| | | itemName.append("商品:"); |
| | | mallOrderItemList.forEach(mallOrderItem -> { |
| | | itemName.append(mallOrderItem.getGoodsName()+"-"+mallOrderItem.getSkuName()); |
| | | }); |
| | | Item item = new Item(); |
| | | item.setItemName(itemName.toString()); |
| | | itemList.add(item); |
| | | items.setItem(itemList); |
| | | kdCreateWaybillOrderPdfNotifyReq.setItems(items); |
| | | |
| | | kdCreateWaybillOrderPdfNotifyReq.setPiece(1); |
| | | KdCreateWaybillOrderPdfNotifyRsp pdfOrder = ClientParamService.getInstance(ClientParamEnum.TEST.name()).createPdfOrder(kdCreateWaybillOrderPdfNotifyReq); |
| | | |
| | | ValidateEntityUtils.ensureEqual(pdfOrder.getResult(), true, "一键发货失败,创建PDF电子面单异常"); |
| | | //更新发货状态 |
| | | mallOrderInfoMapper.updateOrderStateAndDeliveryState( |
| | | mallOrderInfo.getId(), |
| | | OrderStatusEnum.WAIT_FINISH.getValue(), |
| | | OrderDeliveryStateEnum.DELIVERY_ING.getValue()); |
| | | |
| | | MallExpressInfo mallExpressInfo = new MallExpressInfo(); |
| | | mallExpressInfo.setMemberId(mallOrderInfo.getMemberId()); |
| | | mallExpressInfo.setOrderId(mallOrderInfo.getId()); |
| | | mallExpressInfo.setExpressNo(pdfOrder.getMailNo()); |
| | | mallExpressInfo.setExpressCom("Best Logistic"); |
| | | mallExpressInfo.setExpressCode("Best Logistic"); |
| | | mallExpressInfoMapper.insert(mallExpressInfo); |
| | | |
| | | return new FebsResponse().success().data(pdfOrder); |
| | | } |
| | | |
| | | @Override |
| | | public FebsResponse cancelDeliver(Long id) { |
| | | |
| | | MallOrderInfo mallOrderInfo = ValidateEntityUtils.ensureColumnReturnEntity(id, MallOrderInfo::getId, mallOrderInfoMapper::selectOne, "订单不存在"); |
| | | ValidateEntityUtils.ensureEqual(mallOrderInfo.getStatus(), OrderStatusEnum.WAIT_FINISH.getValue(), "订单不是待收货状态"); |
| | | |
| | | KdCancelOrderNotifyReq kdCancelOrderNotifyReq = new KdCancelOrderNotifyReq(); |
| | | kdCancelOrderNotifyReq.setTxLogisticId(mallOrderInfo.getOrderNo()); |
| | | kdCancelOrderNotifyReq.setReason("Don't want to buy"); |
| | | KdCancelOrderNotifyRsp kdCancelOrderNotifyRsp = ClientParamService.getInstance(ClientParamEnum.TEST.name()).cancelOrder(kdCancelOrderNotifyReq); |
| | | ValidateEntityUtils.ensureEqual(kdCancelOrderNotifyRsp.getResult(), true, "取消发货失败"); |
| | | |
| | | //更新发货状态 |
| | | mallOrderInfoMapper.updateOrderStateAndDeliveryState( |
| | | mallOrderInfo.getId(), |
| | | OrderStatusEnum.WAIT_SHIPPING.getValue(), |
| | | OrderDeliveryStateEnum.DELIVERY_WAIT.getValue()); |
| | | |
| | | List<MallExpressInfo> mallExpressInfoList = ValidateEntityUtils.ensureColumnReturnEntityList(mallOrderInfo.getId(), MallExpressInfo::getOrderId, mallExpressInfoMapper::selectList, "未查询到物流信息"); |
| | | mallExpressInfoList.forEach(mallExpressInfo -> mallExpressInfoMapper.deleteById(mallExpressInfo.getId())); |
| | | return new FebsResponse().success().message("取消发货成功,请重新发货"); |
| | | } |
| | | |
| | | @Override |
| | | public void deliverGoodsByOrderNo(DeliverGoodsDto deliverGoodsDto) { |
| | | MallOrderInfo mallOrderInfo = mallOrderInfoMapper.selectByOrderNo(deliverGoodsDto.getOrderNo()); |
| | | if (mallOrderInfo == null) { |
| | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.best.javaSdk.Client; |
| | | import com.best.javaSdk.kdTraceQuery.request.KdTraceQueryReq; |
| | | import com.best.javaSdk.kdTraceQuery.request.MailNos; |
| | | import com.best.javaSdk.ClientParamEnum; |
| | | import com.best.javaSdk.ClientParamService; |
| | | import com.best.javaSdk.kdTraceQuery.response.KdTraceQueryRsp; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | |
| | | dataDictionaryCustomMapper.insert(dic); |
| | | } |
| | | |
| | | // @Override |
| | | // public KdTraceQueryRsp checkTraceInfo(ApiCheckTraceInfoDto checkTraceInfoDto) { |
| | | // /** |
| | | // * 正式环境 |
| | | // * MY_LEADING 生产环境参数 |
| | | // * partnerID : MY_LEADING |
| | | // * partnerKey : ER5DFRT320D4ed6FAFs3G410Fs977 |
| | | // * Endpoint :http://sgp-seaedi.800best.com/Malaysia/kdapi/api/process |
| | | // */ |
| | | // String url = "http://sgp-seaedi.800best.com/Malaysia/kdapi/api/process"; |
| | | // String partnerID = "MY_LEADING"; |
| | | // String partnerKey = "ER5DFRT320D4ed6FAFs3G410Fs977"; |
| | | // String format = "JSON"; |
| | | // /** |
| | | // * 测试环境 |
| | | // * 测试物流编号 66660451238000 |
| | | // */ |
| | | //// String url = "http://sea-edi-hxtest.800best.com/Malaysia/kdapi/api/process"; |
| | | //// String partnerID = "M_TEST"; |
| | | //// String partnerKey = "TEST12345"; |
| | | //// String format = "JSON"; |
| | | // |
| | | // Client client = new Client(url, partnerID, partnerKey, format); |
| | | // |
| | | // KdTraceQueryReq tdTraceQueryReq = new KdTraceQueryReq(); |
| | | // MailNos mailNos = new MailNos(); |
| | | // List<String> mailNo = new ArrayList<>(); |
| | | // mailNo.add(checkTraceInfoDto.getTraceNo()); |
| | | // mailNos.setMailNo(mailNo); |
| | | // tdTraceQueryReq.setMailNos(mailNos); |
| | | // tdTraceQueryReq.setLangType("zh-CN"); |
| | | // |
| | | // KdTraceQueryRsp kdTraceQueryRsp = client.executed(tdTraceQueryReq); |
| | | // return kdTraceQueryRsp; |
| | | // } |
| | | @Override |
| | | public KdTraceQueryRsp checkTraceInfo(ApiCheckTraceInfoDto checkTraceInfoDto) { |
| | | /** |
| | | * 正式环境 |
| | | * MY_LEADING 生产环境参数 |
| | | * partnerID : MY_LEADING |
| | | * partnerKey : ER5DFRT320D4ed6FAFs3G410Fs977 |
| | | * Endpoint :http://sgp-seaedi.800best.com/Malaysia/kdapi/api/proces |
| | | */ |
| | | String url = "http://sgp-seaedi.800best.com/Malaysia/kdapi/api/process"; |
| | | String partnerID = "MY_LEADING"; |
| | | String partnerKey = "ER5DFRT320D4ed6FAFs3G410Fs977"; |
| | | String format = "JSON"; |
| | | /** |
| | | * 测试环境 |
| | | * 测试物流编号 66660451238000 |
| | | */ |
| | | // String url = "http://sea-edi-hxtest.800best.com/Malaysia/kdapi/api/process"; |
| | | // String partnerID = "M_TEST"; |
| | | // String partnerKey = "TEST12345"; |
| | | // String format = "JSON"; |
| | | |
| | | Client client = new Client(url, partnerID, partnerKey, format); |
| | | |
| | | KdTraceQueryReq tdTraceQueryReq = new KdTraceQueryReq(); |
| | | MailNos mailNos = new MailNos(); |
| | | List<String> mailNo = new ArrayList<>(); |
| | | mailNo.add(checkTraceInfoDto.getTraceNo()); |
| | | mailNos.setMailNo(mailNo); |
| | | tdTraceQueryReq.setMailNos(mailNos); |
| | | tdTraceQueryReq.setLangType("zh-CN"); |
| | | |
| | | KdTraceQueryRsp kdTraceQueryRsp = client.executed(tdTraceQueryReq); |
| | | return kdTraceQueryRsp; |
| | | return ClientParamService.getInstance(ClientParamEnum.PRD.name()).checkTraceInfo(checkTraceInfoDto); |
| | | } |
| | | } |
New file |
| | |
| | | package com.best.javaSdk; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | @Getter |
| | | public enum ClientParamEnum { |
| | | |
| | | TEST( |
| | | "http://sea-edi-hxtest.800best.com/Malaysia/kdapi/api/process", |
| | | "M_TEST", |
| | | "TEST12345", |
| | | "JSON" |
| | | ), |
| | | |
| | | PRD( |
| | | "http://sgp-seaedi.800best.com/Malaysia/kdapi/api/process", |
| | | "MY_LEADING", |
| | | "ER5DFRT320D4ed6FAFs3G410Fs977", |
| | | "JSON" |
| | | ); |
| | | |
| | | private String url; |
| | | private String partnerID; |
| | | private String partnerKey; |
| | | private String messageFormat; |
| | | |
| | | ClientParamEnum(String url, String partnerID, String partnerKey, String messageFormat) { |
| | | this.url = url; |
| | | this.partnerID = partnerID; |
| | | this.partnerKey = partnerKey; |
| | | this.messageFormat = messageFormat; |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package com.best.javaSdk; |
| | | |
| | | import cc.mrbird.febs.common.exception.FebsException; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | @Slf4j |
| | | public class ClientParamService { |
| | | |
| | | private final static Map<String, ClientServiceImpl> client_map = new HashMap<>(); |
| | | |
| | | static { |
| | | for (ClientParamEnum clientParamEnum : ClientParamEnum.values()) { |
| | | client_map.put( |
| | | clientParamEnum.name(), |
| | | new ClientServiceImpl( |
| | | clientParamEnum.getUrl(), |
| | | clientParamEnum.getPartnerID(), |
| | | clientParamEnum.getPartnerKey(), |
| | | clientParamEnum.getMessageFormat())); |
| | | } |
| | | } |
| | | |
| | | private ClientParamService() { |
| | | } |
| | | |
| | | public final static ClientParamService INSTANCE = new ClientParamService(); |
| | | |
| | | public static ClientService getInstance(String type) { |
| | | ClientServiceImpl clientService = client_map.get(type); |
| | | if (clientService == null) { |
| | | throw new FebsException("参数错误"); |
| | | } |
| | | |
| | | return clientService; |
| | | } |
| | | } |
New file |
| | |
| | | package com.best.javaSdk; |
| | | |
| | | import cc.mrbird.febs.mall.dto.ApiCheckTraceInfoDto; |
| | | import com.best.javaSdk.kdCancelOrderNotify.request.KdCancelOrderNotifyReq; |
| | | import com.best.javaSdk.kdCancelOrderNotify.response.KdCancelOrderNotifyRsp; |
| | | import com.best.javaSdk.kdCreateWaybillOrderPdfNotify.request.KdCreateWaybillOrderPdfNotifyReq; |
| | | import com.best.javaSdk.kdCreateWaybillOrderPdfNotify.response.KdCreateWaybillOrderPdfNotifyRsp; |
| | | import com.best.javaSdk.kdTraceQuery.response.KdTraceQueryRsp; |
| | | |
| | | public interface ClientService { |
| | | |
| | | KdTraceQueryRsp checkTraceInfo(ApiCheckTraceInfoDto checkTraceInfoDto); |
| | | |
| | | KdCreateWaybillOrderPdfNotifyRsp createPdfOrder(KdCreateWaybillOrderPdfNotifyReq kdCreateWaybillOrderPdfNotifyReq); |
| | | |
| | | KdCancelOrderNotifyRsp cancelOrder(KdCancelOrderNotifyReq kdCancelOrderNotifyReq); |
| | | } |
New file |
| | |
| | | package com.best.javaSdk; |
| | | |
| | | import cc.mrbird.febs.mall.dto.ApiCheckTraceInfoDto; |
| | | import cn.hutool.json.JSONUtil; |
| | | import com.best.javaSdk.kdCancelOrderNotify.request.KdCancelOrderNotifyReq; |
| | | import com.best.javaSdk.kdCancelOrderNotify.response.KdCancelOrderNotifyRsp; |
| | | import com.best.javaSdk.kdCreateWaybillOrderPdfNotify.request.KdCreateWaybillOrderPdfNotifyReq; |
| | | import com.best.javaSdk.kdCreateWaybillOrderPdfNotify.response.KdCreateWaybillOrderPdfNotifyRsp; |
| | | import com.best.javaSdk.kdTraceQuery.request.KdTraceQueryReq; |
| | | import com.best.javaSdk.kdTraceQuery.request.MailNos; |
| | | import com.best.javaSdk.kdTraceQuery.response.KdTraceQueryRsp; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | @Slf4j |
| | | public class ClientServiceImpl implements ClientService{ |
| | | |
| | | private Client client; |
| | | private String url; |
| | | private String partnerID; |
| | | private String partnerKey; |
| | | private String messageFormat; |
| | | |
| | | public ClientServiceImpl(String url, String partnerID, String partnerKey, String messageFormat) { |
| | | this.url = url; |
| | | this.partnerID = partnerID; |
| | | this.partnerKey = partnerKey; |
| | | this.messageFormat = messageFormat; |
| | | |
| | | client = new Client(url, partnerID, partnerKey, messageFormat); |
| | | } |
| | | |
| | | @Override |
| | | public KdTraceQueryRsp checkTraceInfo(ApiCheckTraceInfoDto checkTraceInfoDto) { |
| | | KdTraceQueryReq tdTraceQueryReq = new KdTraceQueryReq(); |
| | | MailNos mailNos = new MailNos(); |
| | | List<String> mailNo = new ArrayList<>(); |
| | | mailNo.add(checkTraceInfoDto.getTraceNo()); |
| | | mailNos.setMailNo(mailNo); |
| | | tdTraceQueryReq.setMailNos(mailNos); |
| | | tdTraceQueryReq.setLangType("zh-CN"); |
| | | KdTraceQueryRsp kdTraceQueryRsp = client.executed(tdTraceQueryReq); |
| | | return kdTraceQueryRsp; |
| | | } |
| | | |
| | | @Override |
| | | public KdCreateWaybillOrderPdfNotifyRsp createPdfOrder(KdCreateWaybillOrderPdfNotifyReq kdCreateWaybillOrderPdfNotifyReq) { |
| | | KdCreateWaybillOrderPdfNotifyRsp executed = client.executed(kdCreateWaybillOrderPdfNotifyReq); |
| | | log.info("executed:{}", JSONUtil.parseObj(executed)); |
| | | return executed; |
| | | } |
| | | |
| | | @Override |
| | | public KdCancelOrderNotifyRsp cancelOrder(KdCancelOrderNotifyReq kdCancelOrderNotifyReq) { |
| | | KdCancelOrderNotifyRsp executed = client.executed(kdCancelOrderNotifyReq); |
| | | log.info("executed:{}", JSONUtil.parseObj(executed)); |
| | | return executed; |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | cancelOrder(data.id); |
| | | }); |
| | | } |
| | | if (layEvent === 'deliverPdfGoods') { |
| | | febs.modal.confirm('一键发货', '确认一键发货?', function () { |
| | | deliverPdfGoods(data.id); |
| | | }); |
| | | } |
| | | if (layEvent === 'cancelDeliver') { |
| | | febs.modal.confirm('取消发货', '确认取消发货?', function () { |
| | | cancelDeliver(data.id); |
| | | }); |
| | | } |
| | | if (layEvent === 'seePayImage') { |
| | | var t = $view.find('#seePayImage'+data.id+''); |
| | | //页面层 |
| | |
| | | } |
| | | }); |
| | | |
| | | function cancelDeliver(id) { |
| | | febs.get(ctx + 'admin/order/cancelDeliver/' + id, null, function (data) { |
| | | febs.alert.success(data.message); |
| | | $query.click(); |
| | | }); |
| | | } |
| | | |
| | | function cancelOrder(id) { |
| | | febs.get(ctx + 'admin/order/cancelOrder/' + id, null, function () { |
| | | febs.alert.success('操作成功'); |
| | | $query.click(); |
| | | }); |
| | | } |
| | | |
| | | function deliverPdfGoods(id) { |
| | | febs.get(ctx + 'admin/order/deliverPdfGoods/' + id, null, function (e) { |
| | | if (e.code == 200) { |
| | | // 创建弹层容器 |
| | | const content = ` |
| | | <div style="position:relative;min-height:500px"> |
| | | <div id="pdfContainer" style="margin:20px auto;max-width:800px"></div> |
| | | <div class="layui-form" style="text-align:center;margin-top:20px"> |
| | | <button class="layui-btn layui-btn-normal" onclick="printPdf()">打印</button> |
| | | </div> |
| | | </div> |
| | | `; |
| | | |
| | | // 显示弹层 |
| | | const index = layer.open({ |
| | | type: 1, |
| | | title: "电子面单", |
| | | content: content, |
| | | area: ['90%', '90%'], |
| | | success: function() { |
| | | // 渲染PDF |
| | | renderPdf(e.data.pdfStream); |
| | | } |
| | | }); |
| | | // 存储当前PDF数据 |
| | | window.currentPDF = e.data.pdfStream; |
| | | } else { |
| | | febs.alert.warn(e.message); |
| | | } |
| | | }); |
| | | $query.click(); |
| | | } |
| | | |
| | | // PDF渲染函数 |
| | | function renderPdf(base64Data) { |
| | | // 清理容器 |
| | | const container = document.getElementById('pdfContainer'); |
| | | container.innerHTML = ''; |
| | | |
| | | // Base64转Blob |
| | | const byteCharacters = atob(base64Data); |
| | | const byteNumbers = new Array(byteCharacters.length); |
| | | for (let i = 0; i < byteCharacters.length; i++) { |
| | | byteNumbers[i] = byteCharacters.charCodeAt(i); |
| | | } |
| | | const byteArray = new Uint8Array(byteNumbers); |
| | | const blob = new Blob([byteArray], {type: 'application/pdf'}); |
| | | |
| | | // 生成临时URL |
| | | const url = URL.createObjectURL(blob); |
| | | |
| | | // 使用PDF.js渲染 |
| | | pdfjsLib.getDocument(url).promise.then(pdf => { |
| | | // 循环渲染所有页面 |
| | | for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) { |
| | | pdf.getPage(pageNum).then(page => { |
| | | const scale = 1.5; |
| | | const viewport = page.getViewport({scale: scale}); |
| | | const canvas = document.createElement('canvas'); |
| | | const context = canvas.getContext('2d'); |
| | | canvas.height = viewport.height; |
| | | canvas.width = viewport.width; |
| | | |
| | | // 创建页面容器 |
| | | const pageDiv = document.createElement('div'); |
| | | pageDiv.className = 'pdf-page'; |
| | | pageDiv.style.margin = '10px 0'; |
| | | container.appendChild(pageDiv); |
| | | |
| | | // 渲染到Canvas |
| | | page.render({ |
| | | canvasContext: context, |
| | | viewport: viewport |
| | | }).promise.then(() => { |
| | | pageDiv.appendChild(canvas); |
| | | }); |
| | | }); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // 打印函数 |
| | | window.printPdf = function() { |
| | | let base64Image = window.currentPDF; |
| | | const printWindow = window.open('', '_blank'); |
| | | printWindow.document.write(` |
| | | <html> |
| | | <head> |
| | | <title>电子面单打印</title> |
| | | <style> |
| | | body { margin: 0; padding: 20px } |
| | | canvas { |
| | | width: 100%!important; |
| | | height: auto!important; |
| | | page-break-after: always; |
| | | } |
| | | </style> |
| | | </head> |
| | | <body> |
| | | <iframe src="data:application/pdf;base64,`+base64Image+`" width="100%" height="100%"></iframe> |
| | | </body> |
| | | </html> |
| | | `); |
| | | printWindow.document.close(); |
| | | printWindow.onload = function() { |
| | | printWindow.print(); |
| | | printWindow.close(); |
| | | } |
| | | } |
| | | |
| | | // function deliverPdfGoods(id) { |
| | | // febs.get(ctx + 'admin/order/deliverPdfGoods/' + id, null, function (e) { |
| | | // if(e.code == 200){ |
| | | // console.info(e.data); |
| | | // console.info(e.data.pdfStream); |
| | | // console.info(e.data.result); |
| | | // layer.open({ |
| | | // type: 1, |
| | | // title: "电子面单", |
| | | // skin: 'layui-layer-rim', //加上边框 |
| | | // area: ['100%', '100%'], //宽高 |
| | | // shadeClose: true, //开启遮罩关闭 |
| | | // end: function (index, layero) { |
| | | // return false; |
| | | // }, |
| | | // content: '<div class="layui-card-body" id="pdfViewer"></div>' |
| | | // }); |
| | | // if(e.data.pdfStream === null){ |
| | | // |
| | | // renderPdf(e.data.pdfStreamList); |
| | | // } |
| | | // renderPdf(e.data.pdfStream); |
| | | // }else{ |
| | | // febs.alert.warn(e.message); |
| | | // } |
| | | // $query.click(); |
| | | // }); |
| | | // } |
| | | |
| | | // 渲染 PDF 到页面 |
| | | // function renderPdf(base64Data) { |
| | | // // Base64 转 Blob |
| | | // const byteCharacters = atob(base64Data); |
| | | // const byteNumbers = new Array(byteCharacters.length); |
| | | // for (let i = 0; i < byteCharacters.length; i++) { |
| | | // byteNumbers[i] = byteCharacters.charCodeAt(i); |
| | | // } |
| | | // const byteArray = new Uint8Array(byteNumbers); |
| | | // const blob = new Blob([byteArray], {type: 'application/pdf'}); |
| | | // |
| | | // // 生成临时 URL |
| | | // const url = URL.createObjectURL(blob); |
| | | // |
| | | // // 使用 PDF.js 渲染 |
| | | // pdfjsLib.getDocument(url).promise.then(function(pdf) { |
| | | // pdf.getPage(1).then(function(page) { |
| | | // const scale = 1.5; |
| | | // const viewport = page.getViewport({scale: scale}); |
| | | // const canvas = document.createElement('canvas'); |
| | | // const context = canvas.getContext('2d'); |
| | | // canvas.height = viewport.height; |
| | | // canvas.width = viewport.width; |
| | | // |
| | | // // 将 PDF 页面渲染到 Canvas |
| | | // page.render({ |
| | | // canvasContext: context, |
| | | // viewport: viewport |
| | | // }).promise.then(function() { |
| | | // $('#pdfViewer').html(canvas); |
| | | // }); |
| | | // }); |
| | | // }).catch(function(error) { |
| | | // layer.msg('PDF渲染失败: ' + error.message, {icon: 2}); |
| | | // }); |
| | | // } |
| | | |
| | | // 查询按钮 |
| | | $query.on('click', function () { |
| | |
| | | {title: '操作', |
| | | templet: function (d) { |
| | | if(d.status === 2){ |
| | | return '<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="seeOrder" shiro:hasPermission="user:update">详情</button>' |
| | | +'<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="deliverGoods" shiro:hasPermission="user:update">发货</button>' |
| | | return '<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="deliverPdfGoods" shiro:hasPermission="user:update">一键发货</button>' |
| | | +'<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="seeOrder" shiro:hasPermission="user:update">详情</button>' |
| | | // +'<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="deliverGoods" shiro:hasPermission="user:update">发货</button>' |
| | | }else if(d.status === 3){ |
| | | return '<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="seeOrder" shiro:hasPermission="user:update">详情</button>' |
| | | +'<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="updateDeliver" shiro:hasPermission="user:update">修改物流信息</button>' |
| | | +'<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="cancelDeliver" shiro:hasPermission="user:update">取消发货</button>' |
| | | // +'<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="updateDeliver" shiro:hasPermission="user:update">修改物流信息</button>' |
| | | }else{ |
| | | return '<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="seeOrder" shiro:hasPermission="user:update">详情</button>' |
| | | } |
New file |
| | |
| | | <div class="layui-fluid layui-anim febs-anim" id="sender-setting" lay-title="发货地址"> |
| | | <div class="layui-row layui-col-space8 febs-container"> |
| | | <form class="layui-form" action="" lay-filter="sender-setting-form"> |
| | | <div class="layui-card"> |
| | | <div class="layui-card-body flex" id="cardBodySender"> |
| | | |
| | | </div> |
| | | |
| | | <div class="layui-card-footer"> |
| | | <button class="layui-btn layui-btn-normal save-btn" lay-submit="" lay-filter="sender-setting-form-submit" id="submit">保存</button> |
| | | </div> |
| | | </div> |
| | | </form> |
| | | </div> |
| | | </div> |
| | | <style> |
| | | .layui-form-label { |
| | | width: 120px; |
| | | } |
| | | |
| | | .layui-form-item .layui-input-block { |
| | | margin-left: 150px; |
| | | } |
| | | |
| | | .layui-table-form .layui-form-item { |
| | | margin-bottom: 20px !important; |
| | | } |
| | | .flex{ |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | } |
| | | .save-btn{ |
| | | display: block; |
| | | margin: 0 auto; |
| | | } |
| | | .layui-upload-list{ |
| | | height: 200px; |
| | | } |
| | | </style> |
| | | <script type="text/html" id="senderOperate"> |
| | | {{# layui.each(d, function(index, item){ }} |
| | | <div class="layui-form-item"> |
| | | <label class="layui-form-label febs-form-item-require">{{item.description}}:</label> |
| | | <input type="text" lay-verify="required" name="{{item.code}}" |
| | | autoComplete="off" value="{{item.value}}" class="layui-input"> |
| | | </div> |
| | | {{# }) }} |
| | | </script> |
| | | <script data-th-inline="javascript" type="text/javascript"> |
| | | layui.use(['dropdown', 'jquery', 'validate', 'febs', 'form', 'eleTree', 'laytpl', 'upload'], function () { |
| | | var $ = layui.jquery, |
| | | febs = layui.febs, |
| | | form = layui.form, |
| | | validate = layui.validate |
| | | , templateHtml = senderOperate.innerHTML |
| | | , $cardBodySender = $("#cardBodySender") |
| | | , laytpl = layui.laytpl |
| | | , upload = layui.upload |
| | | , $view = $('#sender-setting'); |
| | | |
| | | form.verify(validate); |
| | | form.render(); |
| | | |
| | | dicDataReq("SENDER_SET"); |
| | | function dicDataReq(type) { |
| | | $cardBodySender.empty(); |
| | | $.get(ctx + 'admin/common/findDicByType/' + type, function (r) { |
| | | if (r.code === 200) { |
| | | var data = r.data; |
| | | laytpl(templateHtml).render(data, function(html) { |
| | | $cardBodySender.append(html); |
| | | }) |
| | | } |
| | | }); |
| | | } |
| | | |
| | | form.on('submit(sender-setting-form-submit)', function (data) { |
| | | $.ajax({ |
| | | 'url':ctx + 'admin/system/bonusSystemSetting', |
| | | 'type':'post', |
| | | 'dataType':'json', |
| | | 'headers' : {'Content-Type' : 'application/json;charset=utf-8'}, |
| | | 'traditional': true, |
| | | 'data':JSON.stringify(data.field), |
| | | 'success':function (data) { |
| | | if (data.code == 200) { |
| | | febs.alert.success(data.message); |
| | | } |
| | | }, |
| | | 'error':function () { |
| | | febs.alert.warn('服务器繁忙'); |
| | | } |
| | | }) |
| | | return false; |
| | | }); |
| | | }); |
| | | </script> |
| | |
| | | <!-- <script src="https://unpkg.com/@wangeditor/editor@latest/dist/index.js"></script>--> |
| | | <script src="http://gosspublic.alicdn.com/aliyun-oss-sdk-6.17.0.min.js"></script> |
| | | <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/wangeditor@latest/dist/wangEditor.min.js" ></script> |
| | | |
| | | <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.6.347/pdf.min.js"></script> |
| | | <style type="text/css"> |
| | | ::-webkit-scrollbar { |
| | | height: 20px !important; |
| | |
| | | package cc.mrbird.febs; |
| | | |
| | | import cc.mrbird.febs.common.enumerates.FlowTypeEnum; |
| | | import cc.mrbird.febs.common.enumerates.MoneyFlowTypeEnum; |
| | | import cc.mrbird.febs.mall.entity.*; |
| | | import cc.mrbird.febs.mall.mapper.*; |
| | | import cc.mrbird.febs.mall.service.IMallMoneyFlowService; |
| | | import cc.mrbird.febs.pay.model.RefundStatus; |
| | | import cc.mrbird.febs.pay.util.FiuuRefundUtil; |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.date.DateUtil; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import cc.mrbird.febs.mall.dto.ApiCheckTraceInfoDto; |
| | | import cn.hutool.json.JSONUtil; |
| | | import com.best.javaSdk.ClientParamEnum; |
| | | import com.best.javaSdk.ClientParamService; |
| | | import com.best.javaSdk.kdTraceQuery.response.KdTraceQueryRsp; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.boot.test.context.SpringBootTest; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Slf4j |
| | | @SpringBootTest |
| | | public class AgentTest { |
| | | |
| | | @Autowired |
| | | private MallActivityMapper mallActivityMapper; |
| | | |
| | | @Autowired |
| | | private MallRefundMapper mallRefundMapper; |
| | | |
| | | @Autowired |
| | | private MallOrderInfoMapper mallOrderInfoMapper; |
| | | |
| | | @Autowired |
| | | private MallOrderItemMapper mallOrderItemMapper; |
| | | |
| | | @Autowired |
| | | private MallGoodsSkuMapper mallGoodsSkuMapper; |
| | | |
| | | @Autowired |
| | | private MallGoodsMapper mallGoodsMapper; |
| | | |
| | | @Autowired |
| | | private FiuuRefundUtil fiuuRefundUtil; |
| | | |
| | | @Autowired |
| | | private IMallMoneyFlowService mallMoneyFlowService; |
| | | @Test |
| | | public void refundJob() { |
| | | LambdaQueryWrapper<MallRefundEntity> mallOrderRefundLambdaQueryWrapper = new LambdaQueryWrapper<>(); |
| | | mallOrderRefundLambdaQueryWrapper.eq(MallRefundEntity::getState, 3); |
| | | mallOrderRefundLambdaQueryWrapper.eq(MallRefundEntity::getType, 1); |
| | | List<MallRefundEntity> mallRefundEntities = mallRefundMapper.selectList(mallOrderRefundLambdaQueryWrapper); |
| | | if(CollUtil.isEmpty(mallRefundEntities)){ |
| | | return; |
| | | } |
| | | mallRefundEntities.forEach(mallRefundEntity -> { |
| | | processRefund(mallRefundEntity); |
| | | }); |
| | | ApiCheckTraceInfoDto apiCheckTraceInfoDto = new ApiCheckTraceInfoDto(); |
| | | apiCheckTraceInfoDto.setTraceNo("60850712015414"); |
| | | KdTraceQueryRsp traceQueryRsp = ClientParamService.getInstance(ClientParamEnum.PRD.name()).checkTraceInfo(apiCheckTraceInfoDto); |
| | | log.info("traceQueryRsp:{}", JSONUtil.parseObj(traceQueryRsp)); |
| | | } |
| | | |
| | | private void processRefund(MallRefundEntity mallRefundEntity) { |
| | | try { |
| | | MallOrderInfo mallOrderInfo = mallOrderInfoMapper.selectById(mallRefundEntity.getOrderId()); |
| | | MallOrderItem mallOrderItem = mallOrderItemMapper.selectById(mallRefundEntity.getItemId()); |
| | | MallGoodsSku mallGoodsSku = mallGoodsSkuMapper.selectById(mallOrderItem.getSkuId()); |
| | | |
| | | String txnId = mallOrderInfo.getPayOrderNo(); |
| | | RefundStatus status = fiuuRefundUtil.pollRefundStatus(txnId); |
| | | |
| | | switch (status.getStatus()) { |
| | | case "success": |
| | | updateOnSuccess(mallRefundEntity, mallOrderInfo, mallOrderItem, mallGoodsSku); |
| | | break; |
| | | case "rejected": |
| | | updateOnRejected(mallRefundEntity, mallOrderItem); |
| | | break; |
| | | default: |
| | | log.warn("未知状态:{}", status.getStatus()); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("处理退款失败: {}", e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | private void updateOnSuccess(MallRefundEntity mallRefundEntity, MallOrderInfo mallOrderInfo, MallOrderItem mallOrderItem, MallGoodsSku mallGoodsSku) { |
| | | mallOrderItem.setState(3); |
| | | mallOrderItemMapper.updateById(mallOrderItem); |
| | | |
| | | MallGoods mallGoods = mallGoodsMapper.selectById(mallOrderItem.getGoodsId()); |
| | | mallGoods.setStock(mallGoods.getStock() + mallOrderItem.getCnt()); |
| | | mallGoods.setVolume(mallGoods.getVolume() - mallOrderItem.getCnt()); |
| | | mallGoodsMapper.updateById(mallGoods); |
| | | |
| | | mallGoodsSku.setStock(mallGoodsSku.getStock() + mallOrderItem.getCnt()); |
| | | mallGoodsSku.setSkuVolume(mallGoodsSku.getSkuVolume() - mallOrderItem.getCnt()); |
| | | mallGoodsSkuMapper.updateById(mallGoodsSku); |
| | | |
| | | mallRefundEntity.setState(1); |
| | | mallRefundEntity.setUpdatedTime(DateUtil.date()); |
| | | mallRefundMapper.updateById(mallRefundEntity); |
| | | |
| | | mallMoneyFlowService.addMoneyFlow( |
| | | mallOrderInfo.getMemberId(), |
| | | mallRefundEntity.getAmount(), |
| | | MoneyFlowTypeEnum.WECHAT_REFUND.getValue(), |
| | | mallOrderInfo.getOrderNo(), |
| | | FlowTypeEnum.WECHAT.getValue(), |
| | | "FIUU退款", |
| | | 2); |
| | | |
| | | List<MallOrderItem> mallOrderItemList = mallOrderItemMapper.selectListByNotInStateAndOrderId(3, mallRefundEntity.getOrderId()); |
| | | if (CollUtil.isEmpty(mallOrderItemList)) { |
| | | MallOrderInfo mallOrderRefund = mallOrderInfoMapper.selectById(mallRefundEntity.getOrderId()); |
| | | mallOrderRefund.setStatus(6); |
| | | mallOrderInfoMapper.updateById(mallOrderRefund); |
| | | } |
| | | } |
| | | |
| | | private void updateOnRejected(MallRefundEntity mallRefundEntity, MallOrderItem mallOrderItem) { |
| | | mallOrderItem.setState(1); |
| | | mallOrderItemMapper.updateById(mallOrderItem); |
| | | |
| | | mallRefundEntity.setState(2); |
| | | mallRefundMapper.updateById(mallRefundEntity); |
| | | } |
| | | |
| | | } |