天天看點

day15【前台】項目釋出day15【前台】項目釋出

day15【前台】項目釋出

1、OSS

1.1、開通OSS服務

  • 進入控制台,選擇【對象存儲OSS】
day15【前台】項目釋出day15【前台】項目釋出
  • 立即開通
day15【前台】項目釋出day15【前台】項目釋出
  • 同意協定
day15【前台】項目釋出day15【前台】項目釋出
  • 開通完成後,前往控制台建立

    Bucket

day15【前台】項目釋出day15【前台】項目釋出

1.2、建立Bucket

  • 建立

    Bucket

day15【前台】項目釋出day15【前台】項目釋出
  • Bucket

    名稱
  • 設定區域為附近區域
  • 設定為低頻通路存儲,适用于較少通路的服務
  • 不開通【版本控制】
  • 讀寫權限設定為公共讀:讀取無需登入,寫入需要登入
day15【前台】項目釋出day15【前台】項目釋出

1.3、頁面上傳檔案

  • 建立目錄用于存儲檔案
day15【前台】項目釋出day15【前台】項目釋出
  • 建立多級目錄
day15【前台】項目釋出day15【前台】項目釋出
  • 選擇上傳檔案
day15【前台】項目釋出day15【前台】項目釋出
  • 上傳本地圖檔至阿裡雲

    OSS

day15【前台】項目釋出day15【前台】項目釋出
day15【前台】項目釋出day15【前台】項目釋出
  • 圖檔的位址:

    Bucket

    域名+圖檔路徑
day15【前台】項目釋出day15【前台】項目釋出

1.4、建立子使用者AccessKey

  • 一般我們不使用主賬戶(登入賬戶)來調用 API 接口,因為主賬戶的權限太大,一旦 key secret 洩露,就玩完。。。
  • 一般建立子賬戶來調用 OSS API 接口,點選使用者頭像,選擇 AccessKey管理
day15【前台】項目釋出day15【前台】項目釋出
  • 選擇開始使用子使用者 AccessKey
day15【前台】項目釋出day15【前台】項目釋出
  • 設計登入名稱和顯示名稱,選擇程式設計通路(自己編寫的程式調用或 PicGo 等第三方程式調用)
day15【前台】項目釋出day15【前台】項目釋出
  • Key Secret

    貌似僅此一次機會可以檢視,趕緊儲存下來
day15【前台】項目釋出day15【前台】項目釋出

1.5、子使用者AccessKey授權

  • 給建立的子使用者添權重限
day15【前台】項目釋出day15【前台】項目釋出
  • 添加 OSS 服務:AliyunOSSFullAccess ,FullAccess 表示可讀可寫
day15【前台】項目釋出day15【前台】項目釋出

1.6、project-consumer環境

1.6.1、引入依賴

  • project-consumer

    工程中引入所需的依賴
day15【前台】項目釋出day15【前台】項目釋出
<dependencies>
    <!-- api工程 -->
    <dependency>
        <groupId>com.atguigu.crowd</groupId>
        <artifactId>atcrowdfunding17-member-api</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>

    <!-- yml配置檔案智能提示 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>

    <!-- web标配 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- 單元測試 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <!-- thymeleaf模闆引擎 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <!-- eureka-client -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

    <!-- 引入springboot&redis整合場景 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!-- 引入springboot&springsession整合場景 -->
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
    </dependency>
</dependencies>
           

1.6.2、建立主啟動類

day15【前台】項目釋出day15【前台】項目釋出
//啟用Feign用戶端功能
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class CrowdMainClass {
	
	public static void main(String[] args) {
		SpringApplication.run(CrowdMainClass.class, args);
	}

}
           

1.6.3、準備OSSProperties類

  • 建立

    OSSProperties

    配置類
day15【前台】項目釋出day15【前台】項目釋出
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class OSSProperties {
	
	private String endPoint;
	private String bucketName;
	private String accessKeyId;
	private String accessKeySecret;
	private String bucketDomain;

}
           

1.6.4、建立yml配置檔案

  • 微服務端口号
  • 微服務名稱
  • Thymeleaf

    模闆引擎
  • SpringSession

    相關配置
  • 指定

    Eureka

    注冊中心的位址
  • 阿裡雲

    OSS

    基本配置
day15【前台】項目釋出day15【前台】項目釋出
server:
  port: 5000
  
spring:
  application:
    name: atguigu-crowd-project
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
  redis:
    host: 192.168.152.129
  session:
    store-type: redis

eureka:
  client:
    service-url:
      defaultZone: http://localhost:1000/eureka
      
aliyun:
  oss:
    access-key-id: <填入你的access-key-id>
    access-key-secret: <填入你的access-key-secret>
    bucket-domain: <填入你的bucket-domain>
    bucket-name: <填入你的bucket-name>
    end-point: <填入你的end-point>
           
  • bucket-domain

    end-point

    去阿裡雲控制台複制,

    bucket-name

    就是

    heygo

    (當初建立

    Bucket

    時取的名字)
day15【前台】項目釋出day15【前台】項目釋出

1.7、上傳檔案工具方法

1.7.1、引入依賴

  • util

    工程引入阿裡雲

    OSS

    服務所需的依賴
day15【前台】項目釋出day15【前台】項目釋出
<!-- OSS用戶端SDK -->
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.5.0</version>
</dependency>
           

1.7.2、建立工具方法

  • 在工具類中添加如下方法
day15【前台】項目釋出day15【前台】項目釋出
/**
 * 專門負責上傳檔案到OSS伺服器的工具方法
 * @param endpoint			OSS參數
 * @param accessKeyId		OSS參數
 * @param accessKeySecret	OSS參數
 * @param inputStream		要上傳的檔案的輸入流
 * @param bucketName		OSS參數
 * @param bucketDomain		OSS參數
 * @param originalName		要上傳的檔案的原始檔案名
 * @return	包含上傳結果以及上傳的檔案在OSS上的通路路徑
 */
public static ResultEntity<String> uploadFileToOss(
		String endpoint, 
		String accessKeyId, 
		String accessKeySecret,
		InputStream inputStream,
		String bucketName,
		String bucketDomain,
		String originalName) {
	
	// 建立OSSClient執行個體。
	OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
	
	// 生成上傳檔案的目錄
	String folderName = new SimpleDateFormat("yyyyMMdd").format(new Date());
	
	// 生成上傳檔案在OSS伺服器上儲存時的檔案名
	// 原始檔案名:beautfulgirl.jpg
	// 生成檔案名:wer234234efwer235346457dfswet346235.jpg
	// 使用UUID生成檔案主體名稱
	String fileMainName = UUID.randomUUID().toString().replace("-", "");
	
	// 從原始檔案名中擷取檔案擴充名
	String extensionName = originalName.substring(originalName.lastIndexOf("."));
	
	// 使用目錄、檔案主體名稱、檔案擴充名稱拼接得到對象名稱
	String objectName = folderName + "/" + fileMainName + extensionName;
	
	try {
		// 調用OSS用戶端對象的方法上傳檔案并擷取響應結果資料
		PutObjectResult putObjectResult = ossClient.putObject(bucketName, objectName, inputStream);
		
		// 從響應結果中擷取具體響應消息
		ResponseMessage responseMessage = putObjectResult.getResponse();
		
		// 根據響應狀态碼判斷請求是否成功
		if(responseMessage == null) {
			
			// 拼接通路剛剛上傳的檔案的路徑
			String ossFileAccessPath = bucketDomain + "/" + objectName;
			
			// 目前方法傳回成功
			return ResultEntity.successWithData(ossFileAccessPath);
		} else {
			// 擷取響應狀态碼
			int statusCode = responseMessage.getStatusCode();
			
			// 如果請求沒有成功,擷取錯誤消息
			String errorMessage = responseMessage.getErrorResponseAsString();
			
			// 目前方法傳回失敗
			return ResultEntity.failed("目前響應狀态碼="+statusCode+" 錯誤消息="+errorMessage);
		}
	} catch (Exception e) {
		e.printStackTrace();
		
		// 目前方法傳回失敗
		return ResultEntity.failed(e.getMessage());
	} finally {
		
		if(ossClient != null) {
			
			// 關閉OSSClient。
			ossClient.shutdown();
		}
	}
	
}
           

1.8、測試

1.8.1、建立測試類

day15【前台】項目釋出day15【前台】項目釋出
@RunWith(SpringRunner.class)
@SpringBootTest
public class OSSTest {

	@Autowired
	private OSSProperties oSSProperties;

	@Test
	public void testOSS() throws FileNotFoundException {

		FileInputStream inputStream = new FileInputStream("SpongeBob.jpg");

		ResultEntity<String> resultEntity = CrowdUtil.uploadFileToOss(
				oSSProperties.getEndPoint(),
				oSSProperties.getAccessKeyId(), 
				oSSProperties.getAccessKeySecret(), 
				inputStream,
				oSSProperties.getBucketName(), 
				oSSProperties.getBucketDomain(), 
				"SpongeBob.jpg");

		System.out.println(resultEntity.getResult());
		
	}
	
}
           
  • 圖檔放置路徑:工程根目錄下
day15【前台】項目釋出day15【前台】項目釋出

1.8.2、測試結果

  • 控制台列印
SUCCESS
           
  • OSS

    上傳圖檔成功
day15【前台】項目釋出day15【前台】項目釋出

2、跳轉到發起項目頁面

2.1、整體思路

day15【前台】項目釋出day15【前台】項目釋出

2.2、解決Zuul相關問題

2.2.1、序列化

  • SpringSession

    存儲對象至

    Redis

    中,要求該類必須是可序列化的,否則會抛異常
day15【前台】項目釋出day15【前台】項目釋出
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MemberLoginVO implements Serializable {
	
	private static final long serialVersionUID = 1L;

	private Integer id;
	
    private String username;
	
	private String email;
	
}
           

2.2.2、session中資料莫名消失

1、問題描述
  • 明明 在登陸成功之後,将

    MemberLoginVO

    對象存入了

    session

    ,卻取不出來,提示

    session

    null

org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "session.loginMember.username" (template: "member-center" - line 69, col 121)
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'username' cannot be found on null
           
day15【前台】項目釋出day15【前台】項目釋出
2、問題分析
  • auth-consumer

    所屬域名為

    http://localhost:4000

    zuul

    所屬域名為

    http://localhost:80

    ,他他倆屬于不同的域名, 浏覽器工作時不會使用相同的

    Cookie

    ,自然

    session

    就不一樣(不共享)咯
3、解決方法
  • 以後重定向的位址都按照通過

    Zuul

    通路的方式寫位址
day15【前台】項目釋出day15【前台】項目釋出
@RequestMapping("/auth/member/do/login")
public String login(
    @RequestParam("loginacct") String loginacct, 
    @RequestParam("userpswd") String userpswd,
    ModelMap modelMap,
    HttpSession session) {

    // 1.調用遠端接口根據登入賬号查詢MemberPO對象
    ResultEntity<MemberPO> resultEntity = 
        mySQLRemoteService.getMemberPOByLoginAcctRemote(loginacct);

    if(ResultEntity.FAILED.equals(resultEntity.getResult())) {

        modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, resultEntity.getMessage());

        return "member-login";

    }

    MemberPO memberPO = resultEntity.getData();

    if(memberPO == null) {
        modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_LOGIN_FAILED);

        return "member-login";
    }

    // 2.比較密碼
    String userpswdDataBase = memberPO.getUserpswd();

    BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    boolean matcheResult = passwordEncoder.matches(userpswd, userpswdDataBase);

    if(!matcheResult) {
        modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_LOGIN_FAILED);

        return "member-login";
    }

    // 3.建立MemberLoginVO對象存入Session域
    MemberLoginVO memberLoginVO = new MemberLoginVO(memberPO.getId(), memberPO.getUsername(), memberPO.getEmail());
    session.setAttribute(CrowdConstant.ATTR_NAME_LOGIN_MEMBER, memberLoginVO);

    // 使用重定向避免重新整理浏覽器導緻重新執行注冊流程
    // 以後重定向的位址都按照通過 Zuul通路的方式寫位址 
    return "redirect:http://www.crowd.com/auth/member/to/center/page";
}
           
// 使用重定向避免重新整理浏覽器導緻重新執行注冊流程
// 以後重定向的位址都按照通過 Zuul通路的方式寫位址 
return "redirect:http://www.crowd.com/auth/member/to/center/page";
           
  • 這裡隻是以一個

    Handler

    方法作為示例,在工程中所有重定向的前面,都加上域名:

    http://www.crowd.com

2.2.3、頁面跳轉位址

1、前置域名問題
  • 需要注意: 前面要寫上域名(如果沒有配置域名寫

    localhost

    一樣), 確定通過

    Zuul

    通路具體功能。
  • 因為必須通過

    Zuul

    通路具體微服務才能夠保持

    Cookie

    , 進而保持

    Session

    一緻
2、Cookie之Domain
  • Domain

    保持一緻,

    Cookie

    才能保持一緻,

    session

    才能保持一緻
day15【前台】項目釋出day15【前台】項目釋出
day15【前台】項目釋出day15【前台】項目釋出
3、舉例
  • 【退出系統】按鈕的超連結
day15【前台】項目釋出day15【前台】項目釋出
  • 退出成功後,頁面重定向至

    http://www.crowd.com

day15【前台】項目釋出day15【前台】項目釋出

2.2.4、ClassNotFoundException

1、問題描述
  • ClassNotFoundException

    異常
Caused by: java.lang.ClassNotFoundException: com.atguigu.crowd.entity.vo.MemberLoginVO
           
2、引入依賴
  • ZuulFilter

    中會從

    Redis

    中讀取

    MemberLoginVO

    對象的資訊,并進行反序列化,是以我們需要在

    zuul

    工程中引入

    entity

    工程的依賴
<!-- entity實體類工程 -->
<dependency>
    <groupId>com.atguigu.crowd</groupId>
    <artifactId>atcrowdfunding09-member-entity</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>
           

2.2.5、測試

  • 正常通路~~~并且域名是

    www.crowd.com

day15【前台】項目釋出day15【前台】項目釋出

2.3、我的衆籌

2.3.1、修改超連結

  • 修改【我的衆籌】超連結
day15【前台】項目釋出day15【前台】項目釋出
<div class="list-group-item " style="cursor:pointer;">
	<a th:href="@{/member/my/crowd}">我的衆籌</a><span class="badge"><i class="glyphicon glyphicon-chevron-right"></i></span>
</div>
           

2.3.2、添加view-controller

  • 通過

    view-controller

    進行跳轉,

    /member/my/crowd

    位址對應着

    templates/member-crowd.html

    頁面
day15【前台】項目釋出day15【前台】項目釋出
@Configuration
public class CrowdWebMvcConfig implements WebMvcConfigurer {
	
	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		
		// 添加view-controller
		// 浏覽器通路的位址	
		// 目标視圖的名稱,将來拼接“prefix: classpath:/templates/”、“suffix: .html”前字尾
		
		registry.addViewController("/auth/member/to/reg/page").setViewName("member-reg");
		registry.addViewController("/auth/member/to/login/page").setViewName("member-login");
		registry.addViewController("/auth/member/to/center/page").setViewName("member-center");
		registry.addViewController("/member/my/crowd").setViewName("member-crowd");
		
	}

}
           

2.3.3、衆籌頁面

  • 添加衆籌頁面,并添加使用者資訊的回顯
day15【前台】項目釋出day15【前台】項目釋出

2.3.4、測試

  • 點選【我的衆籌】,正常跳轉
day15【前台】項目釋出day15【前台】項目釋出

3、發起項目

3.1、整體思路

day15【前台】項目釋出day15【前台】項目釋出

3.2、建立資料庫表

3.2.1、分類表

  • 分類表用于存儲項目的分類資訊
CREATE TABLE t_type (
  id INT (11) NOT NULL AUTO_INCREMENT,
  NAME VARCHAR (255) COMMENT 		'分類名稱',
  remark VARCHAR (255) COMMENT 		'分類介紹',
  PRIMARY KEY (id)
)
           

3.2.2、項目分類中間表

  • 項目分類中間表用于存儲項目與分類之間的對應關系
CREATE TABLE t_project_type (
  id INT NOT NULL AUTO_INCREMENT,
  projectid INT (11),
  typeid INT (11),
  PRIMARY KEY (id)
) ;
           

3.2.3、标簽表

  • 标簽表用于存儲項目的标簽資訊
create table t_tag (
  id int (11) not null auto_increment,
  pid int (11),
  name varchar (255),
  primary key (id)
) ;
           

3.2.4、項目标簽中間表

  • 項目标簽中間表用于存儲項目與标簽之間的對應關系
CREATE TABLE t_project_tag (
  id INT (11) NOT NULL AUTO_INCREMENT,
  projectid INT (11),
  tagid INT (11),
  PRIMARY KEY (id)
) ;
           

3.2.5、項目表

  • 項目表用于存儲項目的詳細資訊
CREATE TABLE t_project (
  id INT (11) NOT NULL AUTO_INCREMENT,
  project_name VARCHAR (255) COMMENT 		'項目名稱',
  project_description VARCHAR (255) COMMENT 	'項目描述',
  money BIGINT (11) COMMENT 			'籌集金額',
  DAY INT (11) COMMENT 				'籌集天數',
  STATUS INT (4) COMMENT 			'0-即将開始, 1-衆籌中, 2-衆籌成功, 3-衆籌失敗',
  deploydate VARCHAR (10) COMMENT 		'項目發起時間',
  supportmoney BIGINT (11) COMMENT 		'已籌集到的金額',
  supporter INT (11) COMMENT 			'支援人數',
  COMPLETION INT (3) COMMENT 			'百分比完成度',
  memberid INT (11) COMMENT 			'發起人的會員 id',
  createdate VARCHAR (19) COMMENT 		'項目建立時間',
  follower INT (11) COMMENT 			'關注人數',
  header_picture_path VARCHAR (255) COMMENT 	'頭圖路徑',
  PRIMARY KEY (id)
) ;
           

3.2.6、項目詳情圖檔表

  • 由于使用者可以上傳多張項目詳情圖檔,是以我們單獨建個表存儲項目詳情圖檔
CREATE TABLE t_project_item_pic (
  id INT (11) NOT NULL AUTO_INCREMENT,
  projectid INT (11),
  item_pic_path VARCHAR (255),
  PRIMARY KEY (id)
) ;
           

3.2.7、項目發起人資訊表

  • 項目發起人資訊表用于存儲項目發起人的詳細資訊
create table t_member_launch_info (
  id int (11) not null auto_increment,
  memberid int (11) comment 			'會員 id',
  description_simple varchar (255) comment 	'簡單介紹',
  description_detail varchar (255) comment 	'詳細介紹',
  phone_num varchar (255) comment 		'聯系電話',
  service_num varchar (255) comment 		'客服電話',
  primary key (id)
) ;
           

3.2.8、回報資訊表

  • 回報資訊表用于存儲衆籌的回報資訊
CREATE TABLE t_return (
  id INT (11) NOT NULL AUTO_INCREMENT,
  projectid INT (11),
  TYPE INT (4) COMMENT 				'0 - 實物回報, 1 虛拟物品回報',
  supportmoney INT (11) COMMENT 		'支援金額',
  content VARCHAR (255) COMMENT 		'回報内容',
  COUNT INT (11) COMMENT 			'回報産品限額, 0為不限回報數量',
  signalpurchase INT (11) COMMENT 		'是否設定單筆限購',
  purchase INT (11) COMMENT 			'具體限購數量',
  freight INT (11) COMMENT 			'運費, 0為包郵',
  invoice INT (4) COMMENT 			'0 - 不開發票, 1 - 開發票',
  returndate INT (11) COMMENT 			'項目結束後多少天向支援者發送回報',
  describ_pic_path VARCHAR (255) COMMENT 	'說明圖檔路徑',
  PRIMARY KEY (id)
) ;
           

3.2.9、發起人确認資訊表

  • 發起人确認資訊表用于存儲發起人的詳細資訊
CREATE TABLE t_member_confirm_info (
  id INT (11) NOT NULL AUTO_INCREMENT,
  memberid INT (11) COMMENT 	'會員 id',
  paynum VARCHAR (200) COMMENT 	'易付寶企業賬号',
  cardnum VARCHAR (200) COMMENT '法人身份證号',
  PRIMARY KEY (id)
)
           

3.3、逆向工程

3.3.1、逆向工程配置檔案

  • 注意:不生成中間表對應的實體類
day15【前台】項目釋出day15【前台】項目釋出
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
			  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
			  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
	<!-- mybatis-generator:generate -->
	<context id="atguiguTables" targetRuntime="MyBatis3">
		<commentGenerator>
			<!-- 是否去除自動生成的注釋 true:是;false:否 -->
			<property name="suppressAllComments" value="true" />
		</commentGenerator>

		<!--資料庫連接配接的資訊:驅動類、連接配接位址、使用者名、密碼 -->
		<jdbcConnection 
			driverClass="com.mysql.jdbc.Driver"
			connectionURL="jdbc:mysql://localhost:3306/project_crowd" 
			userId="root"
			password="root">
		</jdbcConnection>

		<!-- 預設false,把JDBC DECIMAL 和 NUMERIC 類型解析為 Integer,為 true時把JDBC DECIMAL 
			和 NUMERIC 類型解析為java.math.BigDecimal -->
		<javaTypeResolver>
			<property name="forceBigDecimals" value="false" />
		</javaTypeResolver>

		<!-- targetProject:生成Entity類的路徑 -->
		<javaModelGenerator targetProject=".\src\main\java"
			targetPackage="com.atguigu.crowd.entity.po">
			<!-- enableSubPackages:是否讓schema作為包的字尾 -->
			<property name="enableSubPackages" value="false" />
			<!-- 從資料庫傳回的值被清理前後的空格 -->
			<property name="trimStrings" value="true" />
		</javaModelGenerator>

		<!-- targetProject:XxxMapper.xml映射檔案生成的路徑 -->
		<sqlMapGenerator targetProject=".\src\main\java"
			targetPackage="com.atguigu.crowd.mapper">
			<!-- enableSubPackages:是否讓schema作為包的字尾 -->
			<property name="enableSubPackages" value="false" />
		</sqlMapGenerator>

		<!-- targetPackage:Mapper接口生成的位置 -->
		<javaClientGenerator type="XMLMAPPER"
			targetProject=".\src\main\java"
			targetPackage="com.atguigu.crowd.mapper">
			<!-- enableSubPackages:是否讓schema作為包的字尾 -->
			<property name="enableSubPackages" value="false" />
		</javaClientGenerator>

		<!-- 資料庫表名字和我們的entity類對應的映射指定 -->
		<table tableName="t_type" domainObjectName="TypePO" />
		<table tableName="t_tag" domainObjectName="TagPO" />
		<table tableName="t_project" domainObjectName="ProjectPO" />
		<table tableName="t_project_item_pic" domainObjectName="ProjectItemPicPO" />
		<table tableName="t_member_launch_info" domainObjectName="MemberLaunchInfoPO" />
		<table tableName="t_return" domainObjectName="ReturnPO" />
		<table tableName="t_member_confirm_info" domainObjectName="MemberConfirmInfoPO" />

	</context>
</generatorConfiguration>
           
<javaModelGenerator targetProject=".\src\main\java"
			targetPackage="com.atguigu.crowd.entity.po">
    ...
</javaModelGenerator>
           
<!-- 資料庫表名字和我們的entity類對應的映射指定 -->
<table tableName="t_type" domainObjectName="TypePO" />
<table tableName="t_tag" domainObjectName="TagPO" />
<table tableName="t_project" domainObjectName="ProjectPO" />
<table tableName="t_project_item_pic" domainObjectName="ProjectItemPicPO" />
<table tableName="t_member_launch_info" domainObjectName="MemberLaunchInfoPO" />
<table tableName="t_return" domainObjectName="ReturnPO" />
<table tableName="t_member_confirm_info" domainObjectName="MemberConfirmInfoPO" />
           

3.3.2、執行逆向工程

  • Maven Build

    指令:

    mybatis-generator:generate

day15【前台】項目釋出day15【前台】項目釋出

3.3.3、資源歸位

1、實體類
day15【前台】項目釋出day15【前台】項目釋出
2、Mapper接口
day15【前台】項目釋出day15【前台】項目釋出
3、Mapper.xml映射檔案
day15【前台】項目釋出day15【前台】項目釋出

3.4、建立元件

3.4.1、ProjectHandler

day15【前台】項目釋出day15【前台】項目釋出

3.4.2、ProjectService

day15【前台】項目釋出day15【前台】項目釋出

3.5、建立VO類

day15【前台】項目釋出day15【前台】項目釋出
  • ProjectVO

    :項目詳細資訊
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ProjectVO implements Serializable {
	
	private static final long serialVersionUID = 1L;	
	// 分類id集合
	private List<Integer> typeIdList;
	
	// 标簽id集合
	private List<Integer> tagIdList;
	
	// 項目名稱
	private String projectName;
	
	// 項目描述
	private String projectDescription;
	
	// 計劃籌集的金額
	private Integer money;
	
	// 籌集資金的天數
	private Integer day;
	
	// 建立項目的日期
	private String createdate;
	
	// 頭圖的路徑
	private String headerPicturePath;
	
	// 詳情圖檔的路徑
	private List<String> detailPicturePathList;
	
	// 發起人資訊
	private MemberLauchInfoVO memberLauchInfoVO;
	
	// 回報資訊集合
	private List<ReturnVO> returnVOList;
	
	// 發起人确認資訊
	private MemberConfirmInfoVO memberConfirmInfoVO;
}
           
  • MemberLauchInfoVO

    :項目發起人資訊
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MemberLauchInfoVO  implements Serializable {
	
	private static final long serialVersionUID = 1L;	
	// 簡單介紹
	private String descriptionSimple;
	
	// 詳細介紹
	private String descriptionDetail;
	
	// 聯系電話
	private String phoneNum;
	
	// 客服電話
	private String serviceNum;

}
           
  • ReturnVO

    :回報資訊
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ReturnVO  implements Serializable {
	
	private static final long serialVersionUID = 1L;
	
	// 回報類型:0 - 實物回報, 1 虛拟物品回報
	private Integer type;
	
	// 支援金額
	private Integer supportmoney;
	
	// 回報内容介紹
	private String content;
	
	// 總回報數量,0為不限制
	private Integer count;
	
	// 是否限制單筆購買數量,0表示不限購,1表示限購
	private Integer signalpurchase;
	
	// 如果單筆限購,那麼具體的限購數量
	private Integer purchase;
	
	// 運費,“0”為包郵
	private Integer freight;
	
	// 是否開發票,0 - 不開發票, 1 - 開發票
	private Integer invoice;
	
	// 衆籌結束後返還回報物品天數
	private Integer returndate;
	
	// 說明圖檔路徑
	private String describPicPath;

}
           
  • MemberConfirmInfoVO

    :發起人确認資訊
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MemberConfirmInfoVO implements Serializable {
	
	private static final long serialVersionUID = 1L;
	
	// 易付寶賬号
	private String paynum;

	// 法人身份證号
	private String cardnum;
}
           

3.6、zuul路由規則

  • zuul

    中配置

    atguigu-crowd-project

    的路由規則:

    /project/**

  • 注意:
    • 如果一個請求需要經過

      zuul

      ,請求路徑必須帶上

      /project

      部分
    • 如果一個請求無需經過

      zuul

      ,隻在

      atguigu-crowd-project

      微服務中做轉發(比如配置

      view-controller

      等),那麼請求路徑就不能帶

      /project

      部分
day15【前台】項目釋出day15【前台】項目釋出
zuul:
  ignored-services: "*"
  sensitive-headers: "*"        # 在Zuul向其他微服務重定向時保持原本頭資訊(請求頭、響應頭)
  routes:
    crowd-portal:
      service-id: atguigu-crowd-auth
      path: /**                 # 這裡一定要使用兩個“*”号,不然“/”路徑後面的多層路徑将無法通路
    crowd-project:
      service-id: atguigu-crowd-project
      path: /project/**         # 這裡一定要使用兩個“*”号,不然“/”路徑後面的多層路徑将無法通路
           

3.7、跳轉至發起項目頁面

3.7.1、點選發起衆籌

  • 修改【發起衆籌】按鈕的跳轉位址
    • window.location.href = "http://www.crowd.com/project/agree/protocol/page";

      表示浏覽器的跳轉位址
    • 一定要經過

      zuul

      網關再進行跳轉,否則會出現

      session

      不一緻的問題
day15【前台】項目釋出day15【前台】項目釋出
<li class=" pull-right">
    <script type="text/javascript">
        $(function(){
            $("#launchCrowdBtn").click(function(){
                window.location.href = "http://www.crowd.com/project/agree/protocol/page";
            });
        });
    </script>
    <button id="launchCrowdBtn" type="button"
            class="btn btn-warning">發起衆籌</button></li>
           

3.7.2、配置view-controller

  • project-consumer

    中配置

    view-controller

  • 因為

    view-controller

    是在

    project-consumer

    内部定義的,是以這裡是一個不經過

    Zuul

    通路的位址,是以這個路徑前面不加路由規則中定義的字首:

    /project

day15【前台】項目釋出day15【前台】項目釋出
@Configuration
public class CrowdWebMvcConfig implements WebMvcConfigurer {
	
	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		
		// view-controller是在project-consumer内部定義的,是以這裡是一個不經過Zuul通路的位址,是以這個路徑前面不加路由規則中定義的字首:“/project”
		registry.addViewController("/agree/protocol/page").setViewName("project-agree");
		registry.addViewController("/launch/project/page").setViewName("project-launch");
		
	}

}
           

3.7.3、使用者協定頁面

  • project-consumer

    工程中,建立使用者協定頁面、發起項目頁面
day15【前台】項目釋出day15【前台】項目釋出

3.7.4、測試

  • 點選【發起衆籌】跳轉至【使用者協定】頁面
  • 再點選【閱讀并同意協定】,則可以跳轉至發起項目頁面
day15【前台】項目釋出day15【前台】項目釋出

3.8、常量定義

public class CrowdConstant {
	
	public static final String MESSAGE_LOGIN_FAILED = "抱歉!賬号密碼錯誤!請重新輸入!";
	public static final String MESSAGE_LOGIN_ACCT_ALREADY_IN_USE = "抱歉!這個賬号已經被使用了!";
	public static final String MESSAGE_ACCESS_FORBIDEN = "請登入以後再通路!";
	public static final String MESSAGE_STRING_INVALIDATE = "字元串不合法!請不要傳入空字元串!";
	public static final String MESSAGE_SYSTEM_ERROR_LOGIN_NOT_UNIQUE = "系統錯誤:登入賬号不唯一!";
	public static final String MESSAGE_ACCESS_DENIED = "抱歉!您不能通路這個資源!";
	public static final String MESSAGE_CODE_NOT_EXISTS = "驗證碼已過期!請檢查手機号是否正确或重新發送!";
	public static final String MESSAGE_CODE_INVALID = "驗證碼不正确!";
	public static final String MESSAGE_HEADER_PIC_UPLOAD_FAILED = "頭圖上傳失敗!";
	public static final String MESSAGE_HEADER_PIC_EMPTY = "頭圖不可為空!";
	public static final String MESSAGE_DETAIL_PIC_EMPTY = "詳情圖檔不可為空!";
	public static final String MESSAGE_DETAIL_PIC_UPLOAD_FAILED = "詳情圖檔上傳失敗!";
	public static final String MESSAGE_TEMPLE_PROJECT_MISSING = "臨時存儲的Project對象丢失!";
	
	public static final String ATTR_NAME_EXCEPTION = "exception";
	public static final String ATTR_NAME_LOGIN_ADMIN = "loginAdmin";
	public static final String ATTR_NAME_LOGIN_MEMBER = "loginMember";
	public static final String ATTR_NAME_PAGE_INFO = "pageInfo";
	public static final String ATTR_NAME_MESSAGE = "message";
	public static final String ATTR_NAME_TEMPLE_PROJECT = "tempProject";
	
	public static final String REDIS_CODE_PREFIX = "REDIS_CODE_PREFIX_";

}
           

3.9、送出項目詳情資訊

3.9.1、目标

  • 将如下表單資訊封裝到

    ProjectVO

    對象中,儲存至

    session

    域中,以便後續收集完成,儲存至資料庫中
day15【前台】項目釋出day15【前台】項目釋出

3.9.2、前端表單送出代碼

  • 标簽資訊需要單獨采集,最後以隐藏域的形式追加在表單資料後面
day15【前台】項目釋出day15【前台】項目釋出
// 點選下一步按鈕送出表單
$("#submitBtn").click(function(){
	
	// 将表單中标簽id的值組成的數組轉換成表單内的隐藏域
	for(var i = 0; i < tagIdList.length; i++) {
		var tagId = tagIdList[i];
		
		var hiddenInputHTML = "<input type='hidden' name='tagIdList' value='"+tagId+"' />";
		
		$("#projectForm").append(hiddenInputHTML);
	}
	
	// 送出表單
	$("#projectForm").submit();
});
           

3.9.3、Handler方法

  • 建立

    ProjectConsumerHandler

    ,用于接收表單送出的資料
    • 接收表單送出的

      ProjectVO

      對象
    • 接收上傳的頭圖,上傳至

      OSS

      ,并設定圖檔的網絡路徑
    • 接收項目的詳情圖檔,上傳至

      OSS

      ,并設定圖檔的網絡路徑
    • ProjectVO

      對象存入

      session

      域中,并轉發至回報頁面
day15【前台】項目釋出day15【前台】項目釋出
@Controller
public class ProjectConsumerHandler {
	
	@Autowired
	private OSSProperties ossProperties;
	
	@RequestMapping("/create/project/information")
	public String saveProjectBasicInfo(
			
			// 接收除了上傳圖檔之外的其他普通資料
			ProjectVO projectVO, 
			
			// 接收上傳的頭圖
			MultipartFile headerPicture, 
			
			// 接收上傳的詳情圖檔
			List<MultipartFile> detailPictureList, 
			
			// 用來将收集了一部分資料的ProjectVO對象存入Session域
			HttpSession session,
			
			// 用來在目前操作失敗後傳回上一個表單頁面時攜帶提示消息
			ModelMap modelMap
			) throws IOException {
		
		// 一、完成頭圖上傳
		// 1.擷取目前headerPicture對象是否為空
		boolean headerPictureIsEmpty = headerPicture.isEmpty();
		
		if(headerPictureIsEmpty) {
			
			// 2.如果沒有上傳頭圖則傳回到表單頁面并顯示錯誤消息
			modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_HEADER_PIC_EMPTY);
			
			return "project-launch";
			
		}
		
		// 3.如果使用者确實上傳了有内容的檔案,則執行上傳
		ResultEntity<String> uploadHeaderPicResultEntity = CrowdUtil.uploadFileToOss(
				ossProperties.getEndPoint(), 
				ossProperties.getAccessKeyId(), 
				ossProperties.getAccessKeySecret(), 
				headerPicture.getInputStream(), 
				ossProperties.getBucketName(), 
				ossProperties.getBucketDomain(), 
				headerPicture.getOriginalFilename());
		
		String result = uploadHeaderPicResultEntity.getResult();
		
		// 4.判斷頭圖是否上傳成功
		if(ResultEntity.SUCCESS.equals(result)) {
			
			// 5.如果成功則從傳回的資料中擷取圖檔通路路徑
			String headerPicturePath = uploadHeaderPicResultEntity.getData();
			
			// 6.存入ProjectVO對象中
			projectVO.setHeaderPicturePath(headerPicturePath);
		} else {
			
			// 7.如果上傳失敗則傳回到表單頁面并顯示錯誤消息
			modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_HEADER_PIC_UPLOAD_FAILED);
			
			return "project-launch";
			
		}
		
		// 二、上傳詳情圖檔
		// 1.建立一個用來存放詳情圖檔路徑的集合
		List<String> detailPicturePathList = new ArrayList<String>();
		
		// 2.檢查detailPictureList是否有效
		if(detailPictureList == null || detailPictureList.size() == 0) {
			modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_DETAIL_PIC_EMPTY);
			
			return "project-launch";
		}
		
		// 3.周遊detailPictureList集合
		for (MultipartFile detailPicture : detailPictureList) {
			
			// 4.目前detailPicture是否為空
			if(detailPicture.isEmpty()) {
				
				// 5.檢測到詳情圖檔中單個檔案為空也是回去顯示錯誤消息
				modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_DETAIL_PIC_EMPTY);
				
				return "project-launch";
			}
			
			// 6.執行上傳
			ResultEntity<String> detailUploadResultEntity = CrowdUtil.uploadFileToOss(
					ossProperties.getEndPoint(), 
					ossProperties.getAccessKeyId(), 
					ossProperties.getAccessKeySecret(), 
					detailPicture.getInputStream(), 
					ossProperties.getBucketName(), 
					ossProperties.getBucketDomain(), 
					detailPicture.getOriginalFilename());
			
			// 7.檢查上傳結果
			String detailUploadResult = detailUploadResultEntity.getResult();
			
			if(ResultEntity.SUCCESS.equals(detailUploadResult)) {
				
				String detailPicturePath = detailUploadResultEntity.getData();
				
				// 8.收集剛剛上傳的圖檔的通路路徑
				detailPicturePathList.add(detailPicturePath);
			} else {
				
				// 9.如果上傳失敗則傳回到表單頁面并顯示錯誤消息
				modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_DETAIL_PIC_UPLOAD_FAILED);
				
				return "project-launch";
			}
			
		}
		
		// 10.将存放了詳情圖檔通路路徑的集合存入ProjectVO中
		projectVO.setDetailPicturePathList(detailPicturePathList);
		
		// 三、後續操作
		// 1.将ProjectVO對象存入Session域
		session.setAttribute(CrowdConstant.ATTR_NAME_TEMPLE_PROJECT, projectVO);
		
		// 2.以完整的通路路徑前往下一個收集回報資訊的頁面
		return "redirect:http://www.crowd.com/project/return/info/page";
	}

}
           

3.9.4、添加view-controller

day15【前台】項目釋出day15【前台】項目釋出
@Configuration
public class CrowdWebMvcConfig implements WebMvcConfigurer {
	
	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		
		// view-controller是在project-consumer内部定義的,是以這裡是一個不經過Zuul通路的位址,是以這個路徑前面不加路由規則中定義的字首:“/project”
		registry.addViewController("/agree/protocol/page").setViewName("project-agree");
		registry.addViewController("/launch/project/page").setViewName("project-launch");
		registry.addViewController("/return/info/page").setViewName("project-return");
		
	}

}
           

3.10、添加回報頁面

day15【前台】項目釋出day15【前台】項目釋出

3.11、測試

  • 圖檔能上傳至阿裡與

    OSS

day15【前台】項目釋出day15【前台】項目釋出
  • Redis

    中的

    session

day15【前台】項目釋出day15【前台】項目釋出

4、送出回報資訊

4.1、整體思路

day15【前台】項目釋出day15【前台】項目釋出

4.2、Ajax上傳圖檔

4.2.1、前端Ajax請求

  • Ajax

    形式送出圖檔,并通過響應資訊擷取圖檔的網絡位置
  • "url":"[[@{/project/create/upload/return/picture.json}]]"

    :在頁面位址中需要帶上

    zuul

    網關的路由規則,即域名+微服務路徑

    project

day15【前台】項目釋出day15【前台】項目釋出
// 在檔案上傳框的值改變事件響應函數中預覽并上傳圖檔
$("[name=returnPicture]").change(function(event){
	
	var file = event.target.files[0];
	
	var url = window.url || window.webkitURL;
	
	var path = url.createObjectURL(file);
	
	$(this).next().next().next().next().attr("src",path).show();
	
	// 将上傳的檔案封裝到FormData對象中
	var formData = new FormData();
	
	formData.append("returnPicture", file);
	
	// 發送Ajax請求上傳檔案
	$.ajax({
		"url":"[[@{/project/create/upload/return/picture.json}]]",
		"type":"post",
		"data":formData,
		"contentType":false,
		"processData":false,
		"dataType":"json",
		"success":function(response){
			
			var result = response.result;
			
			if(result == "SUCCESS") {
				alert("上傳成功!");
				
				// 如果上傳成功,則從響應體資料中擷取圖檔的通路路徑
				returnObj.describPicPath = response.data;
			}
			
			if(result == "FAILED") {
				alert(response.message);
			}
			
		},
		"error":function(response){
			alert(response.status + " " + response.statusText);
		}
	});
	
});
           

4.2.2、後端Handler代碼

  • 将圖檔上傳至

    OSS

    ,并傳回上傳的結果
day15【前台】項目釋出day15【前台】項目釋出
// JavaScript代碼:formData.append("returnPicture", file);
// returnPicture是請求參數的名字
// file是請求參數的值,也就是要上傳的檔案
@ResponseBody
@RequestMapping("/create/upload/return/picture.json")
public ResultEntity<String> uploadReturnPicture(
		
		// 接收使用者上傳的檔案
		@RequestParam("returnPicture") MultipartFile returnPicture) throws IOException {
	
	// 1.執行檔案上傳
	ResultEntity<String> uploadReturnPicResultEntity = CrowdUtil.uploadFileToOss(
			ossProperties.getEndPoint(), 
			ossProperties.getAccessKeyId(), 
			ossProperties.getAccessKeySecret(), 
			returnPicture.getInputStream(), 
			ossProperties.getBucketName(), 
			ossProperties.getBucketDomain(), 
			returnPicture.getOriginalFilename());
	
	// 2.傳回上傳的結果
	return uploadReturnPicResultEntity;
}
           

4.3、Ajax送出表單

4.3.1、前端Ajax請求

  • 收集表單項,送出

    Ajax

    請求
day15【前台】項目釋出day15【前台】項目釋出
// 聲明序号儲存表格中資料的序号
var order = 0;

// 點選确定按鈕,綁定單擊響應函數
$("#okBtn").click(function(){
	
	// 1.收集表單資料
	returnObj.type = $("[name=type]:checked").val();
	returnObj.supportmoney = $("[name=supportmoney]").val();
	returnObj.content = $("[name=content]").val();
	returnObj.count = $("[name=count]").val();
	returnObj.signalpurchase = $("[name=signalpurchase]:checked").val();
	returnObj.purchase = $("[name=purchase]").val();
	returnObj.freight = $("[name=freight]").val();
	returnObj.invoice = $("[name=invoice]:checked").val();
	returnObj.returndate = $("[name=returndate]").val();
	
	// 2.發送Ajax請求
	$.ajax({
		"url" : "[[@{/project/create/save/return.json}]]",
		"type": "post",
		"dataType": "json",
		"data": returnObj,
		"success": function(response) {
			var result = response.result;
			if(result == "SUCCESS") {
				alert("這一條儲存成功!");
				
				// 使用returnObj填充表格
				var orderTd = "<td>"+(++order)+"</td>";
				var supportmoneyTd = "<td>"+returnObj.supportmoney+"</td>";
				var countTd = "<td>"+returnObj.count+"</td>";
				var signalpurchaseTd = "<td>"+(returnObj.signalpurchase == 0?"不限購":("限購"+returnObj.purchase))+"</td>";
				var contentTd = "<td>"+returnObj.content+"</td>";
				var returndateTd = "<td>"+returnObj.returndate+"天以後返還</td>";
				var freightTd = "<td>"+(returnObj.freight==0?"包郵":returnObj.freight)+"</td>";
				var operationTd = "<td><button type='button' class='btn btn-primary btn-xs'><i class=' glyphicon glyphicon-pencil'></i></button>&nbsp;<button type='button' class='btn btn-danger btn-xs'><i class=' glyphicon glyphicon-remove'></i></button></td>";
				var trHTML = "<tr>"+orderTd+supportmoneyTd+countTd+signalpurchaseTd+contentTd+returndateTd+freightTd+operationTd+"</tr>";
				
				$("#returnTableBody").append(trHTML);
				
				$("#returnPictureImage").hide();
			}
			
			if(result == "FAILED") {
				alert("這一條儲存失敗!");
			}
			
			// 後續操作
			// 僅僅調用click()函數而不傳入回調函數表示點選一下這個按鈕
			$("#resetBtn").click();
			
			// 将表單部分div隐藏
			$(".returnFormDiv").hide();
		}
	});
});
           

4.3.2、後端Handler代碼

  • 取出

    Redis

    中的

    ProjectVO

    對象,用該對象接收表單發送的

    ReturnVO

    對象,更新

    session

    中的

    ProjectVO

    對象
day15【前台】項目釋出day15【前台】項目釋出
@ResponseBody
@RequestMapping("/create/save/return.json")
public ResultEntity<String> saveReturn(ReturnVO returnVO, HttpSession session) {
	
	try {
		// 1.從session域中讀取之前緩存的ProjectVO對象
		ProjectVO projectVO = (ProjectVO) session.getAttribute(CrowdConstant.ATTR_NAME_TEMPLE_PROJECT);
		
		// 2.判斷projectVO是否為null
		if(projectVO == null) {
			return ResultEntity.failed(CrowdConstant.MESSAGE_TEMPLE_PROJECT_MISSING);
		}
		
		// 3.從projectVO對象中擷取存儲回報資訊的集合
		List<ReturnVO> returnVOList = projectVO.getReturnVOList();
		
		// 4.判斷returnVOList集合是否有效
		if(returnVOList == null || returnVOList.size() == 0) {
			
			// 5.建立集合對象對returnVOList進行初始化
			returnVOList = new ArrayList<>();
			// 6.為了讓以後能夠正常使用這個集合,設定到projectVO對象中
			projectVO.setReturnVOList(returnVOList);
		}
		
		// 7.将收集了表單資料的returnVO對象存入集合
		returnVOList.add(returnVO);
		
		// 8.把資料有變化的ProjectVO對象重新存入Session域,以確定新的資料最終能夠存入Redis
		session.setAttribute(CrowdConstant.ATTR_NAME_TEMPLE_PROJECT, projectVO);
		
		// 9.所有操作成功完成傳回成功
		return ResultEntity.successWithoutData();
	} catch (Exception e) {
		e.printStackTrace();
		
		return ResultEntity.failed(e.getMessage());
	}
	
}
           

4.4、測試

  • 回報資訊送出成功
day15【前台】項目釋出day15【前台】項目釋出
  • 阿裡雲

    OSS

    上傳圖檔成功
day15【前台】項目釋出day15【前台】項目釋出

5、送出确認資訊

5.1、建立頁面

  • project-confirm.html

    :确認資訊頁面
  • project-success.html

    :成功頁面
day15【前台】項目釋出day15【前台】項目釋出

5.2、修改【下一步】超連結

  • 修改回報資訊頁面【下一步】按鈕的超連結,點選【下一步】按鈕,跳轉至成功頁面
day15【前台】項目釋出day15【前台】項目釋出

5.3、添加view-controller

day15【前台】項目釋出day15【前台】項目釋出
@Configuration
public class CrowdWebMvcConfig implements WebMvcConfigurer {
	
	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		
		// view-controller是在project-consumer内部定義的,是以這裡是一個不經過Zuul通路的位址,是以這個路徑前面不加路由規則中定義的字首:“/project”
		registry.addViewController("/agree/protocol/page").setViewName("project-agree");
		registry.addViewController("/launch/project/page").setViewName("project-launch");
		registry.addViewController("/return/info/page").setViewName("project-return");
		registry.addViewController("/create/confirm/page").setViewName("project-confirm");
		registry.addViewController("/create/success").setViewName("project-success");
		
	}

}
           
registry.addViewController("/create/confirm/page").setViewName("project-confirm");
registry.addViewController("/create/success").setViewName("project-success");
           

5.4、确認資訊表單

  • action

    送出位址與

    Handler

    方法對應
  • 表單标簽項的

    name

    屬性與

    VO

    實體類屬性名對應
day15【前台】項目釋出day15【前台】項目釋出
<form id="confirmFomr" th:action="@{/project/create/confirm}" method="post" role="form">
    <div class="form-group">
         <label for="exampleInputEmail1">易付寶企業賬号:</label><input type="email" name="paynum" class="form-control" id="exampleInputEmail1" />
    </div>
    <div class="form-group">
         <label for="exampleInputPassword1">法人身份證号:</label><input type="password" name="cardnum" class="form-control" id="exampleInputPassword1" />
    </div>
</form>
           

5.5、送出按鈕

  • 點選【送出】按鈕送出表單
day15【前台】項目釋出day15【前台】項目釋出
<script type="text/javascript">
	$(function(){
		$("#submitBtn").click(function(){
			$("#confirmFomr").submit();
		});
	});
</script>
<button type="button" id="submitBtn" class="btn  btn-warning btn-lg">送出</button>

           

5.6、Handler代碼

  • session

    域中取出

    ProjectVO

    對象
  • 更新

    ProjectVO

    對象的确認資訊
  • ProjectVO

    對象送出至

    mysql-provider

    進行儲存
  • 儲存成功則删除

    session

    域中的

    ProjectVO

    對象,并重定向至成功頁面
day15【前台】項目釋出day15【前台】項目釋出
@Autowired
private MySQLRemoteService mySQLRemoteService;

@RequestMapping("/create/confirm")
public String saveConfirm(ModelMap modelMap, HttpSession session, MemberConfirmInfoVO memberConfirmInfoVO) {

    // 1.從Session域讀取之前臨時存儲的ProjectVO對象
    ProjectVO projectVO = (ProjectVO) session.getAttribute(CrowdConstant.ATTR_NAME_TEMPLE_PROJECT);

    // 2.如果projectVO為null
    if(projectVO == null) {
        throw new RuntimeException(CrowdConstant.MESSAGE_TEMPLE_PROJECT_MISSING);
    }

    // 3.将确認資訊資料設定到projectVO對象中
    projectVO.setMemberConfirmInfoVO(memberConfirmInfoVO);

    // 4.從Session域讀取目前登入的使用者
    MemberLoginVO memberLoginVO = (MemberLoginVO) session.getAttribute(CrowdConstant.ATTR_NAME_LOGIN_MEMBER);

    Integer memberId = memberLoginVO.getId();

    // 5.調用遠端方法儲存projectVO對象
    ResultEntity<String> saveResultEntity = mySQLRemoteService.saveProjectVORemote(projectVO, memberId);

    // 6.判斷遠端的儲存操作是否成功
    String result = saveResultEntity.getResult();
    if(ResultEntity.FAILED.equals(result)) {

        modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, saveResultEntity.getMessage());

        return "project-confirm";
    }

    // 7.将臨時的ProjectVO對象從Session域移除
    session.removeAttribute(CrowdConstant.ATTR_NAME_TEMPLE_PROJECT);

    // 8.如果遠端儲存成功則跳轉到最終完成頁面
    return "redirect:http://www.crowd.com/project/create/success";
}
           

5.7、api遠端調用接口聲明

  • MySQLRemoteService

    遠端調用接口中聲明上述方法
day15【前台】項目釋出day15【前台】項目釋出
@FeignClient("atguigu-crowd-mysql")
public interface MySQLRemoteService {
	
	@RequestMapping("/get/memberpo/by/login/acct/remote")
	ResultEntity<MemberPO> getMemberPOByLoginAcctRemote(@RequestParam("loginacct") String loginacct);

	@RequestMapping("/save/member/remote")
	public ResultEntity<String> saveMember(@RequestBody MemberPO memberPO);

	@RequestMapping("/save/project/vo/remote")
	ResultEntity<String> saveProjectVORemote(@RequestBody ProjectVO projectVO, @RequestParam("memberId") Integer memberId);

}
           

6、執行儲存

6.1、Handler代碼

  • Handler

    中調用本地

    Service

    方法完成項目資訊的儲存
day15【前台】項目釋出day15【前台】項目釋出
@RestController
public class ProjectProviderHandler {
	
	@Autowired
	private ProjectService projectService;
	
	@RequestMapping("/save/project/vo/remote")
	public ResultEntity<String> saveProjectVORemote(
			@RequestBody ProjectVO projectVO, 
			@RequestParam("memberId") Integer memberId) {
		
		try {
			// 調用“本地”Service執行儲存
			projectService.saveProject(projectVO, memberId);
			
			return ResultEntity.successWithoutData();
		} catch (Exception e) {
			e.printStackTrace();
			
			return ResultEntity.failed(e.getMessage());
		}
		
	}

}
           

6.2、Service代碼

  • 分為如下步驟:
    • 儲存

      ProjectPO

      對象
    • 儲存項目分類資訊
    • 儲存項目标簽資訊
    • 儲存項目發起人資訊
    • 儲存項目回報資訊
    • 儲存項目确認資訊
day15【前台】項目釋出day15【前台】項目釋出
@Transactional(readOnly = true)
@Service
public class ProjectServiceImpl implements ProjectService {
	
	@Autowired
	private ReturnPOMapper returnPOMapper;
	
	@Autowired
	private MemberConfirmInfoPOMapper memberConfirmInfoPOMapper;
	
	@Autowired
	private MemberLaunchInfoPOMapper memberLaunchInfoPOMapper;
	
	@Autowired
	private ProjectPOMapper projectPOMapper;
	
	@Autowired
	private ProjectItemPicPOMapper projectItemPicPOMapper;

	@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
	@Override
	public void saveProject(ProjectVO projectVO, Integer memberId) {
		
		// 一、儲存ProjectPO對象
		// 1.建立空的ProjectPO對象
		ProjectPO projectPO = new ProjectPO();
		
		// 2.把projectVO中的屬性複制到projectPO中
		BeanUtils.copyProperties(projectVO, projectPO);
		
		// 修複bug:把memberId設定到projectPO中
		projectPO.setMemberid(memberId);
		
		// 修複bug:生成建立時間存入
		String createdate = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
		projectPO.setCreatedate(createdate);
		
		// 修複bug:status設定成0,表示即将開始
		projectPO.setStatus(0);
		
		// 3.儲存projectPO
		// 為了能夠擷取到projectPO儲存後的自增主鍵,需要到ProjectPOMapper.xml檔案中進行相關設定
		// <insert id="insertSelective" useGeneratedKeys="true" keyProperty="id" ……
		projectPOMapper.insertSelective(projectPO);
		
		// 4.從projectPO對象這裡擷取自增主鍵
		Integer projectId = projectPO.getId();
		
		// 二、儲存項目、分類的關聯關系資訊
		// 1.從ProjectVO對象中擷取typeIdList
		List<Integer> typeIdList = projectVO.getTypeIdList();
		projectPOMapper.insertTypeRelationship(typeIdList, projectId);
		
		// 三、儲存項目、标簽的關聯關系資訊
		List<Integer> tagIdList = projectVO.getTagIdList();
		projectPOMapper.insertTagRelationship(tagIdList, projectId);
		
		// 四、儲存項目中詳情圖檔路徑資訊
		List<String> detailPicturePathList = projectVO.getDetailPicturePathList();
		projectItemPicPOMapper.insertPathList(projectId, detailPicturePathList);
		
		// 五、儲存項目發起人資訊
		MemberLauchInfoVO memberLauchInfoVO = projectVO.getMemberLauchInfoVO();
		MemberLaunchInfoPO memberLaunchInfoPO = new MemberLaunchInfoPO();
		BeanUtils.copyProperties(memberLauchInfoVO, memberLaunchInfoPO);
		memberLaunchInfoPO.setMemberid(memberId);
		
		memberLaunchInfoPOMapper.insert(memberLaunchInfoPO);
		
		// 六、儲存項目回報資訊
		List<ReturnVO> returnVOList = projectVO.getReturnVOList();
		
		List<ReturnPO> returnPOList = new ArrayList<>();
		
		for (ReturnVO returnVO : returnVOList) {
			
			ReturnPO returnPO = new ReturnPO();
			
			BeanUtils.copyProperties(returnVO, returnPO);
			
			returnPOList.add(returnPO);
		}
		
		returnPOMapper.insertReturnPOBatch(returnPOList, projectId);
		
		// 七、儲存項目确認資訊
		MemberConfirmInfoVO memberConfirmInfoVO = projectVO.getMemberConfirmInfoVO();
		MemberConfirmInfoPO memberConfirmInfoPO = new MemberConfirmInfoPO();
		BeanUtils.copyProperties(memberConfirmInfoVO, memberConfirmInfoPO);
		memberConfirmInfoPO.setMemberid(memberId);
		memberConfirmInfoPOMapper.insert(memberConfirmInfoPO);
	
	}

}
           

6.3、擷取自增主鍵

  • 設定插入資料時,擷取插入資料的自增主鍵
    • useGeneratedKeys="true"

      :擷取自增主鍵
    • keyProperty="id"

      PO

      類中主鍵屬性名為

      id

day15【前台】項目釋出day15【前台】項目釋出
<insert id="insertSelective" useGeneratedKeys="true" keyProperty="id" parameterType="com.atguigu.crowd.entity.po.ProjectPO" >
  insert into t_project
  <trim prefix="(" suffix=")" suffixOverrides="," >
    <if test="id != null" >
      id,
    </if>
    <if test="projectName != null" >
      project_name,
    </if>
    <if test="projectDescription != null" >
      project_description,
    </if>
    <if test="money != null" >
      money,
    </if>
    <if test="day != null" >
      day,
    </if>
    <if test="status != null" >
      status,
    </if>
    <if test="deploydate != null" >
      deploydate,
    </if>
    <if test="supportmoney != null" >
      supportmoney,
    </if>
    <if test="supporter != null" >
      supporter,
    </if>
    <if test="completion != null" >
      completion,
    </if>
    <if test="memberid != null" >
      memberid,
    </if>
    <if test="createdate != null" >
      createdate,
    </if>
    <if test="follower != null" >
      follower,
    </if>
    <if test="headerPicturePath != null" >
      header_picture_path,
    </if>
  </trim>
  <trim prefix="values (" suffix=")" suffixOverrides="," >
    <if test="id != null" >
      #{id,jdbcType=INTEGER},
    </if>
    <if test="projectName != null" >
      #{projectName,jdbcType=VARCHAR},
    </if>
    <if test="projectDescription != null" >
      #{projectDescription,jdbcType=VARCHAR},
    </if>
    <if test="money != null" >
      #{money,jdbcType=BIGINT},
    </if>
    <if test="day != null" >
      #{day,jdbcType=INTEGER},
    </if>
    <if test="status != null" >
      #{status,jdbcType=INTEGER},
    </if>
    <if test="deploydate != null" >
      #{deploydate,jdbcType=VARCHAR},
    </if>
    <if test="supportmoney != null" >
      #{supportmoney,jdbcType=BIGINT},
    </if>
    <if test="supporter != null" >
      #{supporter,jdbcType=INTEGER},
    </if>
    <if test="completion != null" >
      #{completion,jdbcType=INTEGER},
    </if>
    <if test="memberid != null" >
      #{memberid,jdbcType=INTEGER},
    </if>
    <if test="createdate != null" >
      #{createdate,jdbcType=VARCHAR},
    </if>
    <if test="follower != null" >
      #{follower,jdbcType=INTEGER},
    </if>
    <if test="headerPicturePath != null" >
      #{headerPicturePath,jdbcType=VARCHAR},
    </if>
  </trim>
</insert>
           

6.4、儲存項目分類資訊

6.4.1、項目分類表

  • 存儲項目與分類資訊之間的關聯關系
day15【前台】項目釋出day15【前台】項目釋出

6.4.2、Mapper接口

day15【前台】項目釋出day15【前台】項目釋出
void insertTypeRelationship(
    @Param("typeIdList") List<Integer> typeIdList, 
    @Param("projectId") Integer projectId);
           

6.4.3、Mapper映射檔案

day15【前台】項目釋出day15【前台】項目釋出
<!-- 
	void insertTypeRelationship(
		@Param("typeIdList") List<Integer> typeIdList, 
		@Param("projectId") Integer projectId);
 -->
<insert id="insertTypeRelationship">
	insert into t_project_type(`projectid`,`typeid`) values
	<foreach collection="typeIdList" item="typeId" separator=",">(#{projectId},#{typeId})</foreach>
</insert>
           

6.5、儲存項目标簽資訊

6.5.1、項目标簽表

  • 存儲項目與标簽資訊之間的關聯關系
day15【前台】項目釋出day15【前台】項目釋出

6.5.2、Mapper接口

day15【前台】項目釋出day15【前台】項目釋出
void insertTagRelationship(
    @Param("tagIdList") List<Integer> tagIdList, 
    @Param("projectId") Integer projectId);
           

6.5.3、Mapper映射檔案

day15【前台】項目釋出day15【前台】項目釋出
<!-- 
	void insertTagRelationship(
		@Param("tagIdList") List<Integer> tagIdList, 
		@Param("projectId") Integer projectId);
 -->
<insert id="insertTagRelationship">
	insert into `t_project_tag`(`projectid`,`tagid`) values
	<foreach collection="tagIdList" item="tagId" separator=",">(#{projectId},#{tagId})</foreach>
</insert>
           

6.6、儲存項目詳情圖檔

6.6.1、項目詳情圖檔表

  • 存儲項目詳情圖檔的網絡路徑
day15【前台】項目釋出day15【前台】項目釋出

6.6.2、Mapper接口

day15【前台】項目釋出day15【前台】項目釋出
void insertPathList(
    @Param("projectId") Integer projectId, 
    @Param("detailPicturePathList") List<String> detailPicturePathList);
           

6.6.3、Mapper映射檔案

day15【前台】項目釋出day15【前台】項目釋出
<!-- 
	void insertPathList(
		@Param("projectId") Integer projectId, 
		@Param("detailPicturePathList") List<String> detailPicturePathList);
 -->
<insert id="insertPathList">
	insert into t_project_item_pic (projectid, item_pic_path)
	values
	<foreach collection="detailPicturePathList" item="detailPath" separator=",">(#{projectId},#{detailPath})</foreach>
</insert>
           

6.7、儲存項目回報資訊

6.7.1、項目回報表

  • 存儲項目的回報資訊
day15【前台】項目釋出day15【前台】項目釋出

6.7.2、Mapper接口

day15【前台】項目釋出day15【前台】項目釋出
void insertReturnPOBatch(
    @Param("returnPOList") List<ReturnPO> returnPOList, 
    @Param("projectId") Integer projectId);
           

6.7.3、Mapper映射檔案

day15【前台】項目釋出day15【前台】項目釋出
<!-- 
void insertReturnPOBatch(
@Param("returnPOList") List<ReturnPO> returnPOList, 
@Param("projectId") Integer projectId);
 -->
<insert id="insertReturnPOBatch">
	insert into t_return (
	projectid, 
	type, 
    supportmoney, 
    content, 
    count, 
    signalpurchase, 
    purchase, 
    freight, 
    invoice, 
    returndate, 
    describ_pic_path
    )
  values
  <foreach collection="returnPOList" item="returnPO" separator=",">
  	(
  		#{projectId},
  		#{returnPO.type},
  		#{returnPO.supportmoney},
  		#{returnPO.content},
  		#{returnPO.count},
  		#{returnPO.signalpurchase},
  		#{returnPO.purchase},
  		#{returnPO.freight},
  		#{returnPO.invoice},
  		#{returnPO.returndate},
  		#{returnPO.describPicPath}
  	)
  </foreach>
</insert>
           

6.8、測試

  • 送出資料成功~
day15【前台】項目釋出day15【前台】項目釋出
  • t_project

    Project

    詳細資訊
day15【前台】項目釋出day15【前台】項目釋出
  • t_project_type

    :項目與分類的關聯關系
day15【前台】項目釋出day15【前台】項目釋出
  • t_project_tag

    :項目與标簽的關聯關系
day15【前台】項目釋出day15【前台】項目釋出
  • t_project_item_pic

    :項目詳情圖檔的路徑
day15【前台】項目釋出day15【前台】項目釋出
  • t_member_launch_info

    :發起人資訊
day15【前台】項目釋出day15【前台】項目釋出
  • t_return

    :回報資訊
day15【前台】項目釋出day15【前台】項目釋出
  • t_member_confirm_info

    :确認資訊
day15【前台】項目釋出day15【前台】項目釋出
  • OSS

    上傳圖檔成功
day15【前台】項目釋出day15【前台】項目釋出

6.9、遇到的問題

  • 可以看到,

    money

    字段的值并沒有儲存成功,我們需要将

    money

    字段的類型從

    Long

    修改為

    Integer

day15【前台】項目釋出day15【前台】項目釋出
  • Mybatis

    逆向生成時,将

    ProjectPO

    類的

    money

    字段的類型設定為

    Long

    ,而我們自己寫的

    ProjectVO

    類的

    money

    字段的類型為

    Integer

day15【前台】項目釋出day15【前台】項目釋出
day15【前台】項目釋出day15【前台】項目釋出
  • BeanUtils.copyProperties

    拷貝屬性值時,貌似會拷貝失敗???反正請求參數中的

    money

    字段值能正常注入
day15【前台】項目釋出day15【前台】項目釋出
day15【前台】項目釋出day15【前台】項目釋出

繼續閱讀