SSM整合项目

 

 

Day01 安装与使用SVN

一、svn的介绍

1.概念:svn是一个用于版本控制的工具,控制开发的软件代码版本,所有的代码集中管理在svn的仓库中
2.svn使用的意义
    1.解决多人协作开发
    2.svn可以实现代码的备份
    3.代码的还原   ---svn可以根据操作的历史记录回退到不同的版本代码
    4.责任追踪

二、svn本地的安装

1.服务器端安装  
      注意安装过程的端口默认443被占用,更改为其余的端口或者直接选择8443
      控制台操作界面 repositories  创建仓库用于存储代码
                    users         创建用户连接仓库
                    groups        用于给用户分组 方便统一授权
2.客户端的安装
      默认选项下一步安装 注意事项安装过程 第二个红叉的选项 下拉选择未第二项
3.重启电脑 

三、svn的基本操作

checkout 检出工程到本地
commit  提交代码到仓库
update  更新最新代码到本地
revert  还原代码   未提交直接还原
                  已提交 show---log 选择版本号 还原
冲突的解决 先update更新 再编辑冲突  后提交代码

四、svn开发工具idea中的使用

1.idea集成svn工具
   settings----version control-----subversion----浏览本地安装的svn客户端目录---bin----svn.exe
2.idea创建工程分享到仓库
   注意:项目的.idea .impl文件 是idea工具生成的可以忽略不交给版本库
        settings----version control-----ingroe files---忽略文件和文件夹
        vcs---import into version control-----share project---定义仓库地址---分享的目录第二项
3.idea检出工程到工作目录
   vcs---check out from version control-----选择仓库地址---选择地址下的工程----选择检出的本地目录
4.idea中的基本操作  提交 更新 还原

五、svn的高级介绍

1.svn的高级 定义目录规范 不同使用场景的代码存储在不同的目录下
  branches  分支定制化代码开发
  tags      不同版本的代码存储
  trunk     日常开发的主干代码
2.创建分支或者标记
   右键 ----subversion -----branch or tag  自定义存储代码的目录和名称
3.切换分支或者标记
   右键---update project ---勾选switch ----选择对应的url切换目录
4.合并的操作 在控制台 选择merge from  合并的分支目录 
5.多人操作同一个仓库 https://192.168.185.39:448/svn/gaoji_78/

Day02 产品功能的实现

前言:AdminLte介绍

AdminLTE是一款建立在bootstrap和jquery之上的开源的模板主题工具,它提供了一系列响应的、可重复使用的组
件,并内置了多个模板页面;同时自适应多种屏幕分辨率,兼容PC和移动端。通过AdminLTE,我们可以快速的创
建一个响应式的Html5网站。AdminLTE框架在网页架构与设计上,有很大的辅助作用,尤其是前端架构设计师,
用好AdminLTE 不但美观,而且可以免去写很大CSS与JS的工作量

一、框架搭建ssm

1.准备Oracle数据库数据

--创建表空间语句
create tablespace ssm_78
datafile 'c:\ssm_78.dbf' 
size 100m 
autoextend on 
next 10m;
--创建用户授予权限
create user ssm_78 
identified by ssm_78 
default tablespace ssm_78;
--授予用户权限
grant dba to ssm_78;
​
--创建表的sql语句
CREATE TABLE product(
id number(9) PRIMARY KEY ,
productNum VARCHAR2(50) ,
productName VARCHAR2(50),
cityName VARCHAR2(50),
--timestamp时间戳 默认秒后6位 最大支持秒后9位
--(0)表示只支持到秒
departureTime TIMESTAMP(0),
productPrice NUMBER(8,2),
productDesc VARCHAR2(500),
productStatus number(2)
);

--创建序列语句
create sequence com_sequence;
insert into PRODUCT 
values (com_sequence.nextval, 'itcast-001', '魔都五日游', '上海', sysdate, 1800, '魔都我来了', 0);
insert into PRODUCT 
values (com_sequence.nextval, 'itcast-002', '雾都三日游', '北京', sysdate, 1200, '不错的旅行', 1);
insert into PRODUCT
values (com_sequence.nextval, 'itcast-003', '杭州三日游', '哈尔滨',sysdate, 1200, '不错的旅行', 1);
insert into PRODUCT 
values (com_sequence.nextval, 'itcast-004', '魔都五日游', '杭州', sysdate, 1800, '魔都我来了', 0);
insert into PRODUCT 
values (com_sequence.nextval, 'itcast-005', '雾都三日游', '广州', sysdate, 1200, '不错的旅行', 1);
insert into PRODUCT 
values (com_sequence.nextval, 'itcast-006', '雾都三日游', '北京', sysdate, 1200, '不错的旅行', 1);
commit;

2.创建工程分模块搭建

模块              打包方式 
parent 父工程  ------pom 
util          ------jar 
domain        ------jar 
dao           ------jar 
service       ------jar 
web           ------war

3.引入静态资源文件 AdminLte

4.引入依赖

 <properties>
        <spring.version>5.0.2.RELEASE</spring.version>
        <slf4j.version>1.6.6</slf4j.version>
        <log4j.version>1.2.12</log4j.version>
        <mybatis.version>3.4.5</mybatis.version>
        <spring.security.version>5.0.1.RELEASE</spring.security.version>
    </properties>
    
    <dependencies>
        <!-- spring -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>ojdbc</groupId>
            <artifactId>ojdbc</artifactId>
            <version>14</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!-- log start -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <!-- log end -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>${mybatis.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.9</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.2</version>
        </dependency>
​
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            <version>${spring.security.version}</version>
        </dependency>       
    </dependencies>
​
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.2</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                        <showWarnings>true</showWarnings>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
            </plugin>
        </plugins>
    </build>
</project>

5.代码的开发

1.dao层代码  接口+注解
     //列表查询
    @Select("select * from product")
    public List<Product> findAllProduct();
2.service层代码  注解实现事务的声明
    @Service
    @Transactional  //注解支持事务的声明
    public class ProductServiceImpl implements ProductService {
        @Autowired
        private ProductDao productDao;
​
        @Override
        public List<Product> findAllProduct() {
            return productDao.findAllProduct();
        }
    }
3.web层代码 
@Controller
@RequestMapping("/product")
public class ProductController {
​
    @Autowired
    private ProductService productService;
    //查询列表
    @RequestMapping("/findAllProduct")
    public String findAllProduct(Model model){
​
        List<Product> products = productService.findAllProduct();
        model.addAttribute("products",products);
        
        return "product/productList";
    }
}
​
4.domain 实体类 
5.util 工具类
spring的applicationContext文件
        dao的配置  
                   数据源可以加载外部的jdbc.properities文件
                   dataSource 数据源
                   sessionFactory sessin工厂
                   MapperScanerConfigure
        service的配置
                   扫描实现类包路基
                   事务的管理器
                   tx:annotation-driven +实现类上@Tranactional   
                                                     ==  xml文件的   tx:advice aop:config
        ----------------------------------------------------------------------
         <!--数据源可以加载外部的jdbc.properities文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--dataSource 数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--sessionFactory session工厂-->
    <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="typeAliasesPackage" value="com.itheima.domain"></property>
    </bean>
   <!-- MapperScanerConfigure-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.itheima.dao"></property>
    </bean>
​
   <!-- 扫描实现类包路径-->
    <context:component-scan base-package="com.itheima.service"/>
    <!-- 事务的管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--tx:annotation-driven +实现类上@Tranactional-->
    <tx:annotation-driven/>
springMvc的springMvc.xml
    扫描controller
    视图解析器
    mvc注解支持
    mvc:default-servleet-handller
    ----------------------------------------------------
     <!--扫描controller-->
    <context:component-scan base-package="com.itheima.controller"/>
   <!-- 视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        
        <property name="suffix" value=".jsp"></property>
    </bean>
    <!--mvc注解支持-->
    <mvc:annotation-driven/>
   <!-- mvc:default-servleet-handller-->
   <mvc:default-servlet-handler/>
webx.xml文件
   servlet
   listener
   filter
   ---------------------------------------------------------------
   <!--spirngMvc的核心控制器-->
    <servlet>
        <servlet-name>springMvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <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>/</url-pattern>
    </servlet-mapping>
​
    <!--spring的监听器-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
​
    <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>

二、产品的模块

1.产品的列表

1.更改请求按钮 发起列表查询
     <a href="${pageContext.request.contextPath}/product/findAllProduct">
                            <i class="fa fa-circle-o"></i> 产品列表
     </a>
2.controller返回的数据为 products 
   jsp页面 c:forEach  循环读取产品集合 el表达式直接取值
3.数据展示的处理
   1.添加日期转换的工具类
    public static String formatDateToStr(Date date){
​
        try {
            //获取当前对象的日期属性 格式化后返回
            //将当前的日期属性格式化后
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            //返回处理后的字符串
            return sdf.format(date);
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }
   2.在jsp页面获取实体类格式化的属性
     public String getDepartureTimeStr() {
​
        return DateUtil.formatDateToStr(this.departureTime);
    }

2.产品的添加

1.点击新建的按钮 实现添加数据页面的跳转 注意:WEB-INF下jsp不可以直接浏览器访问通过后台转发
   location.href="${pageContext.request.contextPath}/product/productAddUI
2.controller的接受请求方法
    @RequestMapping("/productAdd")
    public String productAdd(Product product){
        //接受页面输入的产品信息
        productService.saveProduct(product);
        //保存成功 后 跳转到展示页
        //重定向 redirect: 路径变化  数据丢失  两次
        //转发  forward:   路径不变  数据不丢  一次
        return "redirect:/product/findAllProduct";
​
    }
 3.日期格式的处理
      1.自定义日期转换器 自定义类实现接口Convert  配置springMvc的节点中 
      2.使用注解格式化参数赋值  @DateTimeFormat  用在实体类的属性上
        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
        private Date departureTime;
      3.使用自定义id属性编辑器-----了解
         @InitBinder
    public void initBind(WebDataBinder binder){
        //注册自定义的属性编辑器
        //参数1为属性需要转换的类型 但是2 为使用的属性编辑器
        binder.registerCustomEditor(Date.class,new PropertiesEditor(){
            //接受浏览器传递的字符串参数
            @Override
            public void setAsText(String dateStr) throws IllegalArgumentException {
                //自定义逻辑转换日期后 赋值
                Date date = DateUtil.parseStrToDate(dateStr);
                setValue(date);
            }
        });
    }

3.产品的修改

1.点击修改 需要传递参数:当前被修改的产品id
            ${pageContext.request.contextPath}/product/productUpdateUI?id=${product.id}
          需要查询产品对象 回显jsp页面
          跳转到修改的jsp页面
2.controler的查询和跳转代码实现
     @RequestMapping("/productUpdateUI")
    public  String productUpdateUI(Integer id,Model model){
​
        //根据id查询产品对象
        Product product = productService.findProductById(id);
        model.addAttribute("product",product);
        //跳转回显编辑产品的页
        return "product/productUpdate";
    }
 3.jsp的表单通过input的value回显数据
     下拉框的回显 
     <option value="0" <c:if test="${product.productStatus==0}">selected</c:if> >关闭</option>
     <option value="1" ${product.productStatus==1?"selected":""}>开启</option>
 4.提交表单实现数据库记录的更新

4.产品的删除

1.点击删除按钮 触发弹框提示的事件
    οnclick='javascript:deleteById(${product.id})'
    
2.确认删除 请求删除的url路径
//根据id删除产品的数据
        function  deleteById(productId) {
            //提示是否删除
            var flag = window.confirm('你确定要删除吗?');
            if(flag){
                //点击确定后发起删除的请求
                window.location.href="${pageContext.request.contextPath}/、
                        product/deleteById?id="+productId;
            }
        }
3.controller根据id删除产品的数据
  //根据id删除产品
    @RequestMapping("deleteById")
    public String deleteById(Integer id){
        productService.deleteById(id);
        return "redirect:/product/findAllProduct";
    }

Day03 分页功能

一、订单的模块

1.订单和产品的列表展示

1.订单和产品的关系分析

订单多对一产品

2.初始化订单和产品的环境

1.数据库表
--创建orders表
CREATE TABLE orders(
id NUMBER(9) PRIMARY KEY ,
orderNum VARCHAR2(20) NOT NULL UNIQUE,
orderTime TIMESTAMP(0),
peopleCount NUMBER,
orderDesc VARCHAR2(500),
payType NUMBER(2),
orderStatus NUMBER(2),
productId NUMBER(9),
FOREIGN KEY (productId) REFERENCES product(id)
)
--插入测试数据
insert into orders values(1,'order1',sysdate,3,'nothing',0,1,22); 
insert into orders values(2,'order2',sysdate,2,'nothing',1,0,5); 
insert into orders values(3,'order3',sysdate,6,'nothing',0,1,1); 
insert into orders values(4,'order4',sysdate,3,'nothing',2,0,2); 
insert into orders values(5,'order5',sysdate,1,'nothing',0,1,6); 
insert into orders values(6,'order6',sysdate,3,'nothing',1,0,3); 
insert into orders values(7,'order7',sysdate,3,'nothing',0,1,4);
commit;
2.java 的实体类
    private Long id;
    private String orderNum;
    private Date orderTime;
    private Integer peopleCount;
    private String orderDesc;
    private Integer payType;
    private Integer orderStatus;
    //一方的实体对象 当前订单的产品
    private Product product;
    get/set方法(略)
3.查询的 dao  service  web
4.延迟加载的dao查询
   
 @Select("select * from orders")
    @Results({
            @Result(id=true,column = "id",property = "id"),
            @Result(column = "productId",property = "product",javaType = Product.class,
                    one = @One(select = "com.itheima.dao.ProductDao.findById" ))
    })

二、分页的效果实现

1.自定义分页实现(略)

1.创建pageBean对象
    private Integer pageNum;
    private Integer pageSize;
    private Integer totalCount;
    private Integer totalPage;
    private List<T> list;
2.controller接受分页参数返回pageBean对象
    @RequestMapping("/findAllProduct")
    public String findAllProduct(Model model,
                                 @RequestParam(defaultValue = "1") Integer pageNum, 
                                 @RequestParam(defaultValue = "3") Integer pageSize){
        //调用servcie得到分页的pageBean对象
        PageBean<Product> pageBean = productService.findAllProduct(pageNum,pageSize);
        model.addAttribute("pageBean",pageBean);
​
        return "product/productList";
    }
3.service组装pageBean返回
 public PageBean<Product> findAllProduct(Integer pageNum, Integer pageSize) {
       //组装分页的pageBean对象返回
        PageBean<Product> pb  = new PageBean<Product>();
        pb.setPageNum(pageNum);
        pb.setPageSize(pageSize);
        //调用dao得到totalCount
        Integer totalCount= productDao.findTotalCount();
        pb.setTotalCount(totalCount);
        //计算得到总页数 
        Double totalPage =  Math.ceil(1.0*totalCount/pageSize);
        pb.setTotalPage(totalPage.intValue());
        /*
        * 分页sql语句的编写 使用的表为product  查询第二页每页3条
        * mySql select * from product limit 3 ,3
        * startIndex = (2-1)*3=3 
        * oracle的数据库分页sql
        * select * from (select rownum r,p.* from product p) t where t.r>3 and t.r<=6
        * select * from (select rownum r,p.* from product p where rownum<=6) t where t.r>3 
        * endIndex = pageNum*pageSize;
        * startIndex = (pageNum-1)*pageSize
         * */
        Integer startIndex = (pageNum-1)*pageSize;
        Integer endIndex = pageNum*pageSize;
        List<Product> list = productDao.findAllProduct(startIndex,endIndex);
        pb.setList(list);
        return pb;
    }
4.dao的统计总数和分页sql查询
 //总计总数量
 @Select("select count(1) from product ")
    Integer findTotalCount();
  //分页查询sql
 @Select("select * from (select rownum r,p.* " +
            " from product p where rownum<=#{endIndex}) t where t.r> #{startIndex} ")
    public List<Product> findAllProduct(@Param("startIndex") Integer startIndex,    @Param("endIndex")Integer endIndex);
5.jsp获取分页的数据展示
     数据集合  总页数 总条数  直接el表达式获取就行
     每页条数 需要判断下拉的回显
        <select id="selectSize" 
            class="form-control" οnchange="javascript:toPage(${pageBean.pageNum});">
                <option <c:if test="${pageBean.pageSize==1}">selected</c:if> >1</option>
                <option <c:if test="${pageBean.pageSize==3}">selected</c:if>>3</option>
                <option ${pageBean.pageSize==5?"selected":""}>5</option>
        </select> 条
     选择下拉框中的值 添加onchange 时间 发请求
     function toPage(pageNum){
            //获取页面的下拉框中最终选中的值
            //通过id获取 select控件
            //jquery的通过id获取$("#selectSize").val()
            var pageSize= document.getElementById("selectSize").value;
            window.location.href="${pageContext.request.contextPath}/product/findAllProduct?pageNum="+pageNum+"&pageSize="+pageSize;
        }
​

自定义分页的弊端

上一页 下一页的边界数据没控制
​
显示的分页控件栏的页码号
​
分页的sql不同数据库不一致

3.分页插件pageHelper的实现(重点)

1.pageHelper是国内用于mybatis分页使用的插件
2.使用分页的实现步骤
  1.引入插件使用的依赖
  2.插件应用的配置文件   sqlMapConfig.xml-----今天不用  品优购项目是这种形式
                       applicationContext.xml 今天使用 配置在sessionFactory中 品优购项目使用
        <!--分页插件拦截器配置的节点-->
        <property name="plugins">
            <array>
                <!--插件使用的类对象-->
                <bean class="com.github.pagehelper.PageInterceptor">
                    <property name="properties">
                       <!-- <value>
                            helperDialect=oracle
                            reasonable=true
                        </value>-->
                        <!--配置分页拦截器使用的数据库方言和参数的合理化-->
                        <props>
                            <prop key="helperDialect">oracle</prop>
                            <prop key="reasonable">true</prop>
                        </props>
                    </property>
​
                </bean>
            </array>
        </property>
3.在执行查询的方法之前开启静态方法的调用
   1.修改controller接受分页参数 返回分页插件的对象
    //查询列表
    @RequestMapping("/findAllOrder")
    public String findAllOrder(Model model,
                               @RequestParam(defaultValue = "1") Integer pageNum, 
                               @RequestParam(defaultValue = "3") Integer pageSize){
        //使用分页的参数获取分页对象
        PageInfo<Order> pageInfo = orderService.findAllOrder(pageNum,pageSize);
        model.addAttribute("pageInfo",pageInfo);
​
        return "order/orderList";
    }
   2.servcie使用插件返回分页对象
     public PageInfo<Order> findAllOrder(Integer pageNum, Integer pageSize) {
        //在dao的查询方法之前开启静态方法
        PageHelper.startPage(pageNum,pageSize);
        List<Order> list = orderDao.findAllOrder();
        //使用查询的list 结果集用于初始化pageInfo的分页对象
        PageInfo<Order> pageInfo = new PageInfo<>(list);
        return pageInfo;
    }

Day04 权限框架的用户模块

一、权限框架的使用

1.权限框架的入门示例搭建

1.权限框架的介绍
     认证
     授权
2.使用步骤
    1.引入框架使用的依赖
    2.配置web.xml文件
         1.配置spring的监听器 加载框架使用的配置文件
     <!--配置spring的监听器 加载框架使用的配置文件-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springSecurity.xml</param-value>
    </context-param>
         2.配置security的过滤器执行链 名字固定必须是springSecurityFilterChain
    <!--配置security的过滤器执行链 名字固定必须是springSecurityFilterChain-->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
3.配置springSecurity.xml文件
         配置拦截项目的规则
         配置验证能否访问的节点
         <!--配置权限框架拦截的规则
    auto-config="true"  应用框架的默认配置
    use-expressions="false" 关闭框架使用的表达式
    intercept-url pattern="/**"  拦截所有的浏览器请求
    access="ROLE_ADMIN"  只有ROLE_ADMIN角色的用才可以访问  规则角色名必须以ROLE_开头
    -->
    <security:http auto-config="true" use-expressions="false">
        <security:intercept-url pattern="/**" access="ROLE_ADMIN"/>
    </security:http>
    
    <!--验证的节点-->
    <security:authentication-manager>
        <security:authentication-provider>
            <!--验证的用户业务类 目前测试使用默认的框架业务类 如果真是项目需要使用自定义的业务类
            {noop}表示登录使用明文密码验证 明文123456 未加密的密码
                                          密文加密后密码123456=fadjkfadfkjda43fdafa334
            -->
            <security:user-service>
                <security:user name="admin" password="{noop}admin" authorities="ROLE_ADMIN"/>
                <security:user name="user" password="{noop}user" authorities="ROLE_USER"/>
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>

2.使用自定义的页面实现权限认证的管理

1.创建自定义的页面 error.jsp login.jsp 403.jsp
     注意:form 表单的提交为post
           账号密码 username  password
2.配置security.xml 应用自定义的jsp页面
    <security:form-login login-processing-url="/login"
                               default-target-url="/index.jsp"
                               authentication-failure-url="/error.jsp"
                               login-page="/login.jsp"/>
3.配置403权限不足的处理
   <!--登录成功后由于权限不足的403错误配置-->
        <security:access-denied-handler error-page="/403.jsp"/>
        <!--关闭csrf跨域请求的拦截-->
        <security:csrf disabled="true"/>
4.放行未登录的资源拦截 必须配置在文件上边
    <security:http pattern="/login.jsp" security="none"/>
    <security:http pattern="/error.jsp" security="none"/>
                               

3.应用权限框架到ssm项目中

1.引入依赖
2.配置web.xml文件
      加载框架配置文件的监听器
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml,classpath:springSecurity.xml</param-value>
        </context-param>
      框架自己使用的11个过滤器执行链
         <!--配置security的过滤器执行链 名字固定必须是springSecurityFilterChain-->
        <filter>
            <filter-name>springSecurityFilterChain</filter-name>
            <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>springSecurityFilterChain</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
​
3.配置securit的配置文件
      直接拷贝

二、用户模块

用户为公司管理员添加的用户用于管理公司不同的业务模块使用,根据不用的用户会分配不同的角色,用于

限制不同的功能操作。

第一节:搭建用户开发环境

1.创建表结构

CREATE TABLE sys_user(
  id number(9)  PRIMARY KEY,
  username VARCHAR(50),
  email VARCHAR(50) ,
  password VARCHAR(80),
  phoneNum VARCHAR(20),
  STATUS number(1)
);

2.用户实体类

public class SysUser implements Serializable{
    
    private String id;
    private String username;
    private String email;
    private String password;
    private String phoneNum;
    private Integer status;
    // get/set  
}   
​

3.编写dao和service

// dao接口
package cn.itcast.dao;
​
public interface SysUserDao {
}
​
​
public interface SysUserService {
​
}
//实现类
 @Service("sysUserService")
 public class SysUserServiceImpl implements SysUserService {
​
   @Autowired
   private SysUserDao sysUserDao;
​
}

4.编写controller

@Controller
@RequestMapping("/user")
public class UserController {
    
    @Autowired
    private UserService userService;
}

第二节:用户列表查询

@Controller
@RequestMapping("/user")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @RequestMapping("/findAll")
    public String findAll(Model model) {
        List<SysUser> ulist = userService.findAll();
        model.addAtribute("userList",uList);
        return "user/userList";
    }
​
}
​
public List<SysUser> findAll() {
    return userDao.findAll();
}
    
@Select("select * from sys_user")
List<SysUser> findAll();
​

第三节:用户添加功能

1.加密介绍

  1. MD5是一个加密的算法,可以对铭文字符串进行加密,得到一个密文。

  2. MD5原文和密文是一对一的键值对。

    1. 例如:原文:123 密文:202cb962ac59075b964b07152d234b70

    2. 不管执行多少次,加密算法是固定的,得到的密文永远不变

    3. 相对来说就不安全,容易被破解

  3. 公司中使用MD5进行加密

    1. 例如:原文密码是123,把首尾交换,再加上用户名进行加密

    2. 例如:321meimei 进行加密e5212215b8cbf493e04f4290cf7cecc8

    3. 这样做相对来说会安全

2.BCryptPasswordEncoder加密(加盐加密算法)

  1. shiro框架有md5hash算法加密,该加密是加盐的方式进行加密的。

  2. 在原有的密码中随机加入盐(就是字符串),再进行加密。

    Md5Hash h = new Md5Hash("123","ha",2);
    System.out.println(h.toString());
  3. 测试代码

        // $2a$10$.W5msBQws5yoJum7gEbMcOQ1J.p2UIjEH5l4lm1rLCers9QUhUVFS
        // $2a$10$UxZneVG78UkChbx4sZND7.mWWx6ulbnl38updtSMRK0KQw5QvEdUy
        public static void main(String[] args) {
            // System.out.println(md5("321meimei"));        
            BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();        
            // 进行判断
            boolean b = encoder.matches("123", "$2a$10$UxZneVG78UkChbx4sZND7.mWWx6ulbnl38updtSMRK0KQw5QvEdUy");
            System.out.println(b);
        }   

3.添加用户的代码

  1. 在security配置文件中配置加密类

    <!-- 配置加密类 -->
        <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>
  2. 编写添加用户的方法

    @RequestMapping("/save")
    public String save(SysUser sysUser) {
        userService.save(sysUser);
        return "redirect:/user/findAll";
    }   
    /**
     * 对密码加密保存
     */
    public void save(SysUser sysUser) {
        // 先对密码进行加密
        String newpwd = passwordEncoder.encode(sysUser.getPassword());
        sysUser.setPassword(newpwd);
        // 保存用户
        userDao.save(sysUser);
    }   
    @Insert("insert into sys_user (username,email,password,phoneNum,status) values (#{username},#{email},#{password},#{phoneNum},#{status})")
    void save(SysUser sysUser);

第四节:登录认证功能

1.代码

@Service("userService")
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserDao userDao;
    /**
     * 该方法是认证的方法
     * 先编写一个默认的认证代码
     * 参数就是表单提交的用户名
     */
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 先设置假的权限
        List<GrantedAuthority> authorities = new ArrayList<>();
        // 传入角色
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
        // 通过用户名查询密码
        SysUser sysUser = userDao.findByUsername(username);
        // 创建用户
        User user = new User(username, "{noop}"+sysUser.getPassword(), authorities) ;
        return user;
    }
​
}
​
@Repository
public interface UserDao {
​
    @Select("select * from sys_user where username = #{username}")
    SysUser findByUsername(String username);
​
}
​

 

第五节:修改登录功能

1.修改配置文件

    <!-- 在内存中临时提供用户名和密码的数据 -->
    <security:authentication-manager>
        <!-- 提供服务类,去数据库查询用户名和密码 -->
        <security:authentication-provider user-service-ref="userService">
            <!-- 提供加密方式 -->
            <security:password-encoder ref="passwordEncoder"/>
        </security:authentication-provider>
    </security:authentication-manager>

2.修改登录的方法,把{noop}代码去掉,表示采用加密方式登录

    /**
     * 该方法是认证的方法
     * 先编写一个默认的认证代码
     * 参数就是表单提交的用户名
     */
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 先设置假的权限
        List<GrantedAuthority> authorities = new ArrayList<>();
        // 传入角色
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
        
        // 通过用户名查询密码
        SysUser sysUser = userDao.findByUsername(username);
        
        // 创建用户
        User user = new User(username, sysUser.getPassword(), authorities) ;
        return user;
    }

第六节:获取用户名

1.服务器端后台对象封装的分析

  1. 对象的封装,认证通过后会返回User对象,该对象中包含用户名等信息。

  2. principal(主角)就是User对象。

  3. 框架会把principal封装到Authentication(认证)对象中

  4. Authentication对象会封装到SecurityContext(Security上下文对象)中

  5. 最后会把SecurityContext绑定到当前的线程中

2.服务器可以编写如下代码

        // 先获取到SecurityContext对象
        SecurityContext context = SecurityContextHolder.getContext();
        // 获取到认证的对象
        Authentication authentication = context.getAuthentication();
        // 获取到登录的用户信息
        User user = (User) authentication.getPrincipal();
        System.out.println(user.getUsername());

3.在JSP的页面上可以使用2种方式来获取用户名

  1. 使用EL表达式方式获取

    ${ sessionScope.SPRING_SECURITY_CONTEXT.authentication.principal.username }
  2. 使用security框架提供方式获取

    <%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>  
    <security:authentication property="principal.username"/>

第七节:用户退出功能

1.在header.jsp的页面中编写超链接

<a href="${pageContext.request.contextPath}/logout"
                                    class="btn btn-default btn-flat">注销</a>

2.配置文件配置注销过滤器

  <security:logout invalidate-session="true" logout-url="/logout" logout-success-url="/login.jsp"/>

第八节:自定义处理器的使用

springSecurity登录成功失败默认的处理器在处理AbstractAuthenticationProcessingFilter中作为属性引用

private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();

,那么如果想使用自定义的处理器,只需要复写处理器的方法 并配置引用自定义的处理器即可。

需求:获取登录成功的时间和登录失败的原因

1.登录成功的自定义处理器

@Component("successHandller")
public class CustomAuthenticationSuccessHandller extends SavedRequestAwareAuthenticationSuccessHandler {
​
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
​
        String loginTime = DateUtil.formatDateToStr(new Date());
        request.getSession().setAttribute("loginTime",loginTime);
​
        super.onAuthenticationSuccess(request, response, authentication);
    }
}

2.登录失败的自定义处理器

@Component("failerHandller")
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
​
    private String failerUrl ="/failer.jsp" ;
​
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
​
        if(exception instanceof DisabledException){
            request.setAttribute("errorMsg","用户不可用,登录失败,请联系管理员或者");
        }
        if(exception instanceof BadCredentialsException){
            request.setAttribute("errorMsg","密码错误,登录失败,请");
        }
        request.getRequestDispatcher(failerUrl)
                .forward(request, response);
    }
}
​

3.配置文件引入自定义处理器

<!--添加自定义登录的处理节点-->
        <security:form-login default-target-url="/index.jsp" 
                                authentication-success-handler-ref="successHandller"
                                authentication-failure-handler-ref="failerHandller"
                                login-processing-url="/login" login-page="/login.jsp"/>
​

4.更改jsp的提示语获取

    <p>
        ${errorMsg}返回到登录页面 <a href="${pageContext.request.contextPath}/login.jsp">
        重新登录</a>
    </p>

Day05 角色模块

第一节:查询所有数据和添加角色

@Controller
@RequestMapping("/role")
public class RoleController {
    
    @Autowired
    private RoleService roleService;
    
    @RequestMapping("/save")
    public String save(Role role) {
        roleService.save(role);
        return "redirect:/role/findAll";
    }
    
    @RequestMapping("/findAll")
    public String save(Model model) {
        List<Role> list = roleService.findAll();
        model.addAttribute("roleList", list);
        return "role-list";
    }
    
}
​
@Service
public class RoleServiceImpl implements RoleService {
    
    @Autowired
    private RoleDao roleDao;
​
    public List<Role> findAll() {
        return roleDao.findAll();
    }
​
    public void save(Role role) {
        roleDao.save(role);
    }
​
}
​
@Repository
public interface RoleDao {
​
    @Select("select * from sys_role")
    List<Role> findAll();
​
    @Insert("insert into sys_role (roleName, roleDesc) values (#{roleName}, #{roleDesc})")
    void save(Role role);
}

一、角色模块

create table sys_role(
   id  number(9) primary key,
   roleName varchar2(15),
   roleDesc varchar2(30)
) 

CREATE TABLE sys_user_role(
userId NUMBER(9),
roleId NUMBER(9),
PRIMARY KEY(userId,roleId),
FOREIGN KEY (userId) REFERENCES sys_USER(id),
FOREIGN KEY (roleId) REFERENCES sys_role(id)
)

1.数据列表

2.数据添加

3.用户维护角色的数据

1.管理用户角色的数据回显

1.修改管理角色按钮 传递用户id发起请求
    ${pageContext.request.contextPath}/user/managerUserRoleUI?id=${user.id}
2.controller接受请求后 查询需要的数据返回
   @RequestMapping("/managerUserRoleUI")
    public String managerUserRoleUI(Integer id,Model model){
        //得到用户的信息
        SysUser user = sysUserService.findUserById(id);
        //获取用户的属性角色集合
        List<Role> userRoles = user.getRoles();
        //循环遍历集合 组装成一个表示用户所有角色的字符串用于页面的包含判断
        if(null!=userRoles&&userRoles.size()>0){
            StringBuilder sb = new StringBuilder();
            for (Role role : userRoles) {
                sb.append(role.getRoleName()+",");
            }
            model.addAttribute("userRolesStr",sb.toString());
        }
        //所有的数据库角色数据
        List<Role> roles = roleService.findAllRole();
        model.addAttribute("user",user);
​
        model.addAttribute("roles",roles);
        return "user/managerUserRole";
    }
    -----userDao 查询用户同时得到用户的角色集合

    @Select("select * from sys_user where id = #{id}")
    @Results({
            @Result(property = "id",column = "id"),
            @Result(property = "roles",column = "id",javaType = List.class,
                    many = @Many(select = "com.itheima.dao.RoleDao.findRolesByUserId"))
    })
    SysUser findUserById(Integer id);
    -----roleDao 必须有findRolesByUserId 的方法 子查询得到用户的角色集合
     @Select("select * from sys_role where id in " +
            " (select roleid from sys_user_role where userid = #{userId})")
    public List<Role> findRolesByUserId(Integer userId);
3.jsp页面获取数据回显
    用户名  el表达式直接取
    角色集合 el表达式获取 遍历
    已有角色回显 要求通过函数标签判断显式
    1.引入标签
        <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
    2.搭配c:if 判断是否应该checked属性
        <td><input name="ids" type="checkbox" 
            <c:if test="${fn:contains(userRolesStr,role.roleName)}">
                                    checked</c:if> value="${role.id}"></td>

2.实现提交数据管理数据用户的角色

1.提交保存 要求传递用户id和选择的多个角色id  以数组形式传递
  <input type="hidden" name="userId" value="${user.id}">
  input teype=checkbox  以复选框形式传递多个参数 
2.提交表单 controller接受两种参数维护数据
     @RequestMapping("/managerUserRole")
    public  String managerUserRole(Integer userId,Integer [] ids){
       
        sysUserService.managerUserRole(userId,ids);
       
        return "redirect:/user/findAllUser";
    }
    service----代码  先移除再添加
     @Override
    public void managerUserRole(Integer userId, Integer[] ids) {
        //通过用户id移除原始的所有角色
        userDao.removeRoleFromUser(userId);
​
        //没有角色直接添加数据
        if(null!=ids&&ids.length>0){
            for (Integer rid : ids) {
                userDao.saveUserRole(userId,rid);
            }
        }
    }

3.用户的详情查询

4.使用真实的用户角色验证登录

1.更改userService实现类 查询用户的真实角色返回验证的集合
//获取用户的真实角色集合
        List<Role> userRoles = sysUser.getRoles();
        if(userRoles!=null&&userRoles.size()>0){
            for (Role userRole : userRoles) {
                authorities.add(new SimpleGrantedAuthority(userRole.getRoleName()));
            }
        }
2.更改security.xml的配置文件 设置支持多个角色
    <security:intercept-url 
            pattern="/**" access="ROLE_ADMIN,ROLE_USER,ROLE_ORDER,ROLE_PRODUCT"/>
​

二、权限控制

1.jsp后台页面的控制

1.需求:针对不同用户登录的角色不同,显式的菜单按钮不一致
1.实现:权限框架通过标签的判断实现
       1.引入权限框架的判断标签
       <%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
       2.在按钮上匹配认证判断
        <security:authorize access="hasAnyRole('ROLE_PRODUCT','ROLE_ADMIN','ROLE_USER')">
             <li id="travellog-manage">
                 <a href="${pageContext.request.contextPath}/product/findAllProduct">
                 <i class="fa fa-circle-o"></i> 产品列表
                 </a>
              </li>
         </security:authorize>

2.管理后台功能代码的访问控制

1.服务器端的访问限制 可以根据注解实现:
2.注解实现的分类:
   1.jsr250注解 java 的一个规范实现 
      导入依赖
       <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>jsr250-api</artifactId>
            <version>1.0</version>
        </dependency>
      开启aop的注解支持
         <!--为了拦截controller的访问方法 需要配置在speingMvc的文件中-->
         <aop:aspectj-autoproxy proxy-target-class="true"/>
      security.xml中开启jsr250的注解支持
         <security:global-method-security jsr250-annotations="enabled"/>
      在类或者方法上 可以通过注解认证 
          @RolesAllowed("ROLE_ADMIN")
   2.security 注解  权限框架提供
        <security:global-method-security secured-annotations="enabled"/>
        @Secured("ROLE_ADMIN")
   3.支持表达式主机  权限框架提供
        <security:global-method-security pre-post-annotations="enabled" />
        @PreAuthorize("hasRole('ROLE_ADMIN')")

三、日志模块

1.日志实现的介绍

1:日志模块需求:记录登录的用户对于管理后台的操作记录
2.日志实体类保存到数据库 属性 username  vistTime 类名 方法名 执行时间 ip地址 执行结果 执行提示
3.aop实现日志的记录
4.aop相关概念的回顾
   1.切面类 表示切面通过@Aspect 作用是 整合切入点和通知
   2.切入点 @PointCut 表示被增强的方法 定义规则表示哪些方法需要被增强
   3.通知  就是对增强的方法做的事情 
          前置通知 @Before
          后置通知 @AfterReturning
          最终通知 @After
          异常通知 @AfterThrowing
          环绕通知 @Around
   4.目标 Target 目标对象 被代理的对象 正在访问的类
   5.连接点 @JoinPoint  正在访问的方法

2.日志开发的实现

1.日志表的初始化

CREATE TABLE sys_log(
id number PRIMARY KEY ,
visitTime TIMESTAMP,
username VARCHAR2(50),
ip VARCHAR2(30),
method VARCHAR2(200),
executeTime number(15),
executeResult varchar2(10),
executeMsg  varchar2(200)
)
2.domain  dao  service 
3.切面类

@Aspect
@Component
public class LogAspectUtil {
​
    //注入service的业务类
    @Autowired
    private LogService logService;
​
    SysLog log;
    //切入点表达式
    @Pointcut("execution( * com.itheima.controller.*.*(..))")
    public void pointCut(){}
​
    @Autowired
    private HttpServletRequest request;
  //前置通知
    @Before("pointCut()")
    public void executeBefore(JoinPoint jp){
        //访问方法之前初始化日志对象
        log = new SysLog();
        log.setVisitTime(new Date());
        //获取登录的用户名
        SecurityContext context = SecurityContextHolder.getContext();
        User user = (User)context.getAuthentication().getPrincipal();
        log.setUsername(user.getUsername());
        //访问的方法
        String methodName =jp.getSignature().getName();
        //访问的类名
        Class target = jp.getTarget().getClass();
        String className = target.getSimpleName();
        log.setMethod(className+"====="+methodName);
        //访问者的ip
        log.setIp(request.getRemoteAddr());
    }
​
    //执行成功
    @AfterReturning("pointCut()")
    public void executeAfter(){
        log.setExecuteMsg("执行成功");
        log.setExecuteResult("success");
        log.setExecuteTime(new Date().getTime()-log.getVisitTime().getTime());
        logService.saveLog(log);
​
    }
    //异常通知
    @AfterThrowing(pointcut = "pointCut()",throwing = "e")
    public void executeException(Exception e){
        log.setExecuteMsg(e.getMessage());
        log.setExecuteResult("exception");
        log.setExecuteTime(new Date().getTime()-log.getVisitTime().getTime());
        logService.saveLog(log);
    }
}


​
  
  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值