1.搭建order-consumer环境
1.依赖
项目结构:
代码:
<dependencies>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>com.zh.crowd</groupId>
<artifactId>crowdfunding17-member-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
</dependencies>
2.application.yml
项目结构:
代码:
server:
port: 6000
spring:
application:
name: crowd-order
thymeleaf:
prefix: classpath:/templates/
suffix: .html
redis:
host: 192.168.0.101
session:
store-type: redis
eureka:
client:
service-url:
defaultZone: http://localhost:1000/eureka/
3.主启动类
项目结构:
代码:
// 启用Feign功能
@EnableFeignClients
@SpringBootApplication
public class CrowdMainOrderApp {
public static void main(String[] args) {
SpringApplication.run(CrowdMainOrderApp.class,args);
}
}
4.在Zuul中添加模块
项目结构:
代码:
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.结构
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.逆向工程
项目结构:
代码:
<!-- 数据库表名与需要的实体类对应映射的指定 -->
<table tableName="t_order" domainObjectName="OrderPO" />
<table tableName="t_address" domainObjectName="AddressPO" />
<table tableName="t_order_project" domainObjectName="OrderProjectPO" />
3.页面-确认回报内容
1.思路
2.代码
1.创建OrderProjectVO类
项目结构:
代码:
@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):
项目结构:
代码:
<a class="btn btn-warning btn-lg" th:href="'http://localhost/order/confirm/return/info/' + ${return.returnId}">
支持
</a>
3.Handler方法
通过order-consumer模块的handler方法,进入确认回报页面
项目结构:
代码:
// 进入确认回报信息的页面
@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接口模块
项目结构:
代码:
@RequestMapping("/get/order/project/vo/remote")
ResultEntity<OrderProjectVO> getOrderProjectVO(@RequestParam("returnId") Integer returnId);
2.mysql模块的handler方法
项目结构:
代码:
@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接口方法
项目结构:
代码:
OrderProjectVO getOrderProjectVO(Integer returnId);
4.mysql模块的service实现类方法
项目结构:
代码:
@Override
public OrderProjectVO getOrderProjectVO(Integer returnId) {
return orderProjectPOMapper.selectOrderProjectVO(returnId);
}
5.SQL语句
项目结构:
代码:
<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得到项目与回报的各种信息:
项目结构:
代码:
<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>
为了实现修改回报数量文本框中的内容,自动修改总价的功能:
项目结构:
代码:
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.跳转到确认订单的页面
1.思路
2.代码
1.创建AddressVO类
项目结构:
代码:
@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方法进行跳转操作
项目结构:
代码:
@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项目中声明接口
项目结构:
代码:
@RequestMapping("/get/address/vo/remote")
ResultEntity<List<AddressVO>> getAddressListByMemberIdRemote(@RequestParam("memberId") Integer memberId);
4.MySQL项目handler方法
项目结构:
代码:
@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接口
项目结构:
代码:
List<AddressVO> getAddressVOList(Integer memberId);
6.MySQL项目中service实现类
项目结构:
代码:
@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.前端页面
2.在确认订单页面显示需要的数据
1.显示收货信息
1.修改表单
项目结构:
代码:
<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方法
项目结构:
代码:
@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接口模块
项目结构:
代码:
@RequestMapping("/save/address/remote")
ResultEntity<String> saveAddressRemote(@RequestBody AddressVO addressVO);
4.mysql模块的远程方法
handler方法:
项目结构:
代码:
@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接口:
项目结构:
代码:
void saveAddressPO(AddressVO addressVO);
service实现方法:
项目结构:
代码:
@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.提交订单表单
项目结构:
构建页面不可见表单:
<!--建立空表单用于提交支付信息-->
<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();
});