Spring boot学习笔记

1 安全验证

package com.example.springbootdemo.pojo;

import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;
import javax.validation.constraints.Pa

 

t;
import java.util.Date;


public class Student {

    int myid;
    @Length(min = 1,max = 5,message = "lenthnot right")
//    @NotNull(message = "wo bu null")
    String name;
    @Null
    String esu;
    @Past(message = "must is post")
    Date birth;
    @Email(message = "email budui")
    String email;
}
    @RequestMapping("/hello2")
    @ResponseBody
    public  String helloIndex2(@Valid Student student, BindingResult bindingResult){
//        System.out.println(bindingResult.getModel());
        if(bindingResult.hasErrors()){
            bindingResult.getAllErrors().forEach(e->{
                System.out.println(e.getDefaultMessage());
            });
        }
        return "hello chaiguowen2";
    }

 

8 application.properties 配置的属性公共的:

https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/common-application-properties.html

 

 

9 enaable自动配置的原理,再启动的时候

会加载meta/info/xxxx

 

 

3 spring boot初始化器 使用失败的原因,联网超时,

使用4g网络秒开,因为4g时移动网络,wifi是xxxxxx网络

 

4 自动生成 serializable 方法的配置再idea中

 

4 真实的 测试dubbo的例子

 

 

/**
 * 订单服务相关接口
 */
public interface OrderService {
    
    /**
     * 初始化订单
     * @param userId
     */
    List<User> initOrder(String userId);

}


public interface UserService {

    /**
     * 获取用户信息
     * @param userId
     * @return
     */
    List<User> getUserList(String userId);
    List<User> getUserAddressList(String userId);
}


user-service-provider
     <dependency>
            <groupId>com.darling.dubboDemo</groupId>
            <artifactId>pub-interfence</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

            <!-- 引入dubbo -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.6.2</version>
        </dependency>
        <!-- 注册中心使用的是zookeeper,引入操作zookeeper的客户端端 -->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>2.12.0</version>
        </dependency>



package com.provider.userserviceprovider.service;

import com.pubinter.pubinterface.dao.UserService;
import com.pubinter.pubinterface.pojo.User;

import java.util.ArrayList;
import java.util.List;

public class UserServiceImpl implements UserService {


    public List<User> getUserAddressList(String userId) {
        List<User> list = new ArrayList();
        list.add(new User(3,"韦德",36,"迈阿密"));
        list.add(new User(23,"詹姆斯",34,"洛杉矶"));
        list.add(new User(24,"科比",39,"洛杉矶"));
        return list;
    }

    @Override
    public List<User> getUserList(String userId) {
        return null;
    }
}


<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <!-- 当前服务的名称 -->
    <dubbo:application name="user-service-provider"/>

    <!-- 注册中心的地址 这里注册中心用的是zookeeper -->
    <dubbo:registry protocol="zookeeper" address="148.70.4.44:2181"/>

    <!-- 指定通信规则(通信协议?通信端口) -->
    <dubbo:protocol name="dubbo" port="20883"></dubbo:protocol>

    <!-- 需要暴露的服务 -->
    <dubbo:service interface="com.pubinter.pubinterface.dao.UserService" ref="userService" version="1.0.0"/>

    <!-- 需要暴露的服务的实现类 -->
    <bean id="userService" class="com.provider.userserviceprovider.service.UserServiceImpl"/>

    <!-- 监控中心协议,如果为protocol="registry",表示从注册中心发现监控中心地址,否则直连监控中心 -->
    <dubbo:monitor protocol="registry"></dubbo:monitor>

    <!--
        timeout:超时时间配置
        retries:重试次数配置(超时报错后重试连接的次数,不含第一次调用,如果目标服务有多个重试的时候会自动切换别的服务)
     -->
    <dubbo:provider timeout="2000" retries="6"></dubbo:provider>
</beans>


public class TestProviderDemo {

    public static void main(String[] args) throws IOException {
        // 加载配置文件
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("provider.xml");
        // 容器启动
        applicationContext.start();
        // 使程序阻塞(由于是单元测试,如果程序跑完了我们再dubbo控制台看不到效果)
        System.in.read();
    }
}


不启用spring boot 主类,因为没必要,只启动了这个一个配置文件
order-service-consumer

      <!-- 引入公共接口层的依赖 -->
        <dependency>
            <groupId>com.darling.dubboDemo</groupId>
            <artifactId>pub-interfence</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- 引入dubbo -->
        <!-- https://mvnrepository.com/artifact/com.alibaba/dubbo -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.6.2</version>
        </dependency>
        <!-- 注册中心使用的是zookeeper,引入操作zookeeper的客户端端 -->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>2.12.0</version>
        </dependency>


    /**
 *   @author 董琳琳
 *   @date 2018/9/14 11:50
 *   @description   订单服务的实现类
 */
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    UserService userService;

    /**
     * 初始化订单并调用用户服务的接口
     * @param userId
     * @return
     */
    @Override
    public List<User> initOrder(String userId) {
        return userService.getUserAddressList(userId);
    }
}


<?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:dubbo="http://dubbo.apache.org/schema/dubbo"
       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-4.3.xsd
        http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
        http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!-- 开启springmvc的包扫描 -->
    <context:component-scan base-package="com.order.orderserviceconsumer"></context:component-scan>
    <!-- 当前服务的名称 -->
    <dubbo:application name="order-service-consumer"/>

    <!-- 注册中心的地址 这里注册中心用的是zookeeper -->
    <dubbo:registry protocol="zookeeper" address="148.70.4.44:2181"/>

    <!-- 声明需要调用的远程服务的接口;生成远程服务代理 -->
    <dubbo:reference id="userService" check="false" interface="com.pubinter.pubinterface.dao.UserService" version="*"/>

    <!-- 监控中心协议,如果为protocol="registry",表示从注册中心发现监控中心地址,否则直连监控中心 -->
    <dubbo:monitor protocol="registry"></dubbo:monitor>
</beans>

user必须实现serializable接口


必须要加扫描路径,不然会出错,找不到类
    <context:component-scan base-package="com.darling.order"></context:component-scan>

/**
 *   @author 董琳琳
 *   @date 2018/9/14 15:57
 *   @description 测试服务调用者是否成功从注册中心订阅服务
 */
public class TestConsumerDemo {
    public static void main(String[] args) throws IOException {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("consumer.xml");
        OrderService service = applicationContext.getBean(OrderService.class);
        List<User> list = service.initOrder("1");
        for (User user:list) {
            System.out.println(user.toString());
        }
        System.in.read();
    }
}


import java.io.Serializable;

public class User implements Serializable {
    private static final long serialVersionUID = -2844400681715908324L;
    private int age;
    private String name;
    private String address;
    private int number;
    User(){};
    public User(int age,String name,int number,String address){
        this.address=address;
        this.age=age;
        this.number=number;
        this.name=name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }
}

  大概流程是,public 接口,有两个,一个user 类,需要实现serializable接口和 set get方法,第二个项目provider引入公共的刚才的想pub,进行生产者配置,第三个消费者配置,兜售ideal 项目的module方式,

最终的效果是

10  邮箱发送 邮件每天限制

各大邮箱每天发送数量限制
网易邮箱:

企业邮箱:单个用户每天最多只能发送 1000 封邮件。单个邮件最多包含 500 个收件人邮箱地址。
163VIP邮箱:每天限制最多能发送800封邮件。
163 、 126 、 yeah 的邮箱:一封邮件最多发送给 40 个收件人 , 每天发送限额为 50 封。

尚易企业邮箱:

一个 IP 一分钟最多发送 400 个邮件地址。
一封邮件最多 200 个邮件地址。
如果一封邮件包括 200 个收信人地址,一分钟最多不能超过 2 封邮件。
如果一封邮件只有一个收信人地址 , 一分钟发送的邮件不能超过 6 封。

QQ邮箱:

为了防范少数垃圾邮件发送者的过量邮件发送行为, QQ邮箱根据不同用户类型设置了不同的发送总数的限制:
2G 的普通用户每天最大发信量是 100 封。
3G 会员、移动 QQ 、 QQ 行及 4G 大肚邮用户每天最大发信量是 500 封。
Foxmail 免费邮箱每天发送量限制为 50 封 。

Gmail邮箱:

邮件数量限制为每天 500 封 。
新申请的邮箱 每天发送量限制 50 封 。

新浪邮箱

企业邮箱试用期用户每天限制 80 封,购买后发信没有限制。
新浪免费邮箱,每天限制发送 30 封 。

雅虎免费邮箱

每小时发送量限制为100封。
每天发送量限制为 200 封。

阿里巴巴英文站提高的企业邮箱:

单个用户每天发送 200 封邮件 。
超过 200 封 / 天可能被系统自动冻结 。

HotMail 邮箱:

每天发送限量限制为 100封 。
每次最多可以将同一封邮件发送给 50 个电子邮件地址。

其他邮箱发送邮箱限制:

搜狐 免费邮箱:每天发送量限制为 100 封 。
GMX 免费邮箱:每天发送量限制为 100 封 。
Gawab 免费邮箱:每天发送量限制为 100 封 。
AOL 免费邮箱:每天发送限制为 100 封 。
中国移动 139 免费邮箱:每天发送限制量为 100 封 。

原文:https://blog.csdn.net/qq_31617637/article/details/73230942

11 使用多服务配置发送邮件的方法email

  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>



	/**
 * 多邮箱发送邮件
 * */
public interface EmailMoreService {

    /**
     * 发送文本邮件
     * */
    boolean sendSimpleMail(String to, String subject, String content);

    /**
     * 发送HTML邮件
     * */
    boolean sendHtmlMail(String to, String subject, String content);

}




package com.example.springbootdemo.email;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.mail.internet.MimeMessage;
import java.util.*;

@Service
public class EmailMoreServiceImpl implements EmailMoreService {
    private Logger logger = LoggerFactory.getLogger(getClass());


    private int emailId = 0;

    private List<Map<String, Object>> listEmail;

@PostConstruct
    private void setListEmail() {
        // 设置邮箱发送列表, 从数据库中获取可发送邮箱列表
        logger.info("=== 初始化可发送邮箱列表 ===");
        this.listEmail =new ArrayList<Map<String,Object>>();
    Map<String,Object> initemail=new HashMap<String, Object>() ;
//    initemail.put();
//    initemail.put();
//    initemail.put();
//    initemail.put();
    listEmail.add(initemail);
        if (listEmail == null || listEmail.isEmpty()) {
            logger.error("=== 可发送邮箱列表为NULL, 请检测代码 ===");
        } else {
            for (Map<String, Object> email : listEmail) {
                logger.info("=== 可发送邮箱: " + email.get("email"));
            }
        }
    }

    private synchronized void setEmailId() {
        // 设置下次可发送邮箱
        boolean isMax = emailId == (listEmail.size()-1);
        emailId = isMax ? 0 : emailId+1;
    }

    private synchronized Map<String, Object> getEmail() {
        // 获取当前可发送邮箱
        Map<String, Object> email = listEmail.get(emailId);
        setEmailId();
        return email;
    }

    private synchronized JavaMailSenderImpl getJavaMailSender() {
        // 获取邮箱发送实例
        Map<String, Object> email = getEmail();
        String host = email.get("host_addr").toString();
        String username = email.get("email").toString();
        String password = email.get("pwd").toString();
        String sslPort = email.get("ssl_port").toString();
        JavaMailSenderImpl javaMailSenderImpl = new JavaMailSenderImpl();
        javaMailSenderImpl.setHost(host);
        javaMailSenderImpl.setUsername(username);
        javaMailSenderImpl.setPassword(password);
        javaMailSenderImpl.setDefaultEncoding("UTF-8");
        Properties properties = new Properties();
        properties.setProperty("mail.smtp.auth", "true");
        properties.setProperty("mail.smtp.socketFactory.port", sslPort);
        properties.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        javaMailSenderImpl.setJavaMailProperties(properties);
        return javaMailSenderImpl;
    }

    @Override
    public boolean sendSimpleMail(String to, String subject, String content) {
        logger.info("简单邮件开始发送");
        try {
            JavaMailSenderImpl javaMailSender = getJavaMailSender();
            String username = javaMailSender.getUsername();
            logger.info("当前发送邮箱: " + username);
            SimpleMailMessage message = new SimpleMailMessage();
            message.setFrom(username);
            message.setTo(to);
            message.setSubject(subject);
            message.setText(content);
            javaMailSender.send(message);
            logger.info("简单邮件发送成功");
            return true;
        } catch (Exception e) {
            logger.error("简单邮件发送异常", e);
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean sendHtmlMail(String to, String subject, String content) {
        logger.info("HTML邮件开始发送");
        try {
            JavaMailSenderImpl javaMailSender = getJavaMailSender();
            MimeMessage message = javaMailSender.createMimeMessage();
            MimeMessageHelper messageHelper = new MimeMessageHelper(message, true);
            String username = javaMailSender.getUsername();
            logger.info("当前发送邮箱: " + username);
            messageHelper.setFrom(username);
            messageHelper.setTo(to);
            messageHelper.setSubject(subject);
            messageHelper.setText(content, true);
            javaMailSender.send(message);
            logger.info("HTML邮件发送成功");
            return true;
        } catch (Exception e) {
            logger.error("HTML邮件发送异常", e);
            e.printStackTrace();
        }
        return false;
    }

}

  

9 spring boot activiti的用法

1 springboot 集成 activiti
第一步依赖包先不写,

地一样注解需要加上  exclude = SecurityAutoConfiguration.class
如何需要手动指定 映射类或配置文件
mapperscan 指定的 注解版配置类
@SpringBootApplication(exclude = SecurityAutoConfiguration.class, scanBasePackages = {"com.lvmoney.**"})
@MapperScan(basePackages = {"com.lvmoney.activiti.dao*"})

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lvmoney.activiti.po.User;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * @describe:
 * @author: lvmoney /xxxx科技有限公司
 * @version:v1.0 2018年10月30日 下午3:29:38
 */
public interface UserMapper extends BaseMapper<User> {
    @Select("select * from user")
    List<User> selectAll();

    @Select("select * from user where name = #{name}")
    List<User> selectByName(String name);
}

2 第二个思路 保持 登录后的用户名在 cookie更好一些

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

            Cookie cookie = new Cookie("userInfo", username);
            cookie.setPath("/");
            response.addCookie(cookie);

3 注销用户的方法,用 cookie
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals("userInfo")) {
                    cookie.setValue(null);
                    // 立即销毁cookie
                    cookie.setMaxAge(0);
                    cookie.setPath("/");
                    response.addCookie(cookie);
                    break;
                }
            }
4 activiti 事件委托
import org.activiti.engine.delegate.DelegateExecution;

import org.activiti.engine.delegate.Expression;

import org.activiti.engine.delegate.JavaDelegate;

/**
 * @describe:
 * @author: lvmoney /xxxx科技有限公司
 * @version:v1.0 2018年10月30日 下午3:29:38
 */


public class ServiceTask implements JavaDelegate {

//流程变量

    private Expression text1;


//重写委托的提交方法

    @Override

    public void execute(DelegateExecution execution) {

        System.out.println("serviceTask已经执行已经执行!");

        String value1 = (String) text1.getValue(execution);

        System.out.println(value1);

        execution.setVariable("var1", new StringBuffer(value1).reverse().toString());

    }

}

执行具体的activiti
import com.lvmoney.activiti.service.BaseActivitiService;
import com.lvmoney.activiti.vo.*;
import com.lvmoney.common.utils.JsonUtil;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.task.Task;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @describe:
 * @author: lvmoney /xxxx科技有限公司
 * @version:v1.0 2018年10月30日 下午3:29:38
 */
@Service
public class BaseActivitiServiceImpl implements BaseActivitiService {
    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    HistoryService historyService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private TaskService taskService;
    @Autowired
    ManagementService managementService;
    @Autowired
    private ProcessEngineConfigurationImpl processEngineConfiguration;

    @Override
    public void startProcessInstanceByKey(StartProcessVo startProcessVo) {
        runtimeService.startProcessInstanceByKey(startProcessVo.getBpmnId(), startProcessVo.getFormId(), startProcessVo.getVariables());
    }

    @Override
    public List<Task> findTasksByFormId(QueryTaskVo queryTaskVo) {
        return taskService.createTaskQuery().processInstanceBusinessKey(queryTaskVo.getFormId()).list();
    }

    @Override
    public List<Task> findTasksByUserId(QueryTaskVo queryTaskVo) {
        return taskService.createTaskQuery().processDefinitionKey(queryTaskVo.getBpmnId()).taskCandidateOrAssigned(queryTaskVo.getUserId()).list();
    }

    @Override
    public List<Task> findTasksByKey(QueryTaskVo queryTaskVo) {
        return taskService.createTaskQuery().processDefinitionKey(queryTaskVo.getBpmnId()).list();
    }

    @Override
    public Task findTaskByTaskId(QueryTaskVo queryTaskVo) {
        return taskService.createTaskQuery() // 创建任务查询
                .taskId(queryTaskVo.getTaskId()) // 根据任务id查询
                .singleResult();
    }


    @Override
    public void completeTaskByTaskId(CompleteTaskVo completeTaskVo) {
        String taskId = completeTaskVo.getTaskId();
        String userId = completeTaskVo.getUserId();
        taskService.claim(taskId, userId);
        Map<String, Object> vars = completeTaskVo.getVariables();
        taskService.complete(taskId, vars);
    }

    @Override
    public List<HistoryVo> findHistoryByFormId(HistoryVo historyVo) {
        String formId = historyVo.getFormId();
        List<HistoryVo> processList = new ArrayList<HistoryVo>();
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
                .processInstanceBusinessKey(formId).list();
        if (list != null && list.size() > 0) {
            for (HistoricTaskInstance hti : list) {
                HistoryVo hv = new HistoryVo();
                hv.setFormId(formId);
                BeanUtils.copyProperties(hti, hv);
                processList.add(hv);
            }
        }
        return processList;
    }


    @Override
    public List<HistoryVo> findHistoryByKey(HistoryVo historyVo) {
        String bpmnId = historyVo.getBpmnId();
        List<HistoryVo> processList = new ArrayList<HistoryVo>();
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
                .processDefinitionKey(bpmnId).list();
        if (list != null && list.size() > 0) {
            for (HistoricTaskInstance hti : list) {
                HistoryVo hv = new HistoryVo();
                hv.setFormId(hti.getProcessInstanceId());
                BeanUtils.copyProperties(hti, hv);
                processList.add(hv);
            }
        }
        return processList;
    }

 5 springboot 的 aop、的例子

 

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;

import org.springframework.stereotype.Component;

@Aspect
@Component
public class ServiceMonitor {

    @AfterReturning("execution(* sample..*Service.*(..))")
    public void logServiceAccess(JoinPoint joinPoint) {
        System.out.println("Completed: " + joinPoint);
    }

}

同时实现 commandlinerunner的例子

import sample.aop.service.HelloWorldService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SampleAopApplication implements CommandLineRunner {

    // Simple example shows how an application can spy on itself with AOP

    @Autowired
    private HelloWorldService helloWorldService;

    @Override
    public void run(String... args) {
        System.out.println(this.helloWorldService.getHelloMessage());
    }

    public static void main(String[] args) {
        SpringApplication.run(SampleAopApplication.class, args);
    }

}

 

可以实现 执行 service方法时 自动调用aop的方法

 

 

1 spring boot的多个模板引擎是 开启了缓存,如果改了html,页面不变化,需要关闭缓存
thymeleaf关闭方法
spring.thymeleaf.cache=false
其他模板关闭缓存 用其他的
2 idea 热部署三种方式
spring loaded/ jrebel 最佳 / spring-boot-devtools 只需要添加一个依赖就可以了

3 下面两个图分别是 jar文件在linux上部署时的方法,一个时centos6.6,一个时 centos7.0的 设置启动运行,开机启动,停止等方法,两种liunx命令不一样

 

 

posted @ 2019-07-18 07:28  冰封剑客  阅读(283)  评论(0编辑  收藏  举报