Browse Source

trade:前端订单管理,联调接口

YunaiV 2 years ago
parent
commit
fc48ab4928

+ 3 - 3
yudao-framework/yudao-spring-boot-starter-biz-ip/src/test/java/cn/iocoder/yudao/framework/ip/core/utils/AreaUtilsTest.java

@@ -28,9 +28,9 @@ public class AreaUtilsTest {
 
     @Test
     public void testFormat() {
-        assertEquals(AreaUtils.format(110105L), "北京 北京市 朝阳区");
-        assertEquals(AreaUtils.format(1L), "中国");
-        assertEquals(AreaUtils.format(2L), "蒙古");
+        assertEquals(AreaUtils.format(110105), "北京 北京市 朝阳区");
+        assertEquals(AreaUtils.format(1), "中国");
+        assertEquals(AreaUtils.format(2), "蒙古");
     }
 
 }

+ 36 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderPageReqVO.java

@@ -2,17 +2,53 @@ package cn.iocoder.yudao.module.trade.controller.admin.order.vo;
 
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.framework.common.validation.Mobile;
 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
+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;
 
 @ApiModel("管理后台 - 交易订单的分页 Request VO")
 @Data
 public class TradeOrderPageReqVO extends PageParam {
 
+    @ApiModelProperty(value = "订单号", example = "88888888", notes = "模糊匹配")
+    private String no;
+
+    @ApiModelProperty(value = "用户编号", example = "1024")
+    private Long userId;
+
+    @ApiModelProperty(value = "用户昵称", example = "小王", notes = "模糊匹配")
+    private String userNickname;
+
+    @ApiModelProperty(value = "用户手机号", example = "小王", notes = "精准匹配")
+    @Mobile
+    private String userMobile;
+
+    @ApiModelProperty(value = "收件人名称", example = "小红", notes = "模糊匹配")
+    private String receiverName;
+
+    @ApiModelProperty(value = "收件人手机", example = "1560", notes = "模糊匹配")
+    @Mobile
+    private String receiverMobile;
+
+    @ApiModelProperty(value = "订单类型", example = "1", notes = "参见 TradeOrderTypeEnum 枚举")
+    private Integer type;
+
     @ApiModelProperty(value = "订单状态", example = "1", notes = "参见 TradeOrderStatusEnum 枚举")
     @InEnum(value = TradeOrderStatusEnum.class, message = "订单状态必须是 {value}")
     private Integer status;
 
+    @ApiModelProperty(value = "支付渠道", example = "wx_lite")
+    private String payChannelCode;
+
+    @ApiModelProperty(value = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
 }

+ 12 - 2
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java

@@ -8,6 +8,8 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.Set;
+
 @Mapper
 public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
 
@@ -20,9 +22,17 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
         return selectOne(TradeOrderDO::getId, id, TradeOrderDO::getUserId, userId);
     }
 
-    default PageResult<TradeOrderDO> selectPage(TradeOrderPageReqVO reqVO) {
+    default PageResult<TradeOrderDO> selectPage(TradeOrderPageReqVO reqVO, Set<Long> userIds) {
         return selectPage(reqVO, new LambdaQueryWrapperX<TradeOrderDO>()
-                .eqIfPresent(TradeOrderDO::getStatus, reqVO.getStatus()));
+                .likeIfPresent(TradeOrderDO::getNo, reqVO.getNo())
+                .eqIfPresent(TradeOrderDO::getUserId, reqVO.getUserId())
+                .inIfPresent(TradeOrderDO::getUserId, userIds)
+                .likeIfPresent(TradeOrderDO::getReceiverName, reqVO.getReceiverName())
+                .likeIfPresent(TradeOrderDO::getReceiverMobile, reqVO.getReceiverMobile())
+                .eqIfPresent(TradeOrderDO::getType, reqVO.getType())
+                .eqIfPresent(TradeOrderDO::getStatus, reqVO.getStatus())
+                .eqIfPresent(TradeOrderDO::getPayChannelCode, reqVO.getPayChannelCode())
+                .betweenIfPresent(TradeOrderDO::getCreateTime, reqVO.getCreateTime()));
     }
 
 }

+ 23 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.trade.service.order;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
@@ -11,6 +12,8 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.module.member.api.address.AddressApi;
 import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
+import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
+import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
 import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
@@ -77,6 +80,8 @@ public class TradeOrderServiceImpl implements TradeOrderService {
     private AddressApi addressApi;
     @Resource
     private CouponApi couponApi;
+    @Resource
+    private MemberUserApi memberUserApi;
 
     @Resource
     private TradeOrderProperties tradeOrderProperties;
@@ -420,7 +425,24 @@ public class TradeOrderServiceImpl implements TradeOrderService {
 
     @Override
     public PageResult<TradeOrderDO> getOrderPage(TradeOrderPageReqVO reqVO) {
-        return tradeOrderMapper.selectPage(reqVO);
+        // 获得 userId 相关的查询
+        Set<Long> userIds = new HashSet<>();
+        if (StrUtil.isNotEmpty(reqVO.getUserMobile())) {
+            MemberUserRespDTO user = memberUserApi.getUserByMobile(reqVO.getUserMobile());
+            if (user == null) { // 没查询到用户,说明肯定也没他的订单
+                return new PageResult<>();
+            }
+            userIds.add(user.getId());
+        }
+        if (StrUtil.isNotEmpty(reqVO.getUserNickname())) {
+            List<MemberUserRespDTO> users = memberUserApi.getUserListByNickname(reqVO.getUserNickname());
+            if (CollUtil.isEmpty(users)) { // 没查询到用户,说明肯定也没他的订单
+                return new PageResult<>();
+            }
+            userIds.addAll(convertSet(users, MemberUserRespDTO::getId));
+        }
+        // 分页查询
+        return tradeOrderMapper.selectPage(reqVO, userIds);
     }
 
     // =================== Order Item ===================

+ 8 - 0
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApi.java

@@ -49,4 +49,12 @@ public interface MemberUserApi {
      */
     List<MemberUserRespDTO> getUserListByNickname(String nickname);
 
+    /**
+     * 基于手机号,精准匹配用户
+     *
+     * @param mobile 手机号
+     * @return 用户信息
+     */
+    MemberUserRespDTO getUserByMobile(String mobile);
+
 }

+ 5 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApiImpl.java

@@ -39,4 +39,9 @@ public class MemberUserApiImpl implements MemberUserApi {
         return UserConvert.INSTANCE.convertList2(userService.getUserListByNickname(nickname));
     }
 
+    @Override
+    public MemberUserRespDTO getUserByMobile(String mobile) {
+        return UserConvert.INSTANCE.convert2(userService.getUserByMobile(mobile));
+    }
+
 }

+ 0 - 1
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java

@@ -31,7 +31,6 @@ public interface MemberUserService {
      */
     List<MemberUserDO> getUserListByNickname(String nickname);
 
-
     /**
      * 基于手机号创建用户。
      * 如果用户已经存在,则直接进行返回

+ 2 - 2
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java

@@ -76,7 +76,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest {
             o.setBody("斌斌子送给灿灿子的炸弹猫");
             o.setNotifyUrl("https://hc.com/lbh");
             o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
-            o.setAmount(10000L);
+            o.setAmount(10000);
             o.setChannelFeeRate(0.01);
             o.setChannelFeeAmount(1L);
             o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus());
@@ -148,7 +148,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest {
             o.setBody("斌斌子送给灿灿子的炸弹猫");
             o.setNotifyUrl("https://hc.com/lbh");
             o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
-            o.setAmount(10000L);
+            o.setAmount(10000);
             o.setChannelFeeRate(0.01);
             o.setChannelFeeAmount(1L);
             o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus());

+ 4 - 4
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java

@@ -67,8 +67,8 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
             o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
             o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus());
             o.setType(PayRefundTypeEnum.SOME.getStatus());
-            o.setPayAmount(100L);
-            o.setRefundAmount(500L);
+            o.setPayAmount(100);
+            o.setRefundAmount(500);
             o.setReason("就是想退款了,你有意见吗");
             o.setUserIp("127.0.0.1");
             o.setChannelOrderNo("CH0000001");
@@ -136,8 +136,8 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
             o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
             o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus());
             o.setType(PayRefundTypeEnum.SOME.getStatus());
-            o.setPayAmount(100L);
-            o.setRefundAmount(500L);
+            o.setPayAmount(100);
+            o.setRefundAmount(500);
             o.setReason("就是想退款了,你有意见吗");
             o.setUserIp("127.0.0.1");
             o.setChannelOrderNo("CH0000001");

+ 2 - 0
yudao-ui-admin/src/utils/dict.js

@@ -65,6 +65,8 @@ export const DICT_TYPE = {
   TRADE_AFTER_SALE_STATUS: 'trade_after_sale_status', // 售后 - 状态
   TRADE_AFTER_SALE_WAY: 'trade_after_sale_way', // 售后 - 方式
   TRADE_AFTER_SALE_TYPE: 'trade_after_sale_type', // 售后 - 类型
+  TRADE_ORDER_TYPE: 'trade_order_type', // 订单 - 类型
+  TRADE_ORDER_STATUS: 'trade_order_status', // 订单 - 状态
 
   // ========== MALL - PROMOTION 模块 ==========
   PROMOTION_DISCOUNT_TYPE: 'promotion_discount_type', // 优惠类型

+ 2 - 2
yudao-ui-admin/src/views/mall/trade/afterSale/index.vue

@@ -46,7 +46,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <!-- Tab 选项:真正的内容在 Lab -->
+    <!-- Tab 选项:真正的内容在 Table -->
     <el-tabs v-model="activeTab" type="card" @tab-click="tabClick" style="margin-top: -40px;">
       <el-tab-pane v-for="tab in statusTabs" :key="tab.value" :label="tab.label" :name="tab.value" />
     </el-tabs>
@@ -100,7 +100,7 @@
 <script>
 import { getAfterSalePage } from "@/api/mall/trade/afterSale";
 import { datePickerOptions } from "@/utils/constants";
-import {DICT_TYPE, getDictDatas} from "@/utils/dict";
+import { DICT_TYPE, getDictDatas } from "@/utils/dict";
 
 export default {
   name: "AfterSale",

+ 86 - 60
yudao-ui-admin/src/views/mall/trade/order/index.vue

@@ -3,32 +3,34 @@
     <!-- 搜索工作栏 -->
     <!-- TODO: inline 看看是不是需要; v-show= 那块逻辑还是要的 -->
     <el-row :gutter="20">
-      <el-form :model="queryParams" label-width="68px" size="small">
+      <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
         <el-col :span="6" :xs="24">
-          <el-form-item label="搜索方式">
-            <el-input style="width: 240px">
-              <el-select v-model="queryParams.searchType" slot="prepend" clearable style="width: 100px">
+          <el-form-item label="搜索方式" prop="searchValue">
+            <el-input v-model="queryParams.searchValue" style="width: 240px">
+              <el-select v-model="queryParams.searchType" slot="prepend" style="width: 100px">
                 <el-option v-for="dict in dicData.searchType" v-bind="dict" :key="dict.value"/>
               </el-select>
             </el-input>
           </el-form-item>
         </el-col>
         <el-col :span="6" :xs="24">
-          <el-form-item label="订单类型">
-            <el-select v-model="queryParams.orderType" clearable style="width: 240px">
-              <el-option v-for="dict in dicData.orderType" v-bind="dict" :key="dict.value"/>
+          <el-form-item label="订单类型" prop="type">
+            <el-select v-model="queryParams.type" clearable style="width: 240px">
+              <el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_ORDER_TYPE)"
+                         :key="dict.value" :label="dict.label" :value="dict.value"/>
             </el-select>
           </el-form-item>
         </el-col>
         <el-col :span="6" :xs="24">
-          <el-form-item label="订单状态">
-            <el-select v-model="queryParams.orderStatus" clearable style="width: 240px">
-              <el-option v-for="dict in dicData.orderStatus" v-bind="dict" :key="dict.value"/>
+          <el-form-item label="订单状态" prop="status">
+            <el-select v-model="queryParams.status" clearable style="width: 240px">
+              <el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_ORDER_STATUS)"
+                         :key="dict.value" :label="dict.label" :value="dict.value"/>
             </el-select>
           </el-form-item>
         </el-col>
         <el-col :span="6" :xs="24">
-          <el-form-item label="订单来源">
+          <el-form-item label="订单来源" prop="terminal">
             <el-select v-model="queryParams.terminal" clearable style="width: 240px">
               <el-option v-for="dict in this.getDictDatas(DICT_TYPE.TERMINAL)"
                          :key="dict.value" :label="dict.label" :value="dict.value"/>
@@ -36,7 +38,7 @@
           </el-form-item>
         </el-col>
         <el-col :span="6" :xs="24">
-          <el-form-item label="支付方式">
+          <el-form-item label="支付方式" prop="payChannelCode">
             <el-select v-model="queryParams.payChannelCode" clearable style="width: 240px">
               <el-option v-for="dict in this.getDictDatas(DICT_TYPE.PAY_CHANNEL_CODE_TYPE)"
                          :key="dict.value" :label="dict.label" :value="dict.value"/>
@@ -44,23 +46,30 @@
           </el-form-item>
         </el-col>
         <el-col :span="6" :xs="24">
-          <el-form-item label="下单时间">
-            <el-date-picker v-model="queryParams.date" type="daterange" range-separator="至"
-                            start-placeholder="开始日期" end-placeholder="结束日期" :picker-options="datePickerOptions" style="width: 240px"/>
+          <el-form-item label="下单时间" prop="createTime">
+            <el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
+                            range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
+                            :picker-options="datePickerOptions" :default-time="['00:00:00', '23:59:59']" />
           </el-form-item>
         </el-col>
         <el-col :span="6" :xs="24" style="line-height: 32px">
-          <el-button type="primary" icon="el-icon-search" size="mini">搜索</el-button>
-          <el-button icon="el-icon-refresh" size="mini">重置</el-button>
+          <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+          <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
         </el-col>
       </el-form>
     </el-row>
 
-    <!-- tab切换-->
-    <el-tabs v-model="activeTabName" type="card">
-      <el-tab-pane v-for="tabPane in tabPanes" :label="tabPane.text" :name="tabPane.name">
-        <!-- table -->
-        <el-table :data="list" :show-header="false" class="order-table">
+    <!-- 操作工具栏 -->
+    <el-row :gutter="10" class="mb8">
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <!-- tab切换 -->
+    <!-- TODO @小程:看看能不能往上挪 -40px,和【隐藏搜索】【刷新】对齐 -->
+    <el-tabs v-model="activeTab" type="card" @tab-click="tabClick">
+      <el-tab-pane v-for="tab in statusTabs" :key="tab.value" :label="tab.label" :name="tab.value">
+        <!-- 列表 -->
+        <el-table v-loading="loading" :data="list" :show-header="false" class="order-table">
           <el-table-column>
             <template slot-scope="{ row }">
               <el-row type="flex" align="middle">
@@ -136,33 +145,25 @@
         </el-table>
       </el-tab-pane>
     </el-tabs>
+    <!-- 分页组件 -->
+    <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
+                @pagination="getList"/>
   </div>
 </template>
 
 <script>
 import { getOrderPage } from "@/api/mall/trade/order";
 import { datePickerOptions } from "@/utils/constants";
+import { DICT_TYPE, getDictDatas } from "@/utils/dict";
 
 const dicData = {
     searchType: [
-      { label: '订单号', value: 'ddh' },
-      { label: '交易流水号', value: 'jylsh' },
-      { label: '订单备注', value: 'ddbz' },
-      { label: '收货人姓名', value: 'shrxm' },
-      { label: '商品名称', value: 'spmc' },
-      { label: '收货人电话', value: 'shrdh' },
-      { label: '会员昵称', value: 'hync' },
-      { label: '商品编号', value: 'spbh' }
-    ],
-    orderStatus: [
-      { label: '全部', value: 'qb' },
-      { label: '待支付', value: 'dzf' },
-      { label: '待发货', value: 'dfh' },
-      { label: '已发货', value: 'yfh' },
-      { label: '已收货', value: 'ysh' },
-      { label: '已完成', value: 'ywc' },
-      { label: '已关闭', value: 'ygb' },
-      { label: '退款中', value: 'tkz' }
+      { label: '订单号', value: 'no' },
+      { label: '会员编号', value: 'userId' },
+      { label: '会员昵称', value: 'userNickname' },
+      { label: '会员手机号', value: 'userMobile' },
+      { label: '收货人姓名', value: 'receiverName' },
+      { label: '收货人手机号码', value: 'receiverMobile' },
     ],
   }
   export default {
@@ -183,20 +184,19 @@ const dicData = {
         queryParams: {
           pageNo: 1,
           pageSize: 10,
-          searchType: 'ddh',
-          orderType: ''
+          searchType: 'no',
+          searchValue: '',
+          type: null,
+          status: null,
+          payChannelCode: null,
+          createTime: [],
         },
-        activeTabName: 'all',
-        tabPanes: [
-          { text: '全部', name: 'all' },
-          { text: '待支付', name: 'toBePay' },
-          { text: '待发货', name: 'toBeSend' },
-          { text: '已发货', name: 'send' },
-          { text: '已收货', name: 'received' },
-          { text: '已完成', name: 'finished' },
-          { text: '已关闭', name: 'closed' },
-          { text: '退款中', name: 'refund' }
-        ],
+        // Tab 筛选
+        activeTab: 'all',
+        statusTabs: [{
+          label: '全部',
+          value: 'all'
+        }],
         // 静态变量
         datePickerOptions: datePickerOptions
       }
@@ -204,24 +204,50 @@ const dicData = {
     created() {
       this.getList();
       // 设置 statuses 过滤
-      // for (const dict of getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_STATUS)) {
-      //   this.statusTabs.push({
-      //     label: dict.label,
-      //     value: dict.value
-      //   })
-      // }
+      for (const dict of getDictDatas(DICT_TYPE.TRADE_ORDER_STATUS)) {
+        this.statusTabs.push({
+          label: dict.label,
+          value: dict.value
+        })
+      }
     },
     methods: {
       /** 查询列表 */
       getList() {
         this.loading = true;
         // 执行查询
-        getOrderPage(this.queryParams).then(response => {
+        getOrderPage({
+          ...this.queryParams,
+          searchType: undefined,
+          searchValue: undefined,
+          no: this.queryParams.searchType === 'no' ? this.queryParams.searchValue : undefined,
+          userId: this.queryParams.searchType === 'userId' ? this.queryParams.searchValue : undefined,
+          userNickname: this.queryParams.searchType === 'userNickname' ? this.queryParams.searchValue : undefined,
+          userMobile: this.queryParams.searchType === 'userMobile' ? this.queryParams.searchValue : undefined,
+          receiverName: this.queryParams.searchType === 'receiverName' ? this.queryParams.searchValue : undefined,
+          receiverMobile: this.queryParams.searchType === 'receiverMobile' ? this.queryParams.searchValue : undefined,
+        }).then(response => {
           this.list = response.data.list;
           this.total = response.data.total;
           this.loading = false;
         });
       },
+      /** 搜索按钮操作 */
+      handleQuery() {
+        this.queryParams.pageNo = 1;
+        this.activeTab = this.queryParams.status ? this.queryParams.status : 'all'; // 处理 tab
+        this.getList();
+      },
+      /** 重置按钮操作 */
+      resetQuery() {
+        this.resetForm("queryForm");
+        this.handleQuery();
+      },
+      /** tab 切换 */
+      tabClick(tab) {
+        this.queryParams.status = tab.name === 'all' ? undefined : tab.name;
+        this.getList();
+      },
       goToDetail (row) {
         this.$router.push({ path: '/mall/trade/order/detail', query: { orderNo: row.orderNo }})
       }