1.搭建order-consumer环境

项目结构:
image.png

1.依赖

项目结构:
image.png
代码:

  1. <dependencies>
  2. <dependency>
  3. <groupId>com.aliyun.oss</groupId>
  4. <artifactId>aliyun-sdk-oss</artifactId>
  5. <version>3.5.0</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>com.zh.crowd</groupId>
  9. <artifactId>crowdfunding17-member-api</artifactId>
  10. <version>0.0.1-SNAPSHOT</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.springframework.boot</groupId>
  14. <artifactId>spring-boot-configuration-processor</artifactId>
  15. <optional>true</optional>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.springframework.boot</groupId>
  19. <artifactId>spring-boot-starter-web</artifactId>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.springframework.boot</groupId>
  23. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.springframework.cloud</groupId>
  27. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.springframework.boot</groupId>
  31. <artifactId>spring-boot-starter-data-redis</artifactId>
  32. </dependency>
  33. <dependency>
  34. <groupId>org.springframework.session</groupId>
  35. <artifactId>spring-session-data-redis</artifactId>
  36. </dependency>
  37. </dependencies>

2.application.yml

项目结构:
image.png
代码:

  1. server:
  2. port: 6000
  3. spring:
  4. application:
  5. name: crowd-order
  6. thymeleaf:
  7. prefix: classpath:/templates/
  8. suffix: .html
  9. redis:
  10. host: 192.168.0.101
  11. session:
  12. store-type: redis
  13. eureka:
  14. client:
  15. service-url:
  16. defaultZone: http://localhost:1000/eureka/

3.主启动类

项目结构:
image.png
代码:

// 启用Feign功能
@EnableFeignClients
@SpringBootApplication
public class CrowdMainOrderApp {
    public static void main(String[] args) {
        SpringApplication.run(CrowdMainOrderApp.class,args);
    }
}

4.在Zuul中添加模块

项目结构:
image.png
代码:

zuul:
  ignored-services: "*"       # 表示忽视直接通过application-name访问微服务,必须通过route
  sensitive-headers: "*"      # 在Zuul向其他微服务重定向时,保持原本的头信息(请求头、响应头)
  routes:                     # 指定网关路由
    crowd-protal:
      service-id: crowd-auth  # 对应application-name
      path: /**               # 表示直接通过根路径访问,必须加上**,否则多层路径无法访问
    crowd-project:
      service-id: crowd-project
      path: /project/**
    crowd-order:
      service-id: crowd-order
      path: /order/**

2.数据库建模

1.结构

image.png

2.数据库建表

# 建表t_order(订单表)
CREATE TABLE `t_order` (
    `id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
    `order_num` CHAR(100) COMMENT '订单号',
    `pay_order_num` CHAR(100) COMMENT '支付宝流水号',
    `order_amount` DOUBLE(10,5) COMMENT '订单金额',
    `invoice` INT COMMENT '是否开发票(0 不开,1 开)',
    `invoice_title` CHAR(100) COMMENT '发票抬头',
    `order_remark` CHAR(100) COMMENT '订单备注',
    `address_id` CHAR(100) COMMENT '收货地址 id', 
    PRIMARY KEY (`id`)
);


# 建表t_address(收货地址表)
CREATE TABLE t_address (
    `id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
    `receive_name` CHAR(100) COMMENT '收件人',
    `phone_num` CHAR(100) COMMENT '手机号',
    `address` CHAR(200) COMMENT '收货地址',
    `member_id` INT COMMENT '用户 id',
    PRIMARY KEY (`id`)
);


# 建表t_order_project(项目信息表)
CREATE TABLE `t_order_project` (
    `id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
    `project_name` CHAR(200) COMMENT '项目名称',
    `launch_name` CHAR(100) COMMENT '发起人',
    `return_content` CHAR(200) COMMENT '回报内容',
    `return_count` INT COMMENT '回报数量',
    `support_price` INT COMMENT '支持单价',
    `freight` INT COMMENT '配送费用',
    `order_id` INT COMMENT '订单表的主键', 
    PRIMARY KEY (`id`)
);

3.逆向工程

项目结构:
image.png
代码:

<!-- 数据库表名与需要的实体类对应映射的指定 -->
        <table tableName="t_order" domainObjectName="OrderPO" />
        <table tableName="t_address" domainObjectName="AddressPO" />
        <table tableName="t_order_project" domainObjectName="OrderProjectPO" />

实体类:
image.png
mapper接口:
image.png
mapper文件:
image.png


3.页面-确认回报内容

1.思路

image.png

2.代码

1.创建OrderProjectVO类

项目结构:
image.png
代码:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderProjectVO implements Serializable {

    private Integer id;

    private String projectName;

    private String launchName;

    private String returnContent;

    private Integer returnCount;

    private Integer supportPrice;

    private Integer freight;

    private Integer orderId;

    //    是否限制单笔购买数量,0 表示不限购,1 表示限购
    private Integer signalPurchase;

    //    如果单笔限购,那么具体的限购数量
    private Integer purchase;


}

2.跳转到确认回报页面

修改project-consumer模块的project-show-detail.html页面代码,使点击支持按钮后跳转到确认回报的页面(需要附带上当前回报内容的id):
项目结构:
image.png
代码:

<a class="btn btn-warning btn-lg" th:href="'http://localhost/order/confirm/return/info/' + ${return.returnId}">
    支持
</a>

3.Handler方法

通过order-consumer模块的handler方法,进入确认回报页面
项目结构:
image.png
代码:

    // 进入确认回报信息的页面
    @RequestMapping("/confirm/return/info/{returnId}")
    public String confirmReturnInfo(
            @PathVariable("returnId") Integer returnId,
            HttpSession session) {

        ResultEntity<OrderProjectVO> resultEntity = mySQLRemoteService.getOrderProjectVO(returnId);

        if (ResultEntity.SUCCESS.equals(resultEntity.getResult())){
            session.setAttribute("orderProjectVO", resultEntity.getData());
        }

        return "order-confirm-return";
    }

4.远程方法

1.api接口模块

项目结构:
image.png
代码:

@RequestMapping("/get/order/project/vo/remote")
ResultEntity<OrderProjectVO> getOrderProjectVO(@RequestParam("returnId") Integer returnId);

2.mysql模块的handler方法

项目结构:
image.png
代码:

@RequestMapping("/get/order/project/vo/remote")
ResultEntity<OrderProjectVO> getOrderProjectVO(@RequestParam("returnId") Integer returnId) {

    try {
        OrderProjectVO orderProjectVO = orderService.getOrderProjectVO(returnId);
        return ResultEntity.successWithData(orderProjectVO);
    } catch (Exception e) {
        e.printStackTrace();
        return ResultEntity.failed(e.getMessage());
    }
}

3.mysql模块的service接口方法

项目结构:
image.png
代码:

OrderProjectVO getOrderProjectVO(Integer returnId);

4.mysql模块的service实现类方法

项目结构:
image.png
代码:

@Override
public OrderProjectVO getOrderProjectVO(Integer returnId) {
    return orderProjectPOMapper.selectOrderProjectVO(returnId);
}

5.SQL语句

项目结构:
image.png
代码:

 <select id="selectOrderProjectVO" resultType="com.zh.crowd.entity.vo.OrderProjectVO">
    select distinct
      project_name projectName,
      description_simple launchName,
      content returnContent,
      count returnCount,
      t_return.supportmoney supportPrice,
      freight,
      signalpurchase signalPurchase,
      purchase
    from t_project
           left join t_member_launch_info on t_project.memberid = t_member_launch_info.memberid
           left join t_return on t_project.id = t_return.projectid
    where t_return.id = #{returnId}
  </select>

4.前台显示

截取,从session域中通过orderProjectVO得到项目与回报的各种信息:
项目结构:
image.png
代码:

<div class="col-md-12 column">
    <table class="table table-bordered" style="text-align:center;">
        <thead>
        <tr style="background-color:#ddd;">
            <td>项目名称</td>
            <td>发起人</td>
            <td width="300">回报内容</td>
            <td width="80">回报数量</td>
            <td>支持单价</td>
            <td>配送费用</td>
        </tr>
        </thead>
        <tbody>
        <tr>
            <td th:text="${session.orderProjectVO.projectName}">活性富氢净水直饮机</td>
            <td th:text="${session.orderProjectVO.launchName}">深圳市博实永道电子商务有限公司</td>
            <td th:text="${session.orderProjectVO.returnContent}">每满1750人抽取一台活性富氢净水直饮机,至少抽取一台。</td>
            <td >
                <input id="returnCountInput" type="text" class="form-control" style="width:60px;" th:value="${session.orderProjectVO.returnCount}">
            </td>
            <td style="color:#F60">¥[[${session.orderProjectVO.supportPrice}]]</td>
            <td th:if="${session.orderProjectVO.freight} == 0">免运费</td>
            <td th:if="${session.orderProjectVO.freight} > 0" th:text="${session.orderProjectVO.freight}">运费</td>
        </tr>
        </tbody>
    </table>
    <div style="float:right;">
        <p>总价(含运费):¥
            <span id="totalAmount" style="font-size:16px;color:#F60;" 
                          th:text="${session.orderProjectVO.freight} + (${session.orderProjectVO.supportPrice} * ${session.orderProjectVO.returnCount})">1.00
            </span>
        </p>
        <button id="submitBtn" type="button" class="btn btn-warning btn-lg" style="float:right;" >
            <i class="glyphicon glyphicon-credit-card"></i> 去结算
        </button>
    </div>
</div>

为了实现修改回报数量文本框中的内容,自动修改总价的功能:
image.png
项目结构:
image.png
代码:

var signalPurchase = [[${session.orderProjectVO.signalPurchase}]];

var purchase = [[${session.orderProjectVO.purchase}]];

var freight = [[${session.orderProjectVO.freight}]]

var supportPrice = [[${session.orderProjectVO.supportPrice}]]

// 自动修正总价的功能
// 当id=returnCountInput的标签发送改动时触发
$("#returnCountInput").change(function () {
    // 得到回报数量框内输入的量
    var count = $.trim($(this).val());
    // 判断是否输入了有效数字
    if (count == null || count == "") {
        alert("请输入有效的数字!");
        // 恢复为默认值
        $(this).val(this.defaultValue);
    }
    // 判断是否超过限购数量
    if (signalPurchase == 1 && purchase < count) {
        alert("不能超过限购数量!");
        // 恢复为默认值
        $(this).val(this.defaultValue);
        return ;
    }

    // 满足上述条件 计算最后的总价
    $("#totalAmount").text(freight + supportPrice * count);
});

4.页面-确认订单

1.跳转到确认订单的页面

通过给确认回报内容的页面的“去结算”按钮添加单击响应函数:
image.png

1.思路

image.png

2.代码

1.创建AddressVO类

项目结构:
image.png
代码:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class AddressVO implements Serializable {
    private Integer id;

    private String receiveName;

    private String phoneNum;

    private String address;

    private Integer memberId;

}

2.后端handler方法进行跳转操作

项目结构:
image.png
代码:

@RequestMapping("/confirm/order/{returnCount}")
public String toConfirmOrderPage(
        @PathVariable("returnCount") Integer returnCount,
        HttpSession session ) {

    // 从session域拿到orderProjectVO对象
    OrderProjectVO orderProjectVO = (OrderProjectVO) session.getAttribute("orderProjectVO");

    // 给orderProjectVO设置回报的数量
    orderProjectVO.setReturnCount(returnCount);

    // 重新将orderProjectVO放回session域
    session.setAttribute("orderProjectVO",orderProjectVO);

    // 获取当前的用户的Address对象(即收货地址信息)

    // 1、先得到当前的用户id
    LoginMemberVO loginMember = (LoginMemberVO)session.getAttribute(CrowdConstant.ATTR_NAME_LOGIN_MEMBER);
    Integer memberId = loginMember.getId();

    // 2、通过用户id得到他的收货地址的List
    ResultEntity<List<AddressVO>> resultEntity = mySQLRemoteService.getAddressListByMemberIdRemote(memberId);

    if (ResultEntity.SUCCESS.equals(resultEntity.getResult())) {
        // 3、如果成功,则将收货地址放入session域
        List<AddressVO> addressVOList = resultEntity.getData();
        session.setAttribute("addressVOList", addressVOList);
    }

    // 进入确认订单页面
    return "order-confirm-order";
}

3.api项目中声明接口

项目结构:
image.png
代码:

@RequestMapping("/get/address/vo/remote")
    ResultEntity<List<AddressVO>> getAddressListByMemberIdRemote(@RequestParam("memberId") Integer memberId);

4.MySQL项目handler方法

项目结构:
image.png
代码:

@RequestMapping("/get/address/vo/remote")
    ResultEntity<List<AddressVO>> getAddressListByMemberIdRemote(@RequestParam("memberId")Integer memberId){

        try {
            List<AddressVO> addressVOList = orderService.getAddressVOList(memberId);
             return ResultEntity.successWithData(addressVOList);
        } catch (Exception e) {
            e.printStackTrace();
           return ResultEntity.failed(e.getMessage());
        }

    }

5.MySQL项目中service接口

项目结构:
image.png
代码:

List<AddressVO> getAddressVOList(Integer memberId);

6.MySQL项目中service实现类

项目结构:
image.png
代码:

@Override
    public List<AddressVO> getAddressVOList(Integer memberId) {
        AddressPOExample example = new AddressPOExample();
        example.createCriteria().andMemberIdEqualTo(memberId);
        List<AddressPO> addressPOS = addressPOMapper.selectByExample(example);

        List<AddressVO> addressVOList = new ArrayList<>();

        for (AddressPO addressPO : addressPOS) {
            AddressVO addressVO = new AddressVO();
            BeanUtils.copyProperties(addressPO,addressVO);
            addressVOList.add(addressVO);
        }
        return addressVOList;
    }

7.前端页面

image.png


2.在确认订单页面显示需要的数据

1.显示收货信息

1.修改表单

项目结构:
image.png
代码:

<form action="order/save/address" method="post" class="form-horizontal">
    <input type="hidden" name="memberId" th:value="${session.loginMember.id}">
    <div class="form-group">
        <label class="col-sm-2 control-label">收货人(*)</label>
        <div class="col-sm-10">
            <input name="receiveName" type="text" class="form-control" style="width:200px;" placeholder="请填写收货人姓名" >
        </div>
    </div>
    <div class="form-group">
        <label class="col-sm-2 control-label">手机(*)</label>
        <div class="col-sm-10">
            <input name="phoneNum" class="form-control" type="text" style="width:200px;" placeholder="请填写11位手机号码"></input>
        </div>
    </div>
    <div class="form-group">
        <label class="col-sm-2 control-label">地址(*)</label>
        <div class="col-sm-10">
            <input name="address" class="form-control" type="text" style="width:400px;" placeholder="请填写收货地址"></input>
        </div>
    </div>
    <div class="form-group">
        <label for="inputEmail3" class="col-sm-2 control-label"></label>
        <div class="col-sm-10">
            <button type="submit" class="btn btn-primary">确认配送信息</button>
        </div>
    </div>
</form>

2.handler方法

项目结构:
image.png
代码:

@RequestMapping("/save/address")
public String saveAddress(AddressVO addressVO, HttpSession session) {

    // 通过远程方法保存地址信息
    ResultEntity<String> resultEntity = mySQLRemoteService.saveAddressRemote(addressVO);

    // 从session域得到当前的orderProjectVO
    OrderProjectVO orderProjectVO = (OrderProjectVO) session.getAttribute("orderProjectVO");

    // 得到当前的回报数量
    Integer returnCount = orderProjectVO.getReturnCount();

    // 再次重定向到确认订单的页面(附带回报数量)
    return "redirect:http://localhost/order/confirm/order/" + returnCount;
}

3.api接口模块

项目结构:
image.png
代码:

@RequestMapping("/save/address/remote")
ResultEntity<String> saveAddressRemote(@RequestBody AddressVO addressVO);

4.mysql模块的远程方法

handler方法:
项目结构:
image.png
代码:

@RequestMapping("/save/address/remote")
ResultEntity<String> saveAddressRemote(@RequestBody AddressVO addressVO) {
    try {
        orderService.saveAddressPO(addressVO);
        return ResultEntity.successWithoutData();
    } catch (Exception e) {
        e.printStackTrace();
        return ResultEntity.failed(e.getMessage());
    }
}

service接口:
项目结构:
image.png
代码:

 void saveAddressPO(AddressVO addressVO);

service实现方法:
项目结构:
image.png
代码:

 @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    @Override
    public void saveAddressPO(AddressVO addressVO) {
        AddressPO addressPO = new AddressPO();
        BeanUtils.copyProperties(addressVO,addressPO);
        addressPOMapper.insert(addressPO);
    }


3.控制“立即付款”按钮是否有效

通过js代码实现勾选“我已了解风险和规则”后才能点击立即付款
下面两个标签设置id,付款按钮默认是disabled的

<li style="margin-top:10px;">
    <button id="payBtn" disabled="disabled" type="button" class="btn btn-warning btn-lg" onclick="window.location.href='pay-step-3.html'">
        <i class="glyphicon glyphicon-credit-card"></i> 立即付款
    </button>
</li>
<li style="margin-top:10px;">
    <div class="checkbox">
        <label>
            <input id="knowCheckbox" type="checkbox"> 我已了解风险和规则
        </label>
    </div>
</li>

JS代码:

<script>
    $("#knowCheckbox").click(function () {
        // 得到当前的勾选状态
        var currentStatus = this.checked;
        // 根据是否勾选,设置付款按钮是否可用
        if (currentStatus) {
            $("#payBtn").prop("disabled","");
        } else {
            $("#payBtn").prop("disabled","disabled");
        }
    });
</script>

5.提交订单表单

项目结构:
image.png
构建页面不可见表单:

<!--建立空表单用于提交支付信息-->
<form id="summaryForm" action="/pay/generate/order" method="post"></form>

收集需要提交的数据:

// 支付按钮的单击响应事件
    $("#payBtn").click(function () {
        // 收集要提交给表单的数据
        var addressId = $("[name=addressId]:checked").val();
        var invoice = $("[name=invoiceRadio]:checked").val();
        var invoiceTitle = $.trim($("[name=invoiceTitle]").val());
        var remark = $.trim($("[name=remark]").val());
        // 提交表单
        $("#summaryForm")
            .append("<input type='hidden' name='addressId' value='"+ addressId +"' />")
            .append("<input type='hidden' name='invoice' value='"+ invoice +"' />")
            .append("<input type='hidden' name='invoiceTitle' value='"+ invoiceTitle +"' />")
            .append("<input type='hidden' name='orderRemark' value='"+ remark +"' />")
            .submit();
    });