前端整体结构
服务调用
会员系统总目标
- 搭建环境
- 会员登陆注册
- 发起众筹项目
- 展示众筹项目
- 支持众筹项目
- 订单
- 支付
会员系统架构
架构图
需要创建的工程
parent过程配置pom.xml
导入坐标
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-dependenciesartifactId> <version>Greenwich.SR2version> <type>pomtype> <scope>importscope> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-dependenciesartifactId> <version>2.1.6.RELEASEversion> <type>pomtype> <scope>importscope> dependency> <dependency> <groupId>org.mybatis.spring.bootgroupId> <artifactId>mybatis-spring-boot-starterartifactId> <version>2.1.0version> dependency> <dependency> <groupId>com.alibabagroupId> <artifactId>druidartifactId> <version>1.1.16version> dependency> dependencies> dependencyManagement>
搭建环境约定
端口号
eureka工程
导入坐标
<dependencies> <dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-starter-netflix-eureka-serverartifactId> dependency> dependencies>
配置文件
server:
port: 1000
spring:
application:
name: adom-crowd-eureka
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
主启动类
@EnableEurekaServer @SpringBootApplication public class CrowdApplication { public static void main(String[] args) { SpringApplication.run(CrowdApplication.class,args); } }
entity工程
创建包
lombok原理
相关注解
@Data:每一个字段都加入getxxx()、setxxx()方法
@NoargsConstructor:无参构造器
@AllArgsConstructor:全部字段都包括的构造器
@EqualsAndHashCode:equals和hashCode方法
@Getter
类:所有字段都加入getXxx()方法
字段:当前字段加入getXxx()方法
@Setter
类:所有字段都加入setXxx()方法
字段:当前字段加入setXxx()方法
MySQL工程基础环境
目标
抽取整个项目中所有针对数据库的操作.
创建数据库表
DROP TABLE IF EXISTS `t_member`; CREATE TABLE `t_member` ( `id` int(11) NOT NULL AUTO_INCREMENT, `login_acct` varchar(255) NOT NULL, `user_pswd` char(200) NOT NULL, `user_name` varchar(255) DEFAULT NULL, `email` varchar(255) DEFAULT NULL, `auth_status` int(4) DEFAULT NULL COMMENT '实名认证状态0-未实名认证,1-实名认证申请中,2-已实名认证', `user_type` int(4) DEFAULT NULL COMMENT '0-个人,1-企业', `real_name` varchar(255) DEFAULT NULL, `card_num` varchar(255) DEFAULT NULL, `acct_type` int(4) DEFAULT NULL COMMENT '0-企业,1-个体,2-个人,3-政府', PRIMARY KEY (`id`), UNIQUE KEY `login_acct` (`login_acct`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; SET FOREIGN_KEY_CHECKS = 1;
实体
Mapper
导入依赖
<dependencies> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-jdbcartifactId> dependency> <dependency> <groupId>org.mybatis.spring.bootgroupId> <artifactId>mybatis-spring-boot-starterartifactId> dependency> <dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-webartifactId> dependency> <dependency> <groupId>com.alibabagroupId> <artifactId>druidartifactId> dependency> <dependency> <groupId>mysqlgroupId> <artifactId>mysql-connector-javaartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-testartifactId> <scope>testscope> dependency> <dependency> <groupId>com.adomgroupId> <artifactId>member-entityartifactId> <version>1.0.0-SNAPSHOTversion> dependency> <dependency> <groupId>com.adomgroupId> <artifactId>utilartifactId> <version>1.0.0-SNAPSHOTversion> dependency> dependencies>
配置文件
server:
port: 2000
spring:
application:
name: adom-crowd-mysql
datasource:
name: mydb
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/campusfunding?serverTimezone=UTC
username: root
password: 11111111
driver-class-name: com.mysql.cj.jdbc.Driver
eureka:
client:
service-url:
defaultZone: http://localhost:1000/eureka
mybatis:
mapper-locations: classpath*:/mybatis/mapper/*Mapper.xml
# config-location: classpath:mybatis/mybatis-config.xml
logging:
level:
com.adom.crowd.mapper: debug
com.adom.crowd.test: debug
启动类
@EnableDiscoveryClient @SpringBootApplication @MapperScan("com.adom.crowd.mapper") public class CrowdApplication { public static void main(String[] args) { SpringApplication.run(CrowdApplication.class, args); } }
MYSQL工程API
API依赖
创建接口
@FeignClient("adom-crowd-mysql") public interface MySQLRemoteService { @RequestMapping("/get/memberpo/by/login/acct/remote") ResultEntitygetMemberPOByLoginAcctRemote(@RequestParam("loginacct") String loginacct); }
MySQL工程
controller代码
@RestController public class MemberProviderController { @Autowired private MemberService memberService; public ResultEntitygetMemberPOByLoginAcctReemote(@RequestParam("loginacct") String loginacct){ //1、调用本地service完成查询 MemberPO memberPO = memberService.getMemberPOByLoginAcct(loginacct); //2、如果没有抛异常,那么就返回成功的结果 return ResultEntity.sucessWithData(memberPO); } }
service代码
public MemberPO getMemberPOByLoginAcct(String loginacct) { //1、创建Example对象 MemberPOExample memberPOExample = new MemberPOExample(); //2、创建Criteria对象 MemberPOExample.Criteria criteria = memberPOExample.createCriteria(); //3、封装查询条件 criteria.andLoginAcctEqualTo(loginacct); //4、执行查询 ListmemberPOS = memberPOMapper.selectByExample(memberPOExample); //5、获取结果 return memberPOS.get(0); }
目标
抽取项目中所有访问Redis的操作
依赖
<dependencies> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-data-redisartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-testartifactId> <scope>testscope> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-webartifactId> dependency> <dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId> dependency> <dependency> <groupId>com.adomgroupId> <artifactId>member-entityartifactId> <version>1.0.0-SNAPSHOTversion> dependency> <dependency> <groupId>com.adomgroupId> <artifactId>utilartifactId> <version>1.0.0-SNAPSHOTversion> dependency> dependencies>
配置文件
server:
port: 3000
spring:
application:
name: adom-crowd-redis
redis:
host: localhost
eureka:
client:
service-url:
defaultZone: http://localhost:1000/eureka
启动类
@SpringBootApplication public class CrowdApplication { public static void main(String[] args) { SpringApplication.run(CrowdApplication.class,args); } }
Redis工程API
API工程接口
@FeignClient("com-adom-redis") public interface RedisRemoteService { @RequestMapping("/set/redis/key/value/remote") ResultEntitysetRedisKeyValueRemote( @RequestParam("key") String key, @RequestParam("value") String value); @RequestMapping("/set/redis/key/value/remote/with/timeout") ResultEntity setRedisKeyValueRemoteWithTimeout( @RequestParam("key") String key, @RequestParam("value") String value, @RequestParam("time") long time, @RequestParam("timeUnix") TimeUnit timeUnit); @RequestMapping("/get/redis/string/value/by/key") ResultEntity getRedisStringValueByKeyRemote(@RequestParam("key") String key); @RequestMapping("/remove/redis/key/remote") ResultEntity removeRedisKeyRemote(@RequestParam("key") String key); }
controller代码
使用StringRedisTemplate组件,操作Redis
@RestController public class RedisController { @Autowired private StringRedisTemplate redisTemplate; ResultEntitysetRedisKeyValueRemote( @RequestParam("key") String key, @RequestParam("value") String value ) { try { ValueOperations operations = redisTemplate.opsForValue(); operations.set(key, value); return ResultEntity.successWithoutData(); } catch (Exception e) { e.printStackTrace(); return ResultEntity.failed(e.getMessage()); } } @RequestMapping("/get/redis/string/value/by/key") ResultEntity getRedisStringValueByKeyRemote(@RequestParam("key") String key) { try { ValueOperations operations = redisTemplate.opsForValue(); String value = operations.get(key); return ResultEntity.sucessWithData(value); } catch (Exception e) { e.printStackTrace(); return ResultEntity.failed(e.getMessage()); } } ResultEntity removeRedisKeyRemote(@RequestParam("key") String key) { try { redisTemplate.delete(key); return ResultEntity.successWithoutData(); } catch (Exception e) { e.printStackTrace(); return ResultEntity.failed(e.getMessage()); } } }
认证工程显示首页
导入依赖
<dependencies> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-webartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-thymeleafartifactId> dependency> <dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId> dependency> <dependency> <groupId>com.adomgroupId> <artifactId>member-apiartifactId> <version>1.0-SNAPSHOTversion> dependency> dependencies>
配置文件
server:
port: 4000
spring:
application:
name: adom-crowd-auth
thymeleaf:
prefix: classpath:/templates/
suffix: .html
eureka:
client:
service-url:
defaultZone: http://localhost:1000/eureka
主启动类
@EnableDiscoveryClient @SpringBootApplication public class CrowdApplication { public static void main(String[] args) { SpringApplication.run(CrowdApplication.class, args); } }
显示首页的controller
@Controller public class PortalController { @RequestMapping("/") public String showPortalPage(){ return "portal"; } }
加入静态资源
Portal.html
DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<base th:href="@{/}">
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="css/font-awesome.min.css">
<link rel="stylesheet" href="css/carousel.css">
<style>
h3 {
font-weight: bold;
}
#footer {
padding: 15px 0;
background: #fff;
border-top: 1px solid #ddd;
text-align: center;
}
#topcontrol {
color: #fff;
z-index: 99;
width: 30px;
height: 30px;
font-size: 20px;
background: #222;
position: relative;
right: 14px !important;
bottom: 11px !important;
border-radius: 3px !important;
}
#topcontrol:after {
/*top: -2px;*/
left: 8.5px;
content: "\f106";
position: absolute;
text-align: center;
font-family: FontAwesome;
}
#topcontrol:hover {
color: #fff;
background: #18ba9b;
-webkit-transition: all 0.3s ease-in-out;
-moz-transition: all 0.3s ease-in-out;
-o-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
/* 侧栏导航 */
.sideBox {
padding: 10px;
height: 220px;
background: #fff;
margin-bottom: 10px;
overflow: hidden;
}
.sideBox .hd {
height: 30px;
line-height: 30px;
background: #f60;
padding: 0 10px;
text-align: center;
overflow: hidden;
}
.sideBox .hd .more {
color: #fff;
}
.sideBox .hd h3 span {
font-weight: bold;
font-size: 14px;
color: #fff;
}
.sideBox .bd {
padding: 5px 0 0;
}
#sideMenu .bd li {
margin-bottom: 2px;
height: 30px;
line-height: 30px;
text-align: center;
overflow: hidden;
}
#sideMenu .bd li a {
display: block;
background: #EAE6DD;
}
#sideMenu .bd li a:hover {
background: #D5CFBF;
}
/* 列表页 */
#mainBox {
margin-bottom: 10px;
padding: 10px;
background: #fff;
overflow: hidden;
}
#mainBox .mHd {
border-bottom: 2px solid #09c;
height: 30px;
line-height: 30px;
}
#mainBox .mHd h3 {
display: initial;
*display: inline;
zoom: 1;
padding: 0 15px;
background: #09c;
color: #fff;
}
#mainBox .mHd h3 span {
color: #fff;
font-size: 14px;
font-weight: bold;
}
#mainBox .path {
float: right;
}
/* 位置导航 */
.path {
height: 30px;
line-height: 30px;
padding-left: 10px;
}
.path a, .path span {
margin: 0 5px;
}
/* 文章列表 */
.newsList {
padding: 10px;
text-align: left;
}
.newsList li {
background: url("../images/share/point.png") no-repeat 2px 14px;
padding-left: 10px;
height: 30px;
line-height: 30px;
}
.newsList li a {
display: inline-block;
*display: inline;
zoom: 1;
font-size: 14px;
}
.newsList li .date {
float: right;
color: #999;
}
.newsList li.split {
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px dotted #ddd;
height: 0px;
line-height: 0px;
overflow: hidden;
}
/* 通用带图片的信息列表_普通式 */
.picList {
padding: 10px;
text-align: left;
}
.picList li {
margin: 0 5px;
height: 190px;
}
h3.break {
font-size: 16px;
display: block;
white-space: nowrap;
word-wrap: normal;
overflow: hidden;
text-overflow: ellipsis;
}
h3.break > a {
text-decoration: none;
}
style>
head>
<body>
<div class="navbar-wrapper">
<div class="container">
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="index.html" style="font-size:32px;">尚筹网-创意产品众筹平台a>
div>
<div id="navbar" class="navbar-collapse collapse" style="float:right;">
<ul class="nav navbar-nav navbar-right">
<li><a href="login.html" th:href="@{/auth/member/to/login/page}">登录a>li>
<li><a href="reg.html" th:href="@{/auth/member/to/reg/page}">注册a>li>
<li><a>|a>li>
<li><a href="admin-login.html">管理员入口a>li>
ul>
div>
div>
nav>
div>
div>
<div id="myCarousel" class="carousel slide" data-ride="carousel">
<ol class="carousel-indicators">
<li data-target="#myCarousel" data-slide-to="0" class="active">li>
<li data-target="#myCarousel" data-slide-to="1">li>
<li data-target="#myCarousel" data-slide-to="2">li>
ol>
<div class="carousel-inner" role="listbox">
<div class="item active" onclick="window.location.href='project.html'" style="cursor:pointer;">
<img src="img/carousel-1.jpg" alt="First slide">
div>
<div class="item" onclick="window.location.href='project.html'" style="cursor:pointer;">
<img src="img/carousel-2.jpg" alt="Second slide">
div>
<div class="item" onclick="window.location.href='project.html'" style="cursor:pointer;">
<img src="img/carousel-3.jpg" alt="Third slide">
div>
div>
<a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left">span>
<span class="sr-only">Previousspan>
a>
<a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
<span class="glyphicon glyphicon-chevron-right">span>
<span class="sr-only">Nextspan>
a>
div>
<div class="container marketing">
<div class="row">
<div class="col-lg-4">
<img class="img-circle" src="img/p1.jpg" alt="Generic placeholder image"
style="width: 140px; height: 140px;">
<h2>智能高清监控机器人h2>
<p>可爱的造型,摄像安防远程互联的全能设计,让你随时随地守护您的家人,陪伴你的生活。p>
<p><a class="btn btn-default" href="project.html" role="button">项目详情 »a>p>
div>
<div class="col-lg-4">
<img class="img-circle" src="img/p2.jpg" alt="Generic placeholder image"
style="width: 140px; height: 140px;">
<h2>NEOKA智能手环h2>
<p>要运动更要安全,这款、名为“蝶舞”的NEOKA-V9100智能运动手环为“安全运动而生”。p>
<p><a class="btn btn-default" href="project.html" role="button">项目详情 »a>p>
div>
<div class="col-lg-4">
<img class="img-circle" src="img/p3.png" alt="Generic placeholder image"
style="width: 140px; height: 140px;">
<h2>驱蚊扣h2>
<p>随处使用的驱蚊纽扣,<br>解决夏季蚊虫问题。p>
<p><a class="btn btn-default" href="project.html" role="button">项目详情 »a>p>
div>
div>
<div th:if="${#strings.isEmpty(portal_data)}">没有加载到任何废了信息div>
<div th:if="${not #strings.isEmpty(portal_data)}">
<div th:each="type : ${portal_data}" class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="box ui-draggable" id="mainBox">
<div class="mHd" style="">
<div class="path">
<a href="projects.html">更多...a>
div>
<h3>
<label th:text="${type.name}">科技label>
<small style="color:#FFF;"th:text="${type.remark}">开启智慧未来small>
h3>
div>
<div class="mBd" style="padding-top:10px;">
<div class="row">
<div th:if="${#strings.isEmpty(type.portalProjectVOList)}">没有任何项目信息div>
<div th:if="${not #strings.isEmpty(type.portalProjectVOList)}">
<div th:each="project : ${type.portalProjectVOList}" class="col-md-3">
<div class="thumbnail">
<img alt="300x200" th:src="${project.headPicturePath}" src="img/product-1.jpg"/>
<div class="caption">
<h3 class="break">
<a th:href="'http://localhost:80/project/get/project/detail/'+${project.projectId}" href="project.html" th:text="${project.projectName}">活性富氢净水直饮机a>
h3>
<p>
<div style="float:left;"><i class="glyphicon glyphicon-screenshot"
title="目标金额">i> $<span th:text="${project.money}">20,000span>
div>
<div style="float:right;"><i title="截至日期"
class="glyphicon glyphicon-calendar">i>
<span th:text="${project.deployDate}">2017-20-20span>
div>
p>
<br>
<div class="progress" style="margin-bottom: 4px;">
<div class="progress-bar progress-bar-success" role="progressbar"
aria-valuenow="40" th:aria-valuemin="${project.percentage}" aria-valuemin="0" aria-valuemax="100"
th:style="'width: '+${project.percentage}+'%'" style="width: 40%">
<span th:text="${project.percentage}+'%'">40% span>
div>
div>
<div><span style="float:right;"><i
class="glyphicon glyphicon-star-empty">i>span> <span><i
class="glyphicon glyphicon-user" title="支持人数">i> <span th:text="${project.supporter}">12345span>span>
div>
div>
div>
div>
div>
div>
div>
div>
div>
div>
div>
div>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div id="footer">
<div class="footerNav">
<a rel="nofollow" href="http://www.atguigu.com">关于我们a> | <a rel="nofollow"
href="http://www.atguigu.com">服务条款a>
| <a rel="nofollow" href="http://www.atguigu.com">免责声明a> | <a rel="nofollow"
href="http://www.atguigu.com">网站地图a>
| <a rel="nofollow" href="http://www.atguigu.com">联系我们a>
div>
<div class="copyRight">
Copyright ?2017-2017atguigu.com 版权所有
div>
div>
div>
div>
div>
div>
<script src="jquery/jquery-2.1.1.min.js">script>
<script src="bootstrap/js/bootstrap.min.js">script>
<script src="script/docs.min.js">script>
<script src="script/back-to-top.js">script>
<script>
$(".thumbnail img").css("cursor", "pointer");
$(".thumbnail img").click(function () {
window.location.href = "project.html";
});
script>
body>
html>
网关工程
依赖
<dependencies> <dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId> dependency> <dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-starter-netflix-zuulartifactId> dependency> dependencies>
主启动类
@EnableZuulProxy @SpringBootApplication public class CrowdApplication { public static void main(String[] args) { SpringApplication.run(CrowdApplication.class, args); } }
配置文件
server:
port: 80
spring:
application:
name: adom-crowd-zuul
eureka:
client:
service-url:
defaultZone: http://localhost:1000/eureka
zuul:
ignored-services: "*"
sensitive-headers: "*" #在Zuul向其他微服务重定向时保持原本头信息(请求头、响应头)
routes:
crowd-portal:
service-id: adom-crowd-auth
path: /** #这里一定要使用两个"*"号,不然"/"路径后面的多层路径将无法访问
配置域名
访问效果
在浏览器输入域名