日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【项目】健康项目day5总结

發布時間:2024/3/12 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【项目】健康项目day5总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第5章 移動端開發-套餐列表、套餐詳情、頁面靜態化

學習目標:

  • 掌握移動端套餐列表頁動態展示實現過程

  • 掌握移動端套餐詳情頁

  • 掌握Freemarker頁面靜態化技術

  • 能夠使用Freemarker生成html靜態頁面

1. 移動端需求分析和環境搭建

1.1. 需求分析

【目標】

  • 能夠搭建移動端開發環境

【路徑】

  • 開發需求
  • 環境搭建

【講解】

1.1.1 移動端開發需求分析

用戶在體檢之前需要進行預約,可以通過電話方式進行預約,此時會由體檢中心客服人員通過后臺系統錄入預約信息。用戶也可以通過手機端自助預約。本章節開發的功能為用戶通過手機自助預約。

預約流程如下:

1、訪問移動端首頁

2、點擊體檢預約進入體檢套餐列表頁面

3、在體檢套餐列表頁面點擊具體套餐進入套餐詳情頁面

4、在套餐詳情頁面點擊立即預約進入預約頁面

5、在預約頁面錄入體檢人相關信息點擊提交預約

效果如下圖:




【小結】

體檢預約–>套餐列表–>套餐詳情–>立即預約(發送手機驗證碼+Redis)

1.2. 搭建移動端工程(互聯用戶)

【目標】

移動端工程搭建

【路徑】

  • 創建health_mobile工程, 導入坐標(依賴health_interface)
  • 導入頁面
  • 配置web.xml(springmvc的核心控制器+post請求亂碼過濾器)
  • 創建springmvc.xml(配置dubbo, 驅動注解)
  • 導入通用組件
  • 【講解】

    本項目是基于SOA架構進行開發,前面我們已經完成了后臺系統的部分功能開發,在后臺系統中都是通過Dubbo調用服務層發布的服務進行相關的操作。本章節我們開發移動端工程也是同樣的模式,所以我們也需要在移動端工程中通過Dubbo調用服務層發布的服務,如下圖:

    1.2.1. 導入maven坐標

    在health_parent工程的pom.xml文件中導入阿里短信發送的maven坐標

    <!--阿里云服務器短信平臺--> <dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>3.3.1</version> </dependency> <dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-dysmsapi</artifactId><version>1.0.0</version> </dependency>

    在health_common工程中添加引入的依賴

    <dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId> </dependency> <dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-dysmsapi</artifactId> </dependency>

    1.2.2. health_mobile

    【路徑】

    1:pom.xml

    2:靜態資源(CSS、html、img等)

    3:web.xml

    4:springmvc.xml

    5:spring-jedis.xml

    6:redis.properties

    7:log4j.properties

    移動端工程,打包方式為war,用于存放Controller,在Controller中通過Dubbo可以遠程訪問服務層相關服務,所以需要依賴health_interface接口工程。

    1.2.2.1. pom.xml

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>health_parent</artifactId><groupId>com.itheima</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>health_mobile</artifactId><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><packaging>war</packaging><dependencies><dependency><groupId>com.itheima</groupId><artifactId>health_interface</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies> </project>

    1.2.2.2.靜態資源(CSS、html、img等,詳見資料)

    1.2.2.3. web.xml

    <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"id="WebApp_ID" version="3.0"><display-name>Archetype Created Web Application</display-name><!-- 解決post亂碼 --><filter><filter-name>CharacterEncodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>utf-8</param-value></init-param></filter><filter-mapping><filter-name>CharacterEncodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 指定加載的配置文件 ,通過參數contextConfigLocation加載 --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>*.do</url-pattern></servlet-mapping> </web-app>

    1.2.2.4. springmvc.xml

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"><mvc:annotation-driven><mvc:message-converters register-defaults="true"><bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"><property name="supportedMediaTypes" value="application/json"/><property name="features"><list><value>WriteMapNullValue</value><value>WriteDateUseDateFormat</value></list></property></bean></mvc:message-converters></mvc:annotation-driven><!-- 指定應用名稱 --><dubbo:application name="health_mobile" /><!--指定服務注冊中心地址--><dubbo:registry address="zookeeper://127.0.0.1:2181"/><!--批量掃描--><dubbo:annotation package="com.itheima.health.controller" /><!--超時全局設置 10分鐘check=false 不檢查服務提供方,開發階段建議設置為falsecheck=true 啟動時檢查服務提供方,如果服務提供方沒有啟動則報錯--><dubbo:consumer timeout="600000" check="false"/><import resource="spring-jedis.xml"></import> </beans>

    1.2.2.5.spring-jedis.xml:

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><context:property-placeholder location="classpath:redis.properties" /><!--Jedis連接池的相關配置--><bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"><property name="maxTotal"><value>${redis.pool.maxActive}</value></property><property name="maxIdle"><value>${redis.pool.maxIdle}</value></property><property name="testOnBorrow" value="true"/><property name="testOnReturn" value="true"/></bean><bean id="jedisPool" class="redis.clients.jedis.JedisPool"><constructor-arg name="poolConfig" ref="jedisPoolConfig" /><constructor-arg name="host" value="${redis.host}" /><constructor-arg name="port" value="${redis.port}" type="int" /><constructor-arg name="timeout" value="${redis.timeout}" type="int" /></bean> </beans>

    1.2.2.6. redis.properties

    #最大分配的對象數 redis.pool.maxActive=200 #最大能夠保持idel狀態的對象數 redis.pool.maxIdle=50 redis.pool.minIdle=10 redis.pool.maxWaitMillis=20000 #當池內沒有返回對象時,最大等待時間 redis.pool.maxWait=300#格式:redis://:[密碼]@[服務器地址]:[端口]/[db index] #redis.uri = redis://:12345@127.0.0.1:6379/0redis.host = 127.0.0.1 redis.port = 6379 redis.timeout = 30000

    1.2.2.7. log4j.properties

    ### direct log messages to stdout ### log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.err log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n### direct messages to file mylog.log ### log4j.appender.file=org.apache.log4j.FileAppender log4j.appender.file.File=c:\\mylog.log log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n### set log levels - for more verbose logging change 'info' to 'debug' ###log4j.rootLogger=debug, stdout

    1.2.3. 導入通用組件

    【路徑】

    1:ValidateCodeUtils工具類:(產生驗證碼)

    2:SMSUtils工具類:(短信服務,用于發送短消息服務(SMS))

    3:RedisMessageConstant常量類:

    【講解】

    在health_common工程中導入如下通用組件

    1:ValidateCodeUtils工具類:(產生驗證碼)

    package com.itheima.health.utils;import java.util.Random;/*** 隨機生成驗證碼工具類*/ public class ValidateCodeUtils {/*** 隨機生成驗證碼* @param length 長度為4位或者6位* @return*/public static Integer generateValidateCode(int length){Integer code =null;if(length == 4){code = new Random().nextInt(9999);//生成隨機數,最大為9999if(code < 1000){code = code + 1000;//保證隨機數為4位數字}}else if(length == 6){code = new Random().nextInt(999999);//生成隨機數,最大為999999if(code < 100000){code = code + 100000;//保證隨機數為6位數字}}else{throw new RuntimeException("只能生成4位或6位數字驗證碼");}return code;}/*** 隨機生成指定長度字符串驗證碼* @param length 長度* @return*/public static String generateValidateCode4String(int length){Random rdm = new Random();String hash1 = Integer.toHexString(rdm.nextInt());String capstr = hash1.substring(0, length);return capstr;} }

    2:SMSUtils工具類:(短信服務,用于發送短消息服務(SMS))

    package com.itheima.health.utils;import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.IAcsClient; import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.http.MethodType; import com.aliyuncs.profile.DefaultProfile; import com.aliyuncs.profile.IClientProfile;/*** 短信發送工具類*/ public class SMSUtils {public static final String VALIDATE_CODE = "SMS_189616640";//發送短信驗證碼public static final String ORDER_NOTICE = "SMS_159771588";//體檢預約成功通知private static final String SIGN_NAEM = "黑馬程序員";// 短信的簽名private static final String PARAMETER_NAME="code";private static final String ACCESS_KEY="LTAI4GERJj7v71F3FKjw3z2A"; //你的AccessKey IDprivate static final String SECRET_KEY="dIVZnHGdUTYbqOKMlxZ7R7jXVcnPoz"; //你的AccessKey Secretpublic static void main(String[] args) throws ClientException {SMSUtils.sendShortMessage(VALIDATE_CODE,"13652431027","666666");}/*** 發送短信* @param phoneNumbers* @param param* @throws ClientException*/public static void sendShortMessage(String templateCode,String phoneNumbers,String param) throws ClientException{// 設置超時時間-可自行調整System.setProperty("sun.net.client.defaultConnectTimeout", "10000");System.setProperty("sun.net.client.defaultReadTimeout", "10000");// 初始化ascClient需要的幾個參數final String product = "Dysmsapi";// 短信API產品名稱(短信產品名固定,無需修改)final String domain = "dysmsapi.aliyuncs.com";// 短信API產品域名(接口地址固定,無需修改)// 替換成你的AKfinal String accessKeyId = "LTAIak3CfAehK7cE";// 你的accessKeyId,參考本文檔步驟2final String accessKeySecret = "zsykwhTIFa48f8fFdU06GOKjHWHel4";// 你的accessKeySecret,參考本文檔步驟2// 初始化ascClient,暫時不支持多region(請勿修改)IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", ACCESS_KEY, SECRET_KEY);DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);IAcsClient acsClient = new DefaultAcsClient(profile);// 組裝請求對象SendSmsRequest request = new SendSmsRequest();// 使用post提交request.setMethod(MethodType.POST);// 必填:待發送手機號。支持以逗號分隔的形式進行批量調用,批量上限為1000個手機號碼,批量調用相對于單條調用及時性稍有延遲,驗證碼類型的短信推薦使用單條調用的方式request.setPhoneNumbers(phoneNumbers);// 必填:短信簽名-可在短信控制臺中找到request.setSignName(SIGN_NAEM);// 必填:短信模板-可在短信控制臺中找到request.setTemplateCode(templateCode);// 可選:模板中的變量替換JSON串,如模板內容為"親愛的${name},您的驗證碼為${code}"時,此處的值為// 友情提示:如果JSON中需要帶換行符,請參照標準的JSON協議對換行符的要求,比如短信內容中包含\r\n的情況在JSON中需要表示成\\r\\n,否則會導致JSON在服務端解析失敗//request.setTemplateParam("{\"code\":\""+param+"\"}");request.setTemplateParam(String.format("{\"%s\":\"%s\"}",PARAMETER_NAME,param));// 可選-上行短信擴展碼(擴展碼字段控制在7位或以下,無特殊需求用戶請忽略此字段)// request.setSmsUpExtendCode("90997");// 可選:outId為提供給業務方擴展字段,最終在短信回執消息中將此值帶回給調用者// request.setOutId("yourOutId");// 請求失敗這里會拋ClientException異常SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);if (sendSmsResponse.getCode() != null && sendSmsResponse.getCode().equals("OK")) {// 請求成功System.out.println("請求成功");}else{System.out.println(sendSmsResponse.getMessage());}} }

    3:RedisMessageConstant常量類:

    package com.itheima.health.constant;public interface RedisMessageConstant {static final String SENDTYPE_ORDER = "001";//用于緩存體檢預約時發送的驗證碼static final String SENDTYPE_LOGIN = "002";//用于緩存手機號快速登錄時發送的驗證碼static final String SENDTYPE_GETPWD = "003";//用于緩存找回密碼時發送的驗證碼 }

    【小結】

    步驟:

    –>創建工程–>導入坐標–>頁面–>配置文件(web.xml、springmvc.xml、spring-jedis.xml)

    –>導入通用組件(生成驗證碼、發送短信工具類、Redis存儲常量類)

    2.套餐列表頁面動態展示

    【目標】

    實現套餐列表功能

    訪問:首頁http://localhost:80

    點擊“體檢預約”,跳轉到“套餐列表”頁面

    【路徑】

    1:前臺代碼編寫

  • 在/pages/setmeal.html , 在mounted()鉤子函數里面
  • 完成需求: 1.使用axios請求服務器, 獲得數據 進行模型綁定 2.數據遍歷(v-for)

    2:后臺代碼編寫

    ? 1.創建類SetmealMobileController.java ,添加查詢所有的方法

    ? 拼接完整的圖片路徑

  • SetmealService與實現類添加查詢所有的方法
  • SetmealDao與映射文件 添加查詢所有的方法
  • 完成需求: 1:查詢所有的套餐

    【講解-需求】

    移動端首頁為/pages/index.html,效果如下:

    (1)在web.xml中配置:

    (2)訪問:首頁http://localhost:80

    (3)點擊體檢預約直接跳轉到體檢套餐列表頁面(/pages/setmeal.html)

    <a href="/pages/setmeal.html" class="link-page"><div class="type-title"><h3>體檢預約</h3><p>實時預約</p></div><div class="type-icon"><i class="icon-zhen"><span class="path1"></span><span class="path2"></span></i></div> </a>

    2.1. 前臺代碼

    2.1.1. 展示套餐信息

    (1)setmeal.html,遍歷v-for=“setmeal in setmealList”

    2.1.2. 獲取套餐列表數據

    <script>var vue = new Vue({el:'#app',data:{setmealList:[] // 套餐列表數據},// 掛載后mounted (){axios.get("/setmeal/getSetmeal.do").then((response)=>{if(response.data.flag){this.setmealList = response.data.data;}else{// 失敗的提示alert(response.data.message);}});}}); </script>

    mounted鉤子和created鉤子

    mounted鉤子:頁面被初始化后執行

    created鉤子:vue模型被初始化后執行

    mounted鉤子是在created鉤子后面執行的。

    2.2. 后臺代碼

    2.2.1. Controller

    在healthmobile_web工程中創建SetmealMobileController并提供getSetmeal方法,在此方法中通過Dubbo遠程調用套餐服務獲取套餐列表數據

    package com.itheima.health.controller;import com.alibaba.dubbo.config.annotation.Reference; import com.itheima.health.constant.MessageConstant; import com.itheima.health.entity.Result; import com.itheima.health.pojo.Setmeal; import com.itheima.health.service.SetmealService; import com.itheima.health.utils.QiNiuUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** Description: No Description* User: Eric*/ @RestController @RequestMapping("/setmeal") public class SetmealMobileController {@Referenceprivate SetmealService setmealService;/*** 查詢所有*/@GetMapping("/getSetmeal")public Result getSetmeal(){// 查詢所有的套餐List<Setmeal> list = setmealService.findAll();// 套餐里有圖片有全路徑嗎? 拼接全路徑list.forEach(s->{s.setImg(QiNiuUtils.DOMAIN + s.getImg());});return new Result(true, MessageConstant.GET_SETMEAL_LIST_SUCCESS,list);} }

    2.2.2. 服務接口

    在SetmealService服務接口中擴展findAll方法

    /*** 查詢所有* @return*/ List<Setmeal> findAll();

    2.2.3. 服務實現類

    在SetmealServiceImpl服務實現類中實現findAll方法

    /*** 查詢所有的套餐* @return*/ @Override public List<Setmeal> findAll() {return setmealDao.findAll(); }

    2.2.4. Dao接口

    在SetmealDao接口中擴展findAll方法

    /*** 查詢所有的套餐* @return*/ List<Setmeal> findAll();

    2.2.5. Mapper映射文件

    在SetmealDao.xml映射文件中擴展SQL語句

    <!--查詢所有--> <select id="findAll" resultType="setmeal">select * from t_setmeal </select>

    查看效果

    【小結】

    套餐列表功能需要展示發布的所有套餐,用于體檢人選擇指定套餐。

    從數據查詢出來的套餐信息的圖片只有圖片名稱,沒有全路徑,在Controller返回之前,設置完整的路徑

    3. 套餐詳情頁面動態展示

    【目標】

    套餐詳情頁面動態展示

    在套餐詳情頁面需要展示當前套餐的信息(包括圖片、套餐名稱、套餐介紹、適用性別、適用年齡)、此套餐包含的檢查組信息、檢查組包含的檢查項信息等。

    【路徑】

    前臺代碼編寫

  • 在/pages/setmeal_detail.html
  • 完成需求: 1.獲取請求參數中套餐id的值 2.獲取套餐詳細信息 3.展示套餐信息(套餐信息、檢查組集合、檢查項集合)

    后臺代碼編寫

    ? 1.類SetmealMobileController.java

    ? 2.類SetmealService.java

    ? 3.類SetmealServiceImpl.java

    ? 4.類SetmealDao.java

    ? 類CheckGroupDao

    ? 類CheckItemDao

    ? 5.配置文件SetmealDao.xml

    ? 配置文件CheckGroupDao.xml

    ? 配置文件CheckItemDao.xml

    完成需求: 1:根據套餐id查詢對應的套餐信息 2:查詢每個套餐對應的檢查組集合 3:查詢每個檢查組對應的檢查項集合

    【講解-需求】

    前面我們已經完成了體檢套餐列表頁面動態展示,點擊其中任意一個套餐則跳轉到對應的套餐詳情頁面(/pages/setmeal_detail.html),并且會攜帶此套餐的id作為參數提交。

    請求路徑格式:http://localhost/pages/setmeal_detail.html?id=10

    在套餐詳情頁面需要展示當前套餐的信息(包括圖片、套餐名稱、套餐介紹、適用性別、適用年齡)、此套餐包含的檢查組信息、檢查組包含的檢查項信息等。

    3.1. 前臺代碼

    3.1.1. 獲取請求參數中套餐id

    (1)在頁面中已經引入了healthmobile.js文件,此文件中已經封裝了getUrlParam方法可以根據URL請求路徑中的參數名獲取對應的值

    //獲取指定的URL參數值 http://localhost/pages/setmeal_detail.html?id=15 // 從url中獲取參數的值 function getUrlParam(paraName) {var url = document.location.toString();//http://localhost/pages/setmeal_detail.html?id=15//alert(url);var arrObj = url.split("?");// arrObj[0]=http://localhost/pages/setmeal_detail.html// arrObj[1]=id=15if (arrObj.length > 1) {//id=15var arrPara = arrObj[1].split("&");var arr;//arrPara[0] id=15for (var i = 0; i < arrPara.length; i++) {// id=15arr = arrPara[i].split("=");//arr[0]=id arr[1]=15if (arr != null && arr[0] == paraName) {return arr[1];}}return "";}else {return "";} }

    (2)在setmeal_detail.html中調用上面定義的方法獲取套餐id的值

    3.1.2. 獲取套餐詳細信息

    <script>var vue = new Vue({el:'#app',data:{setmeal:{}},methods:{toOrderInfo(){window.location.href = "orderInfo.html?id=" + id;}},mounted(){// 獲取套餐詳情信息axios.get("/setmeal/findDetailById.do?id=" + id).then((response) => {if(response.data.flag){this.setmeal = response.data.data;}});}}); </script>

    其中:imgUrl要指定七牛云的空間地址

    3.1.3. 展示套餐信息

    1:{{setmeal.name}}:套餐信息

    2:v-for="checkgroup in setmeal.checkGroups:檢查組信息

    3:v-for=“checkitem in checkgroup.checkItems”:檢查項信息

    套餐圖片的展示

    <!-- 頁面內容 --> <div class="contentBox"><div class="card"><div class="project-img"><img :src="setmeal.img" width="100%" height="100%" /></div><div class="project-text"><h4 class="tit">{{setmeal.name}}</h4><p class="subtit">{{setmeal.remark}}</p><p class="keywords"><span>{{setmeal.sex == '0' ? '性別不限' : setmeal.sex == '1' ? '男':'女'}}</span><span>{{setmeal.age}}</span></p></div></div><div class="table-listbox"><div class="box-title"><i class="icon-zhen"><span class="path1"></span><span class="path2"></span></i><span>套餐詳情</span></div><div class="box-table"><div class="table-title"><div class="tit-item flex2">項目名稱</div><div class="tit-item flex3">項目內容</div><div class="tit-item flex3">項目解讀</div></div><div class="table-content"><ul class="table-list"><li class="table-item" v-for="checkgroup in setmeal.checkGroups"><div class="item flex2">{{checkgroup.name}}</div><div class="item flex3"><label v-for="checkitem in checkgroup.checkItems">{{checkitem.name}}</label></div><div class="item flex3">{{checkgroup.remark}}</div></li></ul></div><div class="box-button"><a @click="toOrderInfo()" class="order-btn">立即預約</a></div></div></div> </div>

    3.2. 后臺代碼

    3.2.1. Controller

    在SetmealMobileController中提供findById方法

    /*** 查詢套餐詳情*/ @GetMapping("/findDetailById") public Result findDetailById(int id){// 調用服務查詢詳情Setmeal setmeal = setmealService.findDetailById(id);// 設置圖片的完整路徑setmeal.setImg(QiNiuUtils.DOMAIN + setmeal.getImg());return new Result(true, MessageConstant.QUERY_SETMEAL_SUCCESS,setmeal); }

    3.2.2. 服務接口

    在SetmealService服務接口中提供findById方法

    /*** 查詢套餐詳情* @param id* @return*/ Setmeal findDetailById(int id);

    3.2.3. 服務實現類

    在SetmealServiceImpl服務實現類中實現findById方法

    /*** 查詢套餐詳情* @param id* @return*/ @Override public Setmeal findDetailById(int id) {return setmealDao.findDetailById(id); }

    3.2.4. Dao接口

    1:在SetmealDao接口中提供findById方法

    /*** 查詢套餐詳情* @param id* @return*/ Setmeal findDetailById(int id);

    3.2.5. Mapper映射文件

    此處會使用mybatis提供的關聯查詢,在根據id查詢套餐時,同時將此套餐包含的檢查組都查詢出來,并且將檢查組包含的檢查項都查詢出來。

    1:SetmealDao.xml文件:

    <select id="findDetailById" parameterType="int" resultMap="setmealDetailMap">selects.*,sg.checkgroup_id, cg.name checkgroup_name, cg.remark as checkgroup_remark,cc.checkitem_id, ci.name checkitem_nameFromt_setmeal s, t_setmeal_checkgroup sg,t_checkgroup cg, t_checkgroup_checkitem cc,t_checkitem ciwheres.id=sg.setmeal_id and sg.checkgroup_id=cg.idand cg.id=cc.checkgroup_id and cc.checkitem_id=ci.idand s.id=#{id} </select> <!-- 1對多關系配置 套餐下有檢查組 檢查組下有檢查項 --> <resultMap id="setmealDetailMap" type="setmeal"><id property="id" column="id"/><result property="name" column="name" /><result property="code" column="code" /><result property="helpCode" column="helpCode" /><result property="sex" column="sex" /><result property="age" column="age" /><result property="price" column="price" /><result property="remark" column="remark" /><result property="attention" column="attention" /><result property="img" column="img" /><collection property="checkGroups" ofType="CheckGroup"><id property="id" column="checkgroup_id"/><result property="name" column="checkgroup_name"/><result property="remark" column="checkgroup_remark"/><collection property="checkItems" ofType="CheckItem"><id property="id" column="checkitem_id"/><result property="name" column="checkitem_name"/></collection></collection> </resultMap>

    【小結】

    1:掌握sql語句的聯合查詢(sql語句的嵌套查詢),應用在多對多場景 2:掌握使用<resultMap>完成映射 3:掌握使用<collection> 1對多 完成集合的封裝(<association> 1對1 完成對象的封裝)

    套餐詳情方式實現結果:

    推薦使用方式一,獲取1個連接,查詢一次就返回結果

    方式二: 獲取1個連接,期間需要執行多次查詢,查詢次數取決于檢查組的個數

    方式三:每次查詢都獲取新的連接,查詢的次數與方式二一樣。

    4.Freemarker頁面靜態化技術

    【目標】

    1:什么是頁面靜態化技術(作用:用于減少查詢數據庫的頻率, 利于SEO 搜索排名靠前)

    2:什么是Freemarker

    3:Freemarker的操作入門

    【路徑】

    1:什么是頁面靜態化技術(作用:用于減少查詢數據庫的頻率)

    2:什么是Freemarker(作用:可生成html靜態資源文件,從而達到減少查詢數據庫的頻率)

    3:Freemarker入門案例

    (1)搭建環境

    (2)創建模板文件

    (3)使用模板文件,生成靜態文件

    4:Freemarker相關指令

    (1)assign指令

    (2)include指令

    (3)if指令

    (4)list指令

    【講解】

    4.1. 頁面靜態化介紹

    本章課程中我們已經實現了移動端套餐列表頁面和套餐詳情頁面的動態展示。但是我們需要思考一個問題,就是對于這兩個頁面來說,每次用戶訪問這兩個頁面都需要查詢數據庫獲取動態數據進行展示,而且這兩個頁面的訪問量是比較大的,這就對數據庫造成了很大的訪問壓力,并且數據庫中的數據變化頻率并不高。那我們需要通過什么方法為數據庫減壓并提高系統運行性能呢?答案就是頁面靜態化。

    頁面靜態化其實就是將原來的動態網頁(例如通過ajax請求動態獲取數據庫中的數據并展示的網頁)改為通過靜態化技術生成的靜態網頁,這樣用戶在訪問網頁時,服務器直接給用戶響應靜態html頁面,沒有了動態查詢數據庫的過程。

    那么這些靜態HTML頁面還需要我們自己去編寫嗎?其實并不需要,我們可以通過專門的頁面靜態化技術幫我們生成所需的靜態HTML頁面,例如:Freemarker、thymeleaf, velocity等。

    頁面靜態化技術: 把頁面需要所有數據都寫死文件里,下次使用時,不需要再去查詢數據庫,直接用即可

    4.2. Freemarker介紹

    FreeMarker 是一個用 Java 語言編寫的模板引擎,它基于模板來生成文本輸出。FreeMarker與 Web 容器無關,即在 Web 運行時,它并不知道 Servlet 或 HTTP。它不僅可以用作表現層的實現技術,而且還可以用于生成 XML,JSP 或 Java 等。

    學習網站:http://freemarker.foofun.cn/

    Freemarker: 是一款文本模板引擎,運行后臺代碼。 Velocity, Thymeleaf

    作用: 實現頁面靜態化,通過模板來生成文件,往模板填寫數據即可

    4.3. Freemarker入門案例

    4.3.1. 環境搭建

    創建freemarkdemo工程并導入Freemarker的maven坐標

    <dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.23</version> </dependency>

    4.3.2. 創建模板文件

    模板文件中有四種元素:

    1、文本,直接輸出的部分
    2、注釋,即<#–…-->格式不會輸出
    3、插值(Interpolation):即${…}部分,將使用數據模型中的部分替代輸出
    4、FTL指令:FreeMarker指令,和HTML標記類似,名字前加#予以區分,不會輸出

    Freemarker的模板文件后綴可以任意,一般建議為ftl。

    在D盤創建ftl目錄,在ftl目錄中創建名稱為test.ftl的模板文件,內容如下:

    <html> <head><meta charset="utf-8"><title>Freemarker入門</title> </head> <body><#--我只是一個注釋,我不會有任何輸出 -->${name}你好,${message} </body> </html>

    4.3.3. 生成文件

    使用步驟:

    第一步:創建一個 Configuration 對象,直接 new 一個對象。構造方法的參數就是 freemarker的版本號。

    第二步:設置模板文件所在的路徑。

    第三步:設置模板文件使用的字符集。一般就是 utf-8。

    第四步:加載一個模板,創建一個模板對象。

    第五步:創建一個模板使用的數據集,可以是 pojo 也可以是 map。一般是 Map。

    第六步:創建一個 Writer 對象,一般創建 FileWriter 對象,指定生成的文件名。

    第七步:調用模板對象的 process 方法輸出文件。

    第八步:關閉流。

    public static void main(String[] args) throws Exception{//1.創建配置類Configuration configuration=new Configuration(Configuration.getVersion());//2.設置模板所在的目錄 configuration.setDirectoryForTemplateLoading(new File("D:\\ftl"));//3.設置字符集configuration.setDefaultEncoding("utf-8");//4.加載模板Template template = configuration.getTemplate("test.ftl");//5.創建數據模型Map map=new HashMap();map.put("name", "張三");map.put("message", "歡迎來到傳智播客!");//6.創建Writer對象Writer out =new FileWriter(new File("d:\\test.html"));//7.輸出template.process(map, out);//8.關閉Writer對象out.close(); }

    上面的入門案例中Configuration配置對象是自己創建的,字符集和模板文件所在目錄也是在Java代碼中指定的。在項目中應用時可以將Configuration對象的創建交由Spring框架來完成,并通過依賴注入方式將字符集和模板所在目錄注入進去。

    4.4. Freemarker指令

    4.4.1. assign指令

    assign指令用于在頁面上定義一個變量

    (1)定義簡單類型

    <#assign linkman="周先生"> 聯系人:${linkman}

    (2)定義對象類型

    <#assign info={"mobile":"13812345678",'address':'北京市昌平區'} > 電話:${info.mobile} 地址:${info.address}

    4.4.2. include指令

    include指令用于模板文件的嵌套, 用于頁面布局, 頁面公共內容的抽取

    (1)創建模板文件head.ftl

    <h1>黑馬程序員</h1>

    (2)修改入門案例中的ftl.ftl,在ftl.ftl模板文件中使用include指令引入上面的模板文件

    <#include "head.ftl"/>

    4.4.3. if指令

    if指令用于判斷

    (1)在模板文件中使用if指令進行判斷

    <#if success=true>你已通過實名認證 <#else> 你未通過實名認證 </#if>

    (2)在java代碼中為success變量賦值

    map.put("success", true);

    在freemarker的判斷中,可以使用= 也可以使用

    if指令可以配合assign success=true使用

    <#assign success1=true> <#if success1=true>你已通過實名認證1 <#else> 你未通過實名認證1 </#if>

    4.4.4. list指令

    list指令用于遍歷

    (1)在模板文件中使用list指令進行遍歷

    <#list goodsList as goods>商品名稱: ${goods.name} 價格:${goods.price}<br> </#list>

    (2)在java代碼中為goodsList賦值

    List goodsList=new ArrayList();Map goods1=new HashMap(); goods1.put("name", "蘋果"); goods1.put("price", 5.8);Map goods2=new HashMap(); goods2.put("name", "香蕉"); goods2.put("price", 2.5);Map goods3=new HashMap(); goods3.put("name", "橘子"); goods3.put("price", 3.2);goodsList.add(goods1); goodsList.add(goods2); goodsList.add(goods3);map.put("goodsList", goodsList);

    【小結】

    http://freemarker.foofun.cn/ 官方

    1:什么是頁面靜態化技術,返回給瀏覽器時,所有的數據都已經寫好了,不需要再訪問服務去獲取數據,頁面中的內容已經寫死了。 減少數據庫訪問、提高seo

    2:什么是Freemarker(作用:可生成html靜態資源文件,從而達到減少查詢數據庫的頻率)

    是一種模板引擎,通過往模板填充數據即生成文件。

    3:Freemarker入門案例

    (1)搭建環境 (maven,引入依賴)

    (2)創建模板文件(【utf-8】【utf-8】【utf-8】另存為選擇utf-8, ANSI->latin-1 -> ISO-8859-1)

    (3)使用模板文件(加載文件,配置模板文件目錄,設置默認的編碼utf-8,獲取模板),生成靜態文件(數據模型map)

  • 通過版本來創建配置類
  • 設置模板路徑
  • 設置編碼
  • 通過文件名獲取模板
  • 創建數據模型Map<String,Object> 實體類
  • process方法,填充數據到輸出文件里
  • 關閉流
  • 4:Freemarker相關指令

    (1)assign指令 聲明變量且給它賦值, 如果數據模型中有相同的變量,則以assin有效

    (2)include指令 導入其它模板文件,頁面布局,公共內容的抽取

    (3)if指令(常用) 判斷輸出

    (4)list指令(常用) 遍歷集合 list as item

  • 如果數據模型dataModel與assign使用同一變量時,則以assign為準
  • 如果模板中使用了某個變量在dataModel沒有定義/沒有assign,則會報錯
  • 模板中使用的變量不能為null, 否則報錯
  • 5. 生成移動端靜態頁面,整合項目

    前面我們已經學習了Freemarker的基本使用方法,下面我們就可以將Freemarker應用到項目中,幫我們生成移動端套餐列表靜態頁面和套餐詳情靜態頁面。接下來我們需要思考幾個問題:

    (1)什么時候生成靜態頁面比較合適呢?套餐的增刪改(列表, 修改的套餐詳情)

    (2)將靜態頁面生成到什么位置呢? health_mobile/webapp/pages/

    (3)應該生成幾個靜態頁面呢? 套餐列表.html, setmeal_{id}.html

    對于第一個問題,應該是當套餐數據發生改變時,需要生成靜態頁面,即我們通過后臺系統修改套餐數據(包括新增、刪除、編輯)時。

    對于第二個問題,如果是在開發階段可以將文件生成到項目工程中,如果上線后可以將文件生成到移動端系統運行的tomcat中。

    對于第三個問題,套餐列表只需要一個頁面就可以了,在這個頁面中展示所有的套餐列表數據即可。套餐詳情頁面需要有多個,即一個套餐應該對應一個靜態頁面。

    【目標】

    1:在傳智健康前端系統中使用頁面靜態化Freemarker技術

    2:使用Freemarker生成 套餐列表靜態化頁面 列表頁

    3:使用Freemarker生成 套餐詳情靜態化頁面 多個,以id區分

    4:Freemarker與spring的整合使用 配置文件

    【路徑】

    1:環境搭建

    2:創建模板文件

    (1)mobile_setmeal.ftl

    (2)mobile_setmeal_detail.ftl

    3:spring整合Freemarker

    (1)freemarker.properties

    (2)applicationContext-freemarker.xml

    4:生成靜態頁面

    生成靜態頁面的時機,在新增套餐的時候生成靜態頁面(套餐列表及套餐詳情)

    【講解】

    5.1. 環境搭建

    在health_parent工程的pom文件中導入Freemarker的maven坐標

    在health_common工程的pom文件中導入Freemarker的maven坐標

    <dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId> </dependency>

    5.2. 創建模板文件

    在health_service工程的WEB-INF目錄中創建ftl目錄,在ftl目錄中創建模板文件mobile_setmeal.ftl和mobile_setmeal_detail.ftl文件,前者是用于生成套餐列表頁面的模板文件,后者是生成套餐詳情頁面的模板文件

    (1)mobile_setmeal.ftl

    <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><!-- 上述3個meta標簽*必須*放在最前面,任何其他內容都*必須*跟隨其后! --><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no,minimal-ui"><meta name="description" content=""><meta name="author" content=""><link rel="icon" href="../img/asset-favico.ico"><title>預約</title><link rel="stylesheet" href="../css/page-health-order.css" /> </head> <body data-spy="scroll" data-target="#myNavbar" data-offset="150"> <div class="app" id="app"><!-- 頁面頭部 --><div class="top-header"><span class="f-left"><i class="icon-back" onclick="history.go(-1)"></i></span><span class="center">傳智健康</span><span class="f-right"><i class="icon-more"></i></span></div><!-- 頁面內容 --><div class="contentBox"><div class="list-column1"><ul class="list"><#list setmealList as setmeal><li class="list-item" ><a class="link-page" href="setmeal_${setmeal.id}.html"><img class="img-object f-left" src="${setmeal.img}" alt=""><div class="item-body"><h4 class="ellipsis item-title">${setmeal.name}</h4><p class="ellipsis-more item-desc">${setmeal.remark}</p><p class="item-keywords"><span><#if setmeal.sex=='0'>性別不限<#else><#if setmeal.sex=='1'>男<#else></#if></#if></span><span>${setmeal.age}</span></p></div></a></li></#list></ul></div></div> </div> </body>

    注意1:上面的數據需保證數據庫中不能為null。

    注意2: 套餐列表 的值里要設置圖片的完整路徑。

    注意3:上面模板文件中每個套餐對應的超鏈接如下:

    可以看到,鏈接的地址是動態構成的,

    如果套餐的id為1,則對應的超鏈接地址為setmeal_1.html;

    如果套餐的id為5,則對應的超鏈接地址為setmeal_5.html。

    所以我們需要為每個套餐生成一個套餐詳情靜態頁面。

    (2)mobile_setmeal_detail.ftl

    <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><!-- 上述3個meta標簽*必須*放在最前面,任何其他內容都*必須*跟隨其后! --><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no,minimal-ui"><meta name="description" content=""><meta name="author" content=""><link rel="icon" href="../img/asset-favico.ico"><title>預約詳情</title><link rel="stylesheet" href="../css/page-health-orderDetail.css" /> </head> <body data-spy="scroll" data-target="#myNavbar" data-offset="150"> <div id="app" class="app"><!-- 頁面頭部 --><div class="top-header"><span class="f-left"><i class="icon-back" onclick="history.go(-1)"></i></span><span class="center">傳智健康</span><span class="f-right"><i class="icon-more"></i></span></div><!-- 頁面內容 --><div class="contentBox"><div class="card"><div class="project-img"><img src="${setmeal.img}" width="100%" height="100%" /></div><div class="project-text"><h4 class="tit">${setmeal.name}</h4><p class="subtit">${setmeal.remark}</p><p class="keywords"><span><#if setmeal.sex=='0'>性別不限<#else><#if setmeal.sex=='1'>男<#else></#if></#if></span><span>${setmeal.age}</span></p></div></div><div class="table-listbox"><div class="box-title"><i class="icon-zhen"><span class="path1"></span><span class="path2"></span></i><span>套餐詳情</span></div><div class="box-table"><div class="table-title"><div class="tit-item flex2">項目名稱</div><div class="tit-item flex3">項目內容</div><div class="tit-item flex3">項目解讀</div></div><div class="table-content"><ul class="table-list"><#list setmeal.checkGroups as checkgroup><li class="table-item"><div class="item flex2">${checkgroup.name}</div><div class="item flex3"><#list checkgroup.checkItems as checkitem><label>${checkitem.name}</label></#list></div><div class="item flex3">${checkgroup.remark}</div></li></#list></ul></div><div class="box-button"><a href="orderInfo.html?id=${setmeal.id}" class="order-btn">立即預約</a></div></div></div></div> </div> </body>

    注意:改變七牛云的訪問地址

    5.3. 配置文件

    (1)在health_service 工程中創建屬性文件freemarker.properties。這個路徑要改你的health_mobile中的webapp/pages目錄,全路徑

    out_put_path=D:/ideaProjects/health_parent/health_mobile/src/main/webapp/pages

    通過上面的配置可以指定將靜態HTML頁面生成的目錄位置

    (2)在health_service工程的Spring配置文件中配置,創建applicationContext-freemarker.xml

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><context:property-placeholder location="classpath:freemarker.properties"/><bean id="freemarkerConfig"class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"><!--指定模板文件所在目錄--><property name="templateLoaderPath" value="/WEB-INF/ftl/" /><!--指定字符集--><property name="defaultEncoding" value="UTF-8" /></bean> </beans>

    (3)在applicationContext-service.xml中導入applicationContext-freemarker.xml

    5.4. 生成靜態頁面

    修改health_service_provider工程中的SetmealServiceImpl類的add方法,加入生成靜態頁面的邏輯。

    /*** 添加套餐* @param setmeal* @param checkgroupIds*/ @Override @Transactional public void add(Setmeal setmeal, Integer[] checkgroupIds) {// 添加套餐信息setmealDao.add(setmeal);// 獲取套餐的idInteger setmealId = setmeal.getId();// 添加套餐與檢查組的關系if(null != checkgroupIds){for (Integer checkgroupId : checkgroupIds) {setmealDao.addSetmealCheckGroup(setmealId, checkgroupId);}}//新增套餐后需要重新生成靜態頁面generateMobileStaticHtml(); }/*** 生成 列表及詳情靜態頁面*/ private void generateMobileStaticHtml(){try {// 套餐列表靜態頁面generateSetmealListHtml();// 套餐詳情靜態頁面 生成單就行了,為了測試方便,生成所有的generateSetmealDetailHtml();} catch (Exception e) {throw new RuntimeException(e);} }/*** 生成套餐詳情靜態頁面*/ private void generateSetmealDetailHtml() throws Exception {// 把所有套餐都生成詳情頁面 方便測試List<Setmeal> setmealList = setmealDao.findAll();// setmealList中的套餐是沒有詳情信息,即沒有檢查組也沒有檢查項的信息,要查詢一遍for (Setmeal setmeal : setmealList) {// 獲取套餐詳情Setmeal setmealDetail = setmealDao.findDetailById(setmeal.getId());// 設置套餐的圖片路徑setmealDetail.setImg(QiNiuUtils.DOMAIN + setmealDetail.getImg());// 生成詳情頁面generateDetailHtml(setmealDetail);} }/*** 生成套餐詳情頁面* @param setmealDetail*/ private void generateDetailHtml(Setmeal setmealDetail) throws Exception {// 獲取模板 套餐列表的模板Template template = freeMarkerConfigurer.getConfiguration().getTemplate("mobile_setmeal_detail.ftl");Map<String, Object> dataMap = new HashMap<String,Object>();dataMap.put("setmeal", setmealDetail);File setmealDetailFile = new File(out_put_path, "setmeal_" + setmealDetail.getId() + ".html");template.process(dataMap, new BufferedWriter(new OutputStreamWriter(new FileOutputStream(setmealDetailFile),"utf-8"))); }/*** 生成 套餐列表靜態頁面*/ private void generateSetmealListHtml() throws Exception {// 獲取模板 套餐列表的模板Template template = freeMarkerConfigurer.getConfiguration().getTemplate("mobile_setmeal.ftl");// 獲取數據模型List<Setmeal> setmealList = setmealDao.findAll();// 圖片地址setmealList.forEach(s->{s.setImg(QiNiuUtils.DOMAIN + s.getImg());});Map<String, Object> dataMap = new HashMap<String,Object>();dataMap.put("setmealList", setmealList);// 給模板填充數據 new OutputStreamWriter要指定編碼格式,否則中文亂碼// 生成的文件 c:/sz89/health_parent/health_mobile/src/main/webapp/pages/m_setmeal.htmlFile setmealListFile = new File(out_put_path, "m_setmeal.html");template.process(dataMap, new BufferedWriter(new OutputStreamWriter(new FileOutputStream(setmealListFile),"utf-8"))); }

    通過上面代碼可以看到,我們生成的套餐列表頁面名稱為m_setmeal.html,為了能夠在移動端訪問到此頁面,需要將移動端工程中的/pages/index.html頁面的超鏈接地址進行修改:

    之前為:

    修改為:

    【小結】

    1:環境搭建

    2:創建模板文件 模板文件的編碼一定是utf-8

    (1)mobile_setmeal.ftl

    (2)mobile_setmeal_detail.ftl

    3:spring整合Freemarker

    (1)freemarker.properties 生成的文件存儲目錄

    (2)applicationContext-freemarker.xml 整合 defaultEncoding=utf-8

    4:生成靜態頁面

    生成靜態頁面的時機,在新增套餐的時候生成靜態頁面(套餐列表及套餐詳情)

    新增時:生成套餐列表頁面,生成新的套餐詳情頁面

    修改時:生成套餐列表頁面,生成修改的套餐詳情頁面

    刪除時:生成套餐列表頁面,(生成刪除的套餐詳情頁面(下架, 所有鏈接要失效, 或者把頁面移除))

    布署項目時,要觸發一次生成所有的靜態頁面

    應該用后臺任務來跑(lastUpdated, 任務表lastUpdate)

    new OutputStreamWriter(os, “utf-8”)

    總結

    以上是生活随笔為你收集整理的【项目】健康项目day5总结的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。