家居网购项目(权限验证+事务管理)

文章目录

    • 1.过滤器权限认证
        • 1.程序框架图
        • 2.web.xml
        • 3.编写AdminAuthorization
        • 4.编写MemberAuthorization
        • 5.细节
        • 6.结果展示
          • 1.未登录可以任意浏览商品
          • 2.点击添加购物车提示登录
          • 3.点击后台管理,提示管理员登录
          • 4.也做了其余资源的访问验证
    • 2.事务管理
        • 1.思路分析
        • 2.重写JDBCUtilsByDruid
          • 1.getConnection()方法
          • 2.commit()方法
          • 3.rollback()方法
          • 4.删除BasicDao中的所有关闭数据库连接的语句
          • 5.编写过滤器TransactionFilter
          • 6.配置web.xml
          • 7.查看调用可能出现问题的方法是不是抛出了异常
        • 3.结果展示
          • 1.将OrderDaoImpl中的sql故意写错
          • 2.执行保存订单操作
          • 3.查看数据库信息(回滚了,没有生成记录)
    • 3.统一错误页面
        • 1.需求分析
        • 2.修改web.xml
        • 3.导入两个页面
        • 4.修改TransactionFilter,将异常抛给tomcat
        • 5.结果展示
    • 4.会员订单管理—分页查询
        • 需求分析
        • 1.编写dao层
          • 1.修改OrderDao
          • 2.修改OrderDaoImpl
          • 3.单元测试
        • 2.编写service层
          • 1.修改OrderService
          • 2.修改OrderServiceImpl
          • 3.单元测试
        • 3.编写web层
          • 1.修改OrderServlet
          • 2.修改order_manage.jsp
            • 1.显示信息
            • 2.分页导航条
        • 4.结果演示
          • 1.会员登录
          • 2.会员点击订单管理只会显示当前会员的历史订单
    • 5.管理员订单管理—分页查询
        • 需求分析
        • 1.编写dao层
          • 1.修改OrderDao
          • 2.修改OrderDaoImpl
        • 2.编写service层
          • 1.修改OrderService
          • 2.修改OrderServiceImpl
        • 3.编写web层
          • 1.修改OrderServlet
          • 2.修改order_manage.jsp
            • 1.显示信息
            • 2.分页导航条
        • 4.结果展示

1.过滤器权限认证

1.程序框架图

image-20240211092524610

2.web.xml
    <filter><filter-name>AdminAuthorization</filter-name><filter-class>com.sxs.furns.filter.AdminAuthorization</filter-class><init-param><!--设置排除的uri--><param-name>excludeUrls</param-name><param-value>/jiaju_mail/views/manage/manage_login.jsp</param-value></init-param></filter><!--除了登录的管理员页面都要进行管理员验证--><filter-mapping><filter-name>AdminAuthorization</filter-name><url-pattern>/views/manage/*</url-pattern></filter-mapping><!--家居管理--><filter-mapping><filter-name>AdminAuthorization</filter-name><url-pattern>/manage/furnServlet/*</url-pattern></filter-mapping><filter><filter-name>MemberAuthorization</filter-name><filter-class>com.sxs.furns.filter.MemberAuthorization</filter-class></filter><!--登录成功的界面,需要普通权限验证--><filter-mapping><filter-name>MemberAuthorization</filter-name><url-pattern>/views/member/login_ok.jsp</url-pattern></filter-mapping><!--所有的购物车界面,需要普通权限验证--><filter-mapping><filter-name>MemberAuthorization</filter-name><url-pattern>/views/cart/*</url-pattern></filter-mapping><!--购物车和订单servlet,需要普通权限验证--><filter-mapping><filter-name>MemberAuthorization</filter-name><url-pattern>/orderServlet/*</url-pattern></filter-mapping><filter-mapping><filter-name>MemberAuthorization</filter-name><url-pattern>/cartServlet/*</url-pattern></filter-mapping>
3.编写AdminAuthorization
package com.sxs.furns.filter;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;/*** @author 孙显圣* @version 1.0*/
public class AdminAuthorization implements Filter {private List<String> excludeUrlsList = new ArrayList<>(); //排除的资源@Overridepublic void init(FilterConfig filterConfig) throws ServletException {//读取配置文件中的信息String excludeUrls = filterConfig.getInitParameter("excludeUrls");//按照,分割,并添加到集合中String[] split = excludeUrls.split(",");for (String s : split) {excludeUrlsList.add(s);}//放到servletContext中ServletContext servletContext = filterConfig.getServletContext();servletContext.setAttribute("excludeUrls", excludeUrlsList);}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//查看session中的admin是否为trueHttpSession session = ((HttpServletRequest) servletRequest).getSession();Object admin = session.getAttribute("admin");//获取uriString requestURI = ((HttpServletRequest) servletRequest).getRequestURI();//获取排除的uriList<String> excludeUrls = (List<String>) servletRequest.getServletContext().getAttribute("excludeUrls");//遍历并匹配for (String excludeUrl : excludeUrls) {System.out.println(excludeUrl);//如果匹配直接放行if (excludeUrl.equals(requestURI)) {filterChain.doFilter(servletRequest, servletResponse);return;}}if (admin != null) {//放行filterChain.doFilter(servletRequest, servletResponse);} else {//重定向到管理员登录界面((HttpServletResponse) servletResponse).sendRedirect("/jiaju_mail/views/manage/manage_login.jsp");}}@Overridepublic void destroy() {}
}
4.编写MemberAuthorization
package com.sxs.furns.filter;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;/*** 普通权限认证* @author 孙显圣* @version 1.0*/
public class MemberAuthorization implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpSession session = ((HttpServletRequest) servletRequest).getSession();//进行普通权限认证Object username = session.getAttribute("username");Object admin = session.getAttribute("admin");if (username != null || admin != null) {System.out.println("普通权限验证成功!");//放行filterChain.doFilter(servletRequest, servletResponse);} else {//重定向到用户登录界面((HttpServletResponse) servletResponse).sendRedirect("/jiaju_mail/views/member/login.jsp");}}@Overridepublic void destroy() {}
}
5.细节

如果想要过滤一个文件夹下除了某个资源的其余资源,可以将排除的资源URI其放在init-param中,然后通过filter的init方法将其放到servletContext中,然后在doFilter中直接放行

6.结果展示
1.未登录可以任意浏览商品

image-20240211114953238

2.点击添加购物车提示登录

image-20240211115035714

image-20240211115041741

3.点击后台管理,提示管理员登录

image-20240211115128144

image-20240211115115218

4.也做了其余资源的访问验证

2.事务管理

1.思路分析

image-20240211142121222

image-20240211142140091

2.重写JDBCUtilsByDruid
1.getConnection()方法
    private static ThreadLocal<Connection> threadLocalConn = new ThreadLocal<>(); //用于存放本线程的连接//重写getConnection方法,使其每个线程获取的是同一个连接public static Connection getConnection() {try {Connection connection = threadLocalConn.get(); //从threadlocal中获取当前线程的连接//如果没有获取到,则放入一个连接if (connection == null) {connection = dataSource.getConnection();connection.setAutoCommit(false); //取消自动提交threadLocalConn.set(connection);}return connection;} catch (SQLException e) {throw new RuntimeException(e);}}
2.commit()方法
    //提交事务public static void commit() {//获取连接Connection connection = threadLocalConn.get();//如果连接有效则提交事务并关闭连接if (connection != null) {try {connection.commit();} catch (SQLException e) {throw new RuntimeException(e);} finally {try {connection.close();} catch (SQLException e) {throw new RuntimeException(e);}}}//清除threadlocal的连接threadLocalConn.remove();}
3.rollback()方法
    //回滚事务public static void rollback() {Connection connection = threadLocalConn.get();if (connection != null) {try {connection.rollback();} catch (SQLException e) {throw new RuntimeException(e);} finally {try {connection.close();} catch (SQLException e) {throw new RuntimeException(e);}}}threadLocalConn.remove();}
4.删除BasicDao中的所有关闭数据库连接的语句

原因:数据库连接的关闭,交给最后提交事务或者回滚事务的时候再执行

image-20240211152822089

5.编写过滤器TransactionFilter
package com.sxs.furns.filter;import com.sxs.furns.utils.JDBCUtilsByDruid;import javax.servlet.*;
import java.io.IOException;/*** @author 孙显圣* @version 1.0*/
public class TransactionFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain){//首先放行try {filterChain.doFilter(servletRequest, servletResponse);JDBCUtilsByDruid.commit();} catch (Exception e) {//如果在这次线程中有异常发生,则回滚System.out.println("发现异常,事务回滚!");JDBCUtilsByDruid.rollback();}}@Overridepublic void destroy() {}
}
6.配置web.xml
    <!--在权限验证之后再编写事务过滤器--><filter><filter-name>TransactionFilter</filter-name><filter-class>com.sxs.furns.filter.TransactionFilter</filter-class></filter><!--对所有请求进行过滤--><filter-mapping><filter-name>TransactionFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
7.查看调用可能出现问题的方法是不是抛出了异常

所有的servlet都是basicservlet反射调用的,并且都抛出了异常,这个异常最后会经过过滤器,所以可以在过滤器中捕获到异常

image-20240211153711602

3.结果展示
1.将OrderDaoImpl中的sql故意写错

image-20240211154703922

2.执行保存订单操作

image-20240211165406002

image-20240211165420900

image-20240211165433434

3.查看数据库信息(回滚了,没有生成记录)

image-20240211165458599

image-20240211165508047

3.统一错误页面

1.需求分析

image-20240211170301996

2.修改web.xml
    <!--配置错误页面--><error-page><error-code>404</error-code><location>/views/error/404.jsp</location></error-page><error-page><error-code>500</error-code><location>/views/error/500.jsp</location></error-page>
3.导入两个页面

image-20240211171312340

4.修改TransactionFilter,将异常抛给tomcat

image-20240211171629237

5.结果展示

image-20240211171404475

image-20240211171743485

4.会员订单管理—分页查询

需求分析

可以返回已登录的会员的历史订单信息

1.编写dao层
1.修改OrderDao
    //3.获取当前页需要展示的数据public List<Order> getPageItems(int begin, int pageSize, String id);//4.获取一共的记录条数public Integer getTotalRow(String id);
2.修改OrderDaoImpl
    /*** 分页查询订单* @param begin* @param pageSize* @return*/@Overridepublic List<Order> getPageItems(int begin, int pageSize, String id) {String sql = "select id, create_time as createTime, price, status, member_id as memberId from `order` where id like ? limit ?, ?";return queryMulti(sql, Order.class, "%-" + id , begin, pageSize);}/*** 获取总记录条数* @return*/@Overridepublic Integer getTotalRow(String id) {String sql = "select count(*) from `order` where id like ?";return ((Number) queryScalar(sql,"%-" + id)).intValue();}
3.单元测试
    @Testpublic void getPageItems() {//查询id为1的会员的订单信息List<Order> pageItems = orderDao.getPageItems(0, 3, "1");for (Order pageItem : pageItems) {System.out.println(pageItem);}}@Testpublic void getTotalRow() {System.out.println("总的记录数为:" + orderDao.getTotalRow("1"));}

image-20240211193017269

image-20240211200733207

2.编写service层
1.修改OrderService
    //3.分页显示订单public Page<Order> getPageItems(int pageNo, int pageSize, String username);
2.修改OrderServiceImpl
    /*** 根据当前页号和分页的大小返回对应的page对象** @param pageNo* @param pageSize* @return*/@Overridepublic Page<Order> getPageItems(int pageNo, int pageSize, String username) {//根据姓名获取用户idMember member = memberDao.queryMemberByUsername(username);//获取总记录数Integer totalRow = orderDao.getTotalRow(member.getId().toString());//计算总页数Integer pageTotalCount = totalRow / pageSize;if (totalRow % pageSize > 0) {pageTotalCount ++;}//计算beginint begin = (pageNo - 1) * pageSize;//获取当页的所有数据List<Order> pageItems = orderDao.getPageItems(begin, pageSize, member.getId().toString());//填充pagePage<Order> orderPage = new Page<>();orderPage.setPageNo(pageNo);orderPage.setPageSize(pageSize);orderPage.setPageTotalCount(pageTotalCount);orderPage.setTotalRow(totalRow);orderPage.setItems(pageItems);return orderPage;}
3.单元测试
    OrderService orderService = new OrderServiceImpl();@Testpublic void getPageItems() {Page<Order> pageItems = orderService.getPageItems(1, 3, "admin");System.out.println(pageItems);}

image-20240211194809877

3.编写web层
1.修改OrderServlet
    //分页显示订单public void showPageItems(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取数据Integer pageNo = DataUtils.parseInt(req.getParameter("pageNo"), 1); //默认第一页Integer pageSize = DataUtils.parseInt(req.getParameter("pageSize"), 6); //默认六条记录String username = req.getParameter("username");//获取page对象Page<Order> page = orderService.getPageItems(pageNo, pageSize, username);//请求转发req.setAttribute("page", page);req.getRequestDispatcher("/views/order_manage/order_manage.jsp").forward(req, resp);}
2.修改order_manage.jsp
1.显示信息

image-20240211201128675

2.分页导航条

image-20240211201147642

4.结果演示
1.会员登录

image-20240211202317112

2.会员点击订单管理只会显示当前会员的历史订单

image-20240211202415806

5.管理员订单管理—分页查询

需求分析

管理员的订单管理,显示的是所有会员的订单

1.编写dao层
1.修改OrderDao
    //5.管理员获取总体的记录条数public Integer getTotalRow();//6.管理员获取当前页需要展示的数据public List<Order> getPageItems(int begin, int pageSize);
2.修改OrderDaoImpl
    @Overridepublic Integer getTotalRow() {String sql = "select count(*) from `order`";return ((Number) queryScalar(sql)).intValue();}@Overridepublic List<Order> getPageItems(int begin, int pageSize) {String sql = "select id, create_time as createTime, price, status, member_id as memberId from `order` limit ?, ?";return queryMulti(sql, Order.class, begin, pageSize);}
2.编写service层
1.修改OrderService
    //4.管理员分页显示订单public Page<Order> getPageItems(int pageNo, int pageSize);
2.修改OrderServiceImpl
    @Overridepublic Page<Order> getPageItems(int pageNo, int pageSize) {//获取总记录数Integer totalRow = orderDao.getTotalRow();//计算总页数Integer pageTotalCount = totalRow / pageSize;if (totalRow % pageSize > 0) {pageTotalCount ++;}//计算beginint begin = (pageNo - 1) * pageSize;//获取当页的所有数据List<Order> pageItems = orderDao.getPageItems(begin, pageSize);//填充pagePage<Order> orderPage = new Page<>();orderPage.setPageNo(pageNo);orderPage.setPageSize(pageSize);orderPage.setPageTotalCount(pageTotalCount);orderPage.setTotalRow(totalRow);orderPage.setItems(pageItems);return orderPage;}
3.编写web层
1.修改OrderServlet
    //管理员分页显示订单public void showAllPageItems(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取数据Integer pageNo = DataUtils.parseInt(req.getParameter("pageNo"), 1); //默认第一页Integer pageSize = DataUtils.parseInt(req.getParameter("pageSize"), 6); //默认六条记录//获取page对象Page<Order> page = orderService.getPageItems(pageNo, pageSize);//请求转发req.setAttribute("page", page);req.getRequestDispatcher("/views/manage/order_manage.jsp").forward(req, resp);}
2.修改order_manage.jsp
1.显示信息

image-20240211211148413

2.分页导航条

image-20240211211203728

4.结果展示

image-20240211211304583

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/818935.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

程序猿之路

我接触计算机算对自己来说是比较晚的了&#xff0c;上初中的时候就有微机课&#xff0c;但是在那个小县城&#xff0c;上课也只是3个人共用一个电脑&#xff0c;我初中整个过程只会开关机&#xff0c;哈哈&#xff0c;虽然学过word&#xff0c;但是无奈&#xff0c;我插不上手呀…

开源相机管理库Aravis例程学习(三)——注册回调multiple-acquisition-callback

开源相机管理库Aravis例程学习&#xff08;三&#xff09;——回调multiple-acquisition-callback 简介例程代码arv_camera_create_streamArvStreamCallbackTypeArvStreamCallback 简介 本文针对官方例程中的&#xff1a;02-multiple-acquisition-callback做简单的讲解。 ara…

C语言 | Leetcode C语言题解之第30题串联所有单词的子串

题目&#xff1a; 题解&#xff1a; typedef struct {char key[32];int val;UT_hash_handle hh; } HashItem;int* findSubstring(char * s, char ** words, int wordsSize, int* returnSize){ int m wordsSize, n strlen(words[0]), ls strlen(s);int *res (int *)mall…

【opencv】示例-videocapture_starter.cpp 从视频文件、图像序列或连接到计算机的摄像头中捕获帧...

/** * file videocapture_starter.cpp * brief 一个使用OpenCV的VideoCapture与捕获设备&#xff0c;视频文件或图像序列的入门示例 * 就像CV_PI一样简单&#xff0c;对吧&#xff1f; * * 创建于: 2010年11月23日 * 作者: Ethan Rublee * * 修改于: 2013年4月17日 * …

金蝶云星空与领星ERP对接集成分布式调入单查询打通添加/编辑本地产品

金蝶云星空与领星ERP对接集成分布式调入单查询打通添加/编辑本地产品 接通系统&#xff1a;金蝶云星空 金蝶K/3Cloud结合当今先进管理理论和数十万家国内客户最佳应用实践&#xff0c;面向事业部制、多地点、多工厂等运营协同与管控型企业及集团公司&#xff0c;提供一个通用的…

Unity 扩展自定义编辑器窗口

在Assets文件夹路径下任意位置创建Editor文件夹&#xff0c;将扩展编辑器的代码放在Editor文件夹下 生成编辑器窗口 代码中首先引用命名空间 using UnityEditor; 然后将创建的类继承自EditorWindow public class MenuEditor : EditorWindow 然后通过扩展编辑器菜单功能调用…

51-40 Align your Latents,基于LDM的高分辨率视频生成

由于数据工程、仿真测试工程&#xff0c;咱们不得不进入AIGC图片视频生成领域。兜兜转转&#xff0c;这一篇与智驾场景特别密切。23年4月&#xff0c;英伟达Nvidia联合几所大学发布了带文本条件融合、时空注意力的Video Latent Diffusion Models。提出一种基于LDM的高分辨率视…

MySQL8.0.36-社区版:通用语法(2)

语法格式规范 sql语句可以以单号或者多行为书写&#xff0c;以分号结尾 可以使用空格或者缩进来增加可读性 mysql的sql语句不区分大小写&#xff0c;但是推荐大写关键字 注释分为单号注释和多行 单号注释&#xff1a;--内容 或者 # 内容 多行注释/* 注释内容 */ sql语句的…

面试算法-175-将有序数组转换为二叉搜索树

题目 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,null,9] 也将被视…

数据结构 -- 数组

本篇文章主要是对数组的实操&#xff0c;所以对数组的概念不在赘述&#xff0c;了解更多数组相关可参照链接 Java数组的概念及使用-CSDN博客 1、DynamicArray类 package com.hh.algorithm.array;import java.util.Arrays; import java.util.Iterator; import java.util.functi…

记录--病理切片图像处理

简介 数字病理切片&#xff0c;也称为全幻灯片成像&#xff08;Whole Slide Imaging&#xff0c;WSI&#xff09;或数字切片扫描&#xff0c;是将传统的玻片病理切片通过高分辨率扫描仪转换为数字图像的技术。这种技术对病理学领域具有革命性的意义&#xff0c;因为它允许病理…

每日OJ题_BFS解决最短路③_力扣127. 单词接龙

目录 ③力扣127. 单词接龙 解析代码 ③力扣127. 单词接龙 127. 单词接龙 难度 困难 字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> ... -> sk&#xff1a; 每一对相邻的单词只差一个字母。…

互联网元搜索引擎SearXNG

最近有个很火的项目叫 FreeAskInternet&#xff0c;其工作原理是&#xff1a; 第一步、用户提出问题第二步、用 SearXNG&#xff08;本地运行&#xff09;在多个搜索引擎上进行搜索第三步、将搜索结果传入 LLM 生成答案 所有进程都在本地运行&#xff0c;适用于需要快速获取信…

Python数据分析与应用 |第4章 使用pandas进行数据预处理 (实训)

表1-1healthcare-dataset-stroke.xlsx 部分中风患者的基础信息和体检数据 编号性别高血压是否结婚工作类型居住类型体重指数吸烟史中风9046男否是私人城市36.6以前吸烟是51676女否是私营企业农村N/A从不吸烟是31112男否是私人农村32.5从不吸烟是60182女否是私人城市34.4抽烟是…

【LAMMPS学习】八、基础知识(3.2)使用chunks计算系统属性

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

蓝桥杯 — —灵能传输

灵能传输 友情链接&#xff1a;灵能传输 题目&#xff1a; 输入样例&#xff1a; 3 3 5 -2 3 4 0 0 0 0 3 1 2 3输出样例&#xff1a; 3 0 3思路&#xff1a; 题目大意&#xff1a;给出一个数组&#xff0c;每次选择数组中的一个数&#xff08;要求不能是第一个数与最后一个…

大数据几种任务调度工具

文章目录 一、DolphinScheduler概述和部署1、DolphinScheduler简介1.1 概述1.2 核心架构 2、DolphinScheduler部署模式2.1 概述2.2 单机模式2.3 伪集群模式2.4 集群模式 3、DolphinScheduler集群模式部署3.1 集群规划与准备3.2 下载与配置部署脚本3.3 初始化数据库3.4 一键部署…

maven引入外部jar包

将jar包放入文件夹lib包中 pom文件 <dependency><groupId>com.jyx</groupId><artifactId>Spring-xxl</artifactId><version>1.0-SNAPSHOT</version><scope>system</scope><systemPath>${project.basedir}/lib/Spr…

电商技术揭秘二十四:无人仓储与自动化技术

相关系列文章 电商技术揭秘一&#xff1a;电商架构设计与核心技术 电商技术揭秘二&#xff1a;电商平台推荐系统的实现与优化 电商技术揭秘三&#xff1a;电商平台的支付与结算系统 电商技术揭秘四&#xff1a;电商平台的物流管理系统 电商技术揭秘五&#xff1a;电商平台…

华媒舍:7种方式,打造出旅游媒体套餐

现如今&#xff0c;伴随着旅游业发展与繁荣&#xff0c;更多旅游业发展从业人员越来越重视产品营销品牌基本建设&#xff0c;希望可以将自己的度假旅游产品和服务营销推广给更多的潜在用户。而建立一个优秀的旅游业发展媒体套餐内容品牌是吸引目标客户的重要步骤。下面我们就详…