跳至主要內容

订单创建

起凡大约 18 分钟起凡商城订单订单创建

订单创建

订单创建
订单创建

枚举值添加

需要把创建人和编辑人的id替换成自己的id

INSERT INTO mall.dict (id, created_time, edited_time, creator_id, editor_id, key_id, key_en_name, key_name, dict_id, dict_name, dict_en_name, order_num) VALUES ('a268e25b-b3b7-4fc2-880d-5b97e1acab0b', '2024-01-26 11:11:40.133277', '2024-01-26 11:11:40.133277', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', 1, 'ALI_PAY', '支付宝', 1004, '支付方式', 'PAY_TYPE', 0);
INSERT INTO mall.dict (id, created_time, edited_time, creator_id, editor_id, key_id, key_en_name, key_name, dict_id, dict_name, dict_en_name, order_num) VALUES ('1a687b7d-9b24-47b0-aa57-e361812dcdf0', '2024-01-26 11:10:49.521488', '2024-01-26 11:10:49.521488', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', 0, 'WE_CHAT_PAY', '微信支付', 1004, '支付类型', 'PAY_TYPE', 0);
INSERT INTO mall.dict (id, created_time, edited_time, creator_id, editor_id, key_id, key_en_name, key_name, dict_id, dict_name, dict_en_name, order_num) VALUES ('461a361d-073c-4574-aed1-c025e04a81a3', '2024-01-26 11:09:32.434369', '2024-01-26 11:13:54.428416', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', 5, 'REFUNDING', '退款中', 1003, '商品订单状态', 'PRODUCT_ORDER_STATUS', 0);
INSERT INTO mall.dict (id, created_time, edited_time, creator_id, editor_id, key_id, key_en_name, key_name, dict_id, dict_name, dict_en_name, order_num) VALUES ('2a8a7427-9fb6-4ecb-822c-8b22fd493a93', '2024-01-26 11:08:34.090534', '2024-01-26 11:08:37.984915', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', 4, 'CLOSED', '已关闭', 1003, '商品订单状态', 'PRODUCT_ORDER_STATUS', 0);
INSERT INTO mall.dict (id, created_time, edited_time, creator_id, editor_id, key_id, key_en_name, key_name, dict_id, dict_name, dict_en_name, order_num) VALUES ('a1a13655-7328-45c3-8cdd-dc0d41ef5792', '2024-01-26 11:06:10.939935', '2024-01-26 11:06:16.216645', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', 3, 'TO_BE_EVALUATED', '待评价', 1003, '商品订单状态', 'PRODUCT_ORDER_STATUS', 0);
INSERT INTO mall.dict (id, created_time, edited_time, creator_id, editor_id, key_id, key_en_name, key_name, dict_id, dict_name, dict_en_name, order_num) VALUES ('56e8d930-6953-4f6a-875c-34d5c26802a5', '2024-01-26 11:03:49.162351', '2024-01-26 11:04:00.418344', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', 2, 'TO_BE_RECEIVED', '待收货', 1003, '商品订单状态', 'PRODUCT_ORDER_STATUS', 0);
INSERT INTO mall.dict (id, created_time, edited_time, creator_id, editor_id, key_id, key_en_name, key_name, dict_id, dict_name, dict_en_name, order_num) VALUES ('fc930d38-0612-4217-91ab-809a5be03656', '2024-01-26 11:02:08.987958', '2024-01-26 11:02:22.277984', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', 1, 'TO_BE_DELIVERED', '待发货', 1003, '商品订单状态 ', 'PRODUCT_ORDER_STATUS', 0);
INSERT INTO mall.dict (id, created_time, edited_time, creator_id, editor_id, key_id, key_en_name, key_name, dict_id, dict_name, dict_en_name, order_num) VALUES ('5c820b53-6545-45fd-8442-22f7e486fc8e', '2024-01-26 10:56:45.364997', '2024-01-26 11:02:58.744868', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', 0, 'TO_BE_PAID', '待付款', 1003, '商品订单状态', 'PRODUCT_ORDER_STATUS', 0);
INSERT INTO mall.dict (id, created_time, edited_time, creator_id, editor_id, key_id, key_en_name, key_name, dict_id, dict_name, dict_en_name, order_num) VALUES ('1f01fa7b-f162-4376-870d-9207735f658d', '2024-01-16 09:33:09.151337', '2024-01-16 09:33:09.151337', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', 2, 'BUTTON', '按钮', 1002, '菜单类型', 'MENU_TYPE', 2);
INSERT INTO mall.dict (id, created_time, edited_time, creator_id, editor_id, key_id, key_en_name, key_name, dict_id, dict_name, dict_en_name, order_num) VALUES ('416c90b4-42e8-4af1-a3f5-7e321c9c3437', '2024-01-16 09:32:28.555205', '2024-01-16 09:32:28.555205', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', '0f07d638-f1bc-4011-88d8-6dc650ab06a7', 1, 'DIRECTORY', '目录', 1002, '菜单类型', 'MENU_TYPE', 0);

添加完之后调用该接口生成枚举类

GET http://localhost:8877/dict/java

表/实体类创建

create table product_order
(
    id             varchar(36) not null
        primary key,
    created_time   datetime(6) not null,
    edited_time    datetime(6) not null,
    creator_id     varchar(36) not null,
    editor_id      varchar(36) not null,
    coupon_user_id varchar(36) null,
    payment_id     varchar(36) not null,
    address_id     varchar(36) not null,
    status         varchar(36) not null,
    remark         text        null
)
@Entity
@GenEntity
public interface ProductOrder extends BaseEntity {

  @GenField(value = "备注", order = 0)
  String remark();

  @GenField(value = "订单状态", order = 1, type = ItemType.SELECTABLE, dictEnName = DictConstants.PRODUCT_ORDER_STATUS)
  ProductOrderStatus status();

  @OneToOne
  Payment payment();

  @OneToOne
  Address address();

  @OneToMany(mappedBy = "productOrder")
  @Valid
  @Size(min = 1, message = "订单至少需要一个商品")
  @NotNull(message = "订单至少需要一个商品")
  List<ProductOrderItem> items();
}

这个ProductOrder实体类设计是电商系统中订单模块的核心部分,它继承了BaseEntity基础实体接口。

实体关系映射 (Jimmer注解)

  • @Entity 表示这是一个Java持久化实体,将会映射到数据库的product_order表。
  • @OneToOne 关系用于表示一个订单对应一个支付详情记录和一个收货地址信息。例如,通过payment()方法可以获取到与该订单相关的具体支付详情,包括支付方式、金额、时间等;而address()方法则能获取到用户下单时选择的配送地址信息。
  • @OneToMany(mappedBy = "productOrder") 表示一个订单可以包含多个商品订单项,与ProductOrderItem实体之间是一对多的关系。其中,mappedBy属性指定了关联关系在对方实体类(即ProductOrderItem)中的维护端字段。

字段解释

  1. 备注(remark)

    • 该字段是一个字符串类型,用于记录关于此订单的任何额外信息或者用户特殊要求,例如用户对配送、包装等的个性化说明。
  2. 订单状态(status)

    • 使用枚举类型 ProductOrderStatus 表示订单的不同状态,如待支付、已支付、待发货、已发货、已完成、已取消等。通过 @GenField 注解定义为可选列表类型,在前端展示时可以从字典表(DictConstants.PRODUCT_ORDER_STATUS)中获取对应的选项。
  3. payment

    • 这是一个一对一关联关系,表示每个商品订单都有一个与之关联的支付记录(Payment)。当需要查询订单支付详情时,可以通过这个字段直接访问关联的 Payment 实体。
  4. address

    • 同样是一对一关联关系,关联到用户的收货地址信息(Address)。存储订单配送的具体地址信息。
  5. items

    • 通过一对多关联,定义了一个包含多个 ProductOrderItem 的列表,代表订单中包含的商品明细项。
    • 使用 @Valid 注解表示列表中的每个元素都必须是有效的 ProductOrderItem 实体。
    • @Size(min = 1, message = "订单至少需要一个商品") 指定订单至少包含一项商品,否则会抛出校验异常。
    • @NotNull(message = "订单至少需要一个商品") 也确保了这一约束,即不能为空列表。

创建订单接口

创建dto修改

ProductOrderInput 是一个用于创建订单时的数据传输对象(DTO, Data Transfer Object),它包含了从客户端传递到服务器端创建新订单所需的部分属性,但排除了某些字段并添加了一些额外的细节。

input ProductOrderInput {
    #allScalars(ProductOrder)
    id? # 可选字段,在创建新订单时不需要提供。
    -status # 排除字段,表示在创建订单输入时不包含“status”属性,因为订单状态通常是由系统根据业务逻辑自动设置的,如默认为待支付状态。
    id(address) 用户需要提供一个收货地址的ID,这个地址已经在系统中存在,通过这个ID将订单与具体的收货地址关联起来
    items {            # 订单项集合,包含每个商品SKU的数量和ID信息。
        skuCount       # 每个商品SKU的数量,例如购买某款商品5件。
        productSkuId   # 商品SKU的唯一标识符,用于确定具体购买的是哪一款商品的不同变体。
    }
}

实际案例: 假设一位用户要下单购买两个不同商品,分别是商品A的红色款式3件,商品B的蓝色款式2件。那么对应的 ProductOrderInput 示例数据可能是这样的:

{
  "addressId": "1234", // 假设这是已存在的一个有效地址ID
  "items": [
    {
      "skuCount": 3,
      "productSkuId": "AS001-RED"
    },
    {
      "skuCount": 2,
      "productSkuId": "BS002-BLUE"
    }
  ]
}

路由配置

export default defineAppConfig({
  pages: [
    "pages/index/index",
    "pages/user/index",
    "pages/address/address-list",
    "pages/address/address-save",
    "pages/order/order-create",
  ],
//   忽略...
});







 




购物车提交处理

点击提交购物车时,购物车会把已选的SKU通过提交购物车事件向外传播,该方法处理提交购物车事件,并将已选的SKU传给订单提交页面。

  1. 当调用handleSubmit函数时,它接收一个参数catItems,这个数组包含了用户购物车中已选择的商品项目(CartItem对象列表),每个CartItem通常会包含商品的SKU信息(Stock Keeping Unit,库存量单位)以及其他相关信息,如数量、价格等。

  2. 函数内部使用了Taro的API Taro.navigateTo 来进行页面跳转,将用户的当前路径导航至订单创建页面——"/pages/order/order-create"。

  3. 在页面跳转成功后(即success回调函数中),通过调用Taro.eventCenter.trigger方法触发了一个自定义全局事件——"submitCart",同时将传入的catItems作为参数传递出去。这样,在订单创建页面或其他监听此事件的地方,可以通过Taro.eventCenter.on订阅该事件,并接收到这些选中的商品SKU信息,进而实现从购物车到订单页面的数据传输和订单生成操作。

  <cart-list @submit="handleSubmit"></cart-list>
const handleSubmit = (catItems: CartItem[]) => {
  Taro.navigateTo({
    url: "/pages/order/order-create",
    success: () => {
      Taro.eventCenter.trigger("submitCart", catItems);
    },
  });
};

订单创建页面

地址选择

  1. 地址选择弹出。

    • v-if="address":只有当address这个数据属性存在时,才会渲染这部分内容。
    • @click="addressChooseVisible = true":点击此组件时,会触发事件处理器,将addressChooseVisible设置为true,从而打开地址选择弹出框。
  2. nut-cell组件中:

    • 使用了location2图标组件来表示位置信息。
    • 通过插槽#title展示当前选中的地址,由address-row组件负责呈现详细信息。
    • 最后,有一个指向右侧的小箭头图标,表明用户可以更换地址。
  3. 接下来是一个<address-choose>自定义组件,它是一个地址选择器:

    • v-model:visible="addressChooseVisible":该组件使用Vue的v-model指令与addressChooseVisible双向绑定,控制其可见性,即是否显示地址选择弹出框。
    • @choose="handleAddressChose":监听并绑定choose事件,当用户在地址列表中选择了一个新地址时,会触发handleAddressChose方法。
<template>
  <div class="order-submit">
    <div class="address" v-if="address">
      <nut-cell is-link center @click="addressChooseVisible = true">
        <template #icon>
          <location2
            color="red"
            size="20"
            style="margin-right: 10px"
          ></location2>
        </template>
        <template #title>
          <address-row :address="address"></address-row>
        </template>
        <template #link>
          <rect-right></rect-right>
        </template>
      </nut-cell>
    </div>
    <address-choose
      v-model:visible="addressChooseVisible"
      @choose="handleAddressChose"
    ></address-choose>
  </div>
</template>

商品Sku展示和价格详情

用户提交订单时呈现他们所选商品的具体信息和预估订单费用概览。

  1. 商品列表

    • 使用 v-for 循环遍历 cartItems (包含了购物车中的所有已选商品项)。
    • 对于每个商品项 item,生成一个名为 product-row 的自定义组件实例,并将商品 SKU 信息以及品牌信息传入作为属性值。
    <product-row
      v-for="item in cartItems"
      :key="item.sku.values.join(',')"
      :product="{
        ...item.sku,
        description: item.sku.values.join(','),
        brand: item.product.brand,
      }"
    >
     <template #operation>
       <div class="sku-count">x{{ item.count }}</div>
     </template>
    </product-row>
    
    • 在每个 product-row 组件内部,通过插槽 (<template #operation>) 渲染出商品数量,显示为 x{{ item.count }}
  2. 订单价格详情

    • 使用 nut-cell-group 和多个嵌套的 nut-cell 组件展示订单总价、配送费、优惠券抵扣金额及VIP优惠等详细信息。
    • 目前这些金额都暂时设置为 0,之后会在后端进行计算并替换这些静态值。
<template>
  <div class="order-submit">
    <!-- 忽略地址信息... -->
    <div class="product-list">
      <product-row
        v-for="item in cartItems"
        :key="item.sku.values.join(',')"
        :product="{
          ...item.sku,
          description: item.sku.values.join(','),
          brand: item.product.brand,
        }"
      >
        <template #operation>
          <div class="sku-count">x{{ item.count }}</div>
        </template>
      </product-row>
    </div>
    <nut-cell-group class="summary">
      <nut-cell title="商品总价">
        <template #desc>
          <div class="value">¥{{ productPrice }}</div>
        </template>
      </nut-cell>
      <nut-cell title="配送费">
        <template #desc>
          <div class="value">¥{{ 0 }}</div>
        </template>
      </nut-cell>
      <nut-cell title="优惠券">
        <template #desc>
          <div class="value">-¥{{ 0 }}</div>
        </template>
      </nut-cell>
      <nut-cell title="vip优惠">
        <template #desc>
          <div class="value">-¥{{ 0 }}</div>
        </template>
      </nut-cell>
    </nut-cell-group>
  </div>
</template>

订单提交

<template>
  <div class="order-submit">
    <div class="submit-bar-wrapper">
      <div class="submit-bar">
        <div class="price">¥{{ productPrice }}</div>
        <nut-button type="danger" @click="saveOrder">提交订单</nut-button>
      </div>
    </div>
  </div>
</template>