超市订单管理系统 (Servlet 版)
1.项目架构
1.1、功能模块设计
:
1.2、数据库设计 :
2.项目搭建准备工作
环境:Maven 3.6.1 、 IDEA2020.1 、JDK 8 、 MySQ L5.7 、 Tomcat 9 、SQLyog 12+
2.1、搭建一个 maven Web 项目
首先创建 Maven 项目,并勾选对应的模板
填写相关信息,并执行下一步
创建相关目录,将 web.xml 修改为 4.0
2.2、配置 Tomcat
2.3、测试项目能否运行
启动 Tomcat
2.4、导入项目中涉及的 jar 包
servlet&jsp
```xml
MySQL 驱动
```xml
JSTL
```xml
standard
```xml
junit
```xml
2.5、创建项目包结构
2.6、编写实体类
ORM 映射:表 - 类 映射
User.java (用户实体)
```java package com.baidou.pojo;
import java.util.Date;
/ * 用户实体类 * Created by baidou on 2020/7/24。 */ public class User { private Integer id; //id private String userCode; //用户编码 private String userName; //用户名称 private String userPassword; //用户密码 private Integer gender; //性别 private Date birthday; //出生日期 private String phone; //电话 private String address; //地址 private Integer userRole; //用户角色 private Integer createdBy; //创建者 private Date creationDate; //创建时间 private Integer modifyBy; //更新者 private Date modifyDate; //更新时间
private Integer age;//年龄
private String userRoleName; //用户角色名称
//set/get...
} ```
Role.java(角色实体)
```java package com.baidou.pojo;
import java.util.Date;
/ * 角色实体类 * Created by baidou on 2020/7/24。 */ public class Role {
private Integer id; //id
private String roleCode; //角色编码
private String roleName; //角色名称
private Integer createdBy; //创建者
private Date creationDate; //创建时间
private Integer modifyBy; //更新者
private Date modifyDate;//更新时间
//set/get...
} ```
Bill.java (账单实体)
```java public class Bill { private Integer id; //id private String billCode; //账单编码 private String productName; //商品名称 private String productDesc; //商品描述 private String productUnit; //商品单位 private BigDecimal productCount; //商品数量 private BigDecimal totalPrice; //总金额 private Integer isPayment; //是否支付 private Integer providerId; //供应商ID private Integer createdBy; //创建者 private Date creationDate; //创建时间 private Integer modifyBy; //更新者 private Date modifyDate;//更新时间
private String providerName;//供应商名称
//set/get...
} ```
Provider.java(供应商实体)
```java package com.baidou.pojo;
import java.util.Date;
/ * 供应商实体类 * Created by baidou on 2020/7/24。 */ public class Provider {
private Integer id; //id
private String proCode; //供应商编码
private String proName; //供应商名称
private String proDesc; //供应商描述
private String proContact; //供应商联系人
private String proPhone; //供应商电话
private String proAddress; //供应商地址
private String proFax; //供应商传真
private Integer createdBy; //创建者
private Date creationDate; //创建时间
private Integer modifyBy; //更新者
private Date modifyDate;//更新时间
```
2.7、编写公共类
1、数据库配置文件
db.properties
properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306?useUnicode=true&characterEncoding=utf-8
usernamem=root
password=root
2、编写数据库的公共类
```java package com.baidou.dao;
import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.Properties;
/ * 操作数据库的公共类 * Created by baidou on 2020/7/24. */ public class BaseDao { private static String driver; private static String url; private static String username; private static String passworld;
// 静态代码块, 类加载的时候就初始化了
static {
Properties properties = new Properties();
// 通过类加载器读取对应的资源
InputStream is = BaseDao.class.getClassLoader().getResourceAsStream("db.properties");
try {
properties.load(is);
} catch (IOException e) {
e.printStackTrace();
}
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
passworld = properties.getProperty("passworld");
}
/
* 获取数据库的连接
*
* @return connection
*/
public static Connection getConnection() {
Connection connection = null;
try {
// 注册数据库驱动
Class.forName(driver);
// 获取数据库连接对象
connection = DriverManager.getConnection(url, username, passworld);
} catch (Exception e) {
e.printStackTrace();
}
return connection;
}
/
* 编写查询公共类
*
* @param connection 数据库连接对象
* @param sql sql语句
* @param params sql语句中的参数
* @return resultSet
* @throws SQLException
*/
public static ResultSet execute(Connection connection, String sql, Object[] params, ResultSet resultSet, PreparedStatement preparedStatement) throws SQLException {
// 预编译的sql, 在后面直接执行就可以了
preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
// setObject占位符从1开始 , 数组索引从0开始
preparedStatement.setObject(i + 1, params[i]);
}
//执行sql并返回结果集
resultSet = preparedStatement.executeQuery();
return resultSet;
}
/
* 编写增删改公共方法
*
* @param connection
* @param sql
* @param params
* @param preparedStatement
* @return
* @throws SQLException
*/
public static int execute(Connection connection, String sql, Object[] params, PreparedStatement preparedStatement) throws SQLException {
preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
// setObject占位符从1开始 , 数组索引从0开始
preparedStatement.setObject(i + 1, params[i]);
}
//执行sql并返回结果集
int updateRows = preparedStatement.executeUpdate();
return updateRows;
}
/
* 释放资源
*/
public static boolean closeResource(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet) {
boolean flag = true;
if (resultSet != null) {
try {
resultSet.close();
// GC回收 (垃圾回收器)
resultSet = null;
} catch (SQLException throwables) {
throwables.printStackTrace();
// 表示没有释放成功
flag = false;
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
// GC回收 (垃圾回收器)
preparedStatement = null;
} catch (SQLException throwables) {
throwables.printStackTrace();
// 表示没有释放成功
flag = false;
}
}
if (connection != null) {
try {
connection.close();
// GC回收 (垃圾回收器)
connection = null;
} catch (SQLException throwables) {
throwables.printStackTrace();
// 表示没有释放成功
flag = false;
}
}
return flag;
}
} ```
3、编写字符编码过滤器
```java package com.baidou.filter;
import javax.servlet.*; import java.io.IOException;
/ * 字符编码过滤器 * Created by baidou on 2020/7/24. */ public class CharacterEncodingFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
chain.doFilter(request, response);
}
public void destroy() {
}
} ```
并在 web.xml 中注册
```xml
<!--字符编码过滤器-->
<filter>
<filter-name>CharacterEncoding</filter-name>
<filter-class>com.baidou.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncoding</filter-name>
<!--过滤所有请求-->
<url-pattern>/*</url-pattern>
</filter-mapping>
```
2.8、导入静态资源
CSS js ...
3.登录功能实现
1、编写前端页面
2、设置首页
```xml
3、编写 dao 层 ,用户登录接口
java
//得到要登录的用户
public User getLoginUser(Connection connection,String userCode)throws SQLException;
4、编写 dao 接口的实现类
```java package com.baidou.dao.user;
import com.baidou.dao.BaseDao; import com.baidou.pojo.User;
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException;
public class UserDaoImpl implements UserDao { / * 得到要登录的用户 */ public User getLoginUser(Connection connection, String userCode) throws SQLException { PreparedStatement pstm = null; ResultSet rs = null; User user = null;
if (connection != null) {
String sql = "select * from smbms_user where userCode=?";
Object[] params = {userCode};
rs = BaseDao.execute(connection, pstm, rs, sql, params);
if (rs.next()) {
user = new User();
user.setId(rs.getInt("id"));
user.setUserCode(rs.getString("userCode"));
user.setUserName(rs.getString("userName"));
user.setUserPassword(rs.getString("userPassword"));
user.setGender(rs.getInt("gender"));
user.setBirthday(rs.getDate("birthday"));
user.setPhone(rs.getString("phone"));
user.setAddress(rs.getString("address"));
user.setUserRole(rs.getInt("userRole"));
user.setCreatedBy(rs.getInt("createdBy"));
user.setCreationDate(rs.getTimestamp("creationDate"));
user.setModifyBy(rs.getInt("modifyBy"));
user.setModifyDate(rs.getTimestamp("modifyDate"));
}
//释放资源
BaseDao.closeResource(null, pstm, rs);
}
return user;
}
} ```
5、业务层接口
```java package com.baidou.service.user;
import com.baidou.pojo.User;
public interface UserService { //用户登录 public User login(String userCode, String password); } ```
6、业务层实现类
```java package com.baidou.service.user;
import com.baidou.dao.BaseDao; import com.baidou.dao.user.UserDao; import com.baidou.dao.user.UserDaoImpl; import com.baidou.pojo.User; import org.junit.Test;
import java.sql.Connection; import java.sql.SQLException;
/ * Created by baidou on 2020/7/25. */ public class UserServiceImpl implements UserService {
//service层调dao层
private UserDao userDao;
public UserServiceImpl() {
userDao = new UserDaoImpl();
}
public User login(String userCode, String password) {
Connection connection = null;
User user = null;
connection = BaseDao.getConnection();
try {
//通过业务层调用对应的数据库操作
user = userDao.getLoginUser(connection, userCode);
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//释放资源
BaseDao.closeResource(connection, null, null);
}
return user;
}
} ```
7、编写 Servlet
```java package com.baidou.servlet.user;
import com.baidou.pojo.User; import com.baidou.service.user.UserServiceImpl; import com.baidou.util.Constants;
import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
/ * 处理登录请求 * Created by baidou on 2020/7/25. */ public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("LoginServlet--start.....");
//获取用户名和密码
String userCode = request.getParameter("userCode");
String userPassword = request.getParameter("userPassword");
//和数据库中的密码进行比对,调用service;
UserServiceImpl userService = new UserServiceImpl();
User user = userService.login(userCode, userPassword); //已经把登录的人查出来了
if (user != null) { //查有此人, 可以登录
//将用户的信息放到Session中
request.getSession().setAttribute(Constants.USER_SESSION, user);
//跳转到主页
response.sendRedirect("jsp/frame.jsp");
} else { //查无此人,无法登陆
//转发回登录界面 , 提示用户名或密码错误
request.setAttribute("error", "用户名或密码错误,请重新尝试亲!");
request.getRequestDispatcher("login.jsp").forward(request, response);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
} ```
8、注册 Servlet
```xml
9、测试访问,确保以上功能成功!
4.登录功能优化
注销功能:
思路:移除 Session,返回登录页面
1、编写 Servlet
```java package com.baidou.servlet.user;
import com.baidou.util.Constants;
import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
/ * 注销操作 * Created by baidou on 2020/7/26. */ public class LogoutServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //移除用户的session request.getSession().removeAttribute(Constants.USER_SESSION); //重定向到登录页 response.sendRedirect(request.getContextPath() + "/login.jsp"); }
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
} ```
2、注册 Servlet
```xml
5.登录拦截优化
1、编写一个过滤器,并注册
```java package com.baidou.filter;
import com.baidou.pojo.User; import com.baidou.util.Constants;
import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
/ * Created by baidou on 2020/7/26. */ public class SysFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
//获取request和response
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//过滤器,从Session中获取Session
User user = (User) request.getSession().getAttribute(Constants.USER_SESSION);
if (user == null) { //已经被移除或者注销了,或者未登录
response.sendRedirect("/smbms/error.jsp");
} else {
chain.doFilter(req, resp);
}
}
public void destroy() {
}
} ```
注册 Servlet
```xml
6.密码修改
1、导入前端素材
```html
```
2、写项目,建议从底层向上写
3、UserDao 接口
java
//修改当前用户密码
public int updatePwd(Connection connection,int id,String password)throws SQLException;
4、UserDaoImpl 接口实现类
```java / * 修改用户密码 * * @param connection * @param id * @param password * @return * @throws SQLException */ public int updatePwd(Connection connection,int id,String password)throws SQLException{ PreparedStatement pstm=null; int code=0; if(connection!=null){ String sql="update smbms_user set smbms_user.userPassword=? where id=? "; Object[]param={password,id}; code=BaseDao.execute(connection,pstm,sql,param); if(code>0){ System.out.println("updatePwd=>密码修改成功!"); }else{ System.out.println("updatePwd=>密码修改失败!!!!!!!"); }
return code;
}
return code;
}
```
5、UserService 层
java
//根据用户ID修改密码
public boolean updatePwd(int id,int pwd)throws SQLException;
6、UserService 实现类
```java / * 修改用户ID修改密码 * * @param id * @param pwd * @return * @throws SQLException */ public boolean updatePwd(int id,int pwd){ Connection connection=null; connection=BaseDao.getConnection(); boolean flag=false;
try{
if(userDao.updatePwd(connection,id,pwd)>0){ //判断是否修改成功
flag=true;
}
}catch(SQLException throwables){
throwables.printStackTrace();
}finally{
BaseDao.closeResource(connection,null,null);
}
return flag;
}
```
7、Servlet 实现复用,需要提取出方法!
```java package com.baidou.servlet.user;
import com.baidou.pojo.User; import com.baidou.service.user.UserService; import com.baidou.service.user.UserServiceImpl; import com.baidou.util.Constants; import com.mysql.jdbc.StringUtils;
import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
/ * user控制器,实现servlet复用 * Created by baidou on 2020/7/28. */ public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getParameter("method"); if (method.equals("savepwd") && method != null) { this.updatePwd(req, resp); } }
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
public void updatePwd(HttpServletRequest req, HttpServletResponse resp) {
//从Session中取id
Object o = req.getSession().getAttribute(Constants.USER_SESSION);
String newpassword = req.getParameter("newpassword");
boolean flag = false;
if (o != null && newpassword != null && newpassword.length() != 0) {
UserService userService = new UserServiceImpl();
flag = userService.updatePwd(((User) o).getId(), newpassword);
if (flag) {
req.setAttribute("message", "修改密码成功,请退出,使用新密码登录!");
//密码修改成功移除当前Session
req.getSession().removeAttribute(Constants.USER_SESSION);
} else {
req.setAttribute("message", "密码修改失败");
}
} else {
req.setAttribute("message", "新密码有问题");
}
try {
req.getRequestDispatcher("pwdmodify.jsp").forward(req, resp);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
```
8、测试
优化密码修改使用 AJAX;
- 阿里巴巴 fastjson
```xml
```java package com.baidou.servlet.user;
import com.alibaba.fastjson.JSONArray; import com.baidou.pojo.User; import com.baidou.service.user.UserService; import com.baidou.service.user.UserServiceImpl; import com.baidou.util.Constants; import com.mysql.jdbc.StringUtils;
import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map;
/ * user控制器,实现servlet复用 * Created by baidou on 2020/7/28. */ public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getParameter("method"); if (method.equals("savepwd") && method != null) { this.updatePwd(req, resp); } else if (method.equals("pwdmodify") && method != null) { this.pwdModify(req, resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
/
* 修改密码
*
* @param req
* @param resp
*/
public void updatePwd(HttpServletRequest req, HttpServletResponse resp) {
//从Session中取id
Object o = req.getSession().getAttribute(Constants.USER_SESSION);
//获取前端参数
String newpassword = req.getParameter("newpassword");
boolean flag = false;
if (o != null && newpassword != null && newpassword.length() != 0) {
UserService userService = new UserServiceImpl();
flag = userService.updatePwd(((User) o).getId(), newpassword);
if (flag) {
req.setAttribute("message", "修改密码成功,请退出,使用新密码登录!");
//密码修改成功移除当前Session
req.getSession().removeAttribute(Constants.USER_SESSION);
} else {
req.setAttribute("message", "密码修改失败");
}
} else {
req.setAttribute("message", "新密码有问题");
}
try {
req.getRequestDispatcher("pwdmodify.jsp").forward(req, resp);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/
* 验证旧密码,session中有用户的密码
*/
public void pwdModify(HttpServletRequest req, HttpServletResponse resp) {
//从session拿到用户
Object o = req.getSession().getAttribute(Constants.USER_SESSION);
//获取前端参数
String oldpassword = req.getParameter("oldpassword");
//用Map装Ajax返回界面中的数据
Map<String, String> resultMap = new HashMap<String, String>();
if (o == null) { //Session过期了
resultMap.put("result", "sessionerror");
} else if (StringUtils.isNullOrEmpty(oldpassword)) { //判断输入密码是否为空
resultMap.put("result", "error");
} else {
String userPassword = ((User) o).getUserPassword(); //Session中的用户密码
if (oldpassword.equals(userPassword)) { //校验接收的oldpassword是否与session中的密码一致
resultMap.put("result", "true");
} else {
resultMap.put("result", "false");
}
}
try {
//设置响应消息格式为json
req.setCharacterEncoding("utf-8");
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
//JSONArray 阿里巴巴的JSON工具类, 转换格式
writer.write(JSONArray.toJSONString(resultMap));
writer.flush(); //刷新
writer.close(); //关闭
} catch (IOException e) {
e.printStackTrace();
}
}}
7.用户管理实现
思虑:
1、导入分页的工具类
2、用户列表页面导入
AJAX
JSON
简介
什么是 JSON?
JSON ( JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。
采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
在 JavaScript 语言中,一切都是对象。因此,任何 JavaScript 支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。看看他的要求和语法格式:
- 对象表示为键值对,数据由逗号分隔
- 花括号保存对象
- 方括号保存数组
JSON 键值对是用来保存 JavaScript 对象的一种方式,和 JavaScript 对象的写法也大同小异,键/值对组合中的键名写在前面并用双引号 "" 包裹,使用冒号 : 分隔,然后紧接着值:
php
{"name": "zhangsan"}
{"age": "18"}
{"sex": "男"}
很多人搞不清楚 JSON 和 JavaScript 对象的关系,甚至连谁是谁都不清楚。其实,可以这么理解:
JSON 是 JavaScript 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。
php
var obj = {a: 'Hello', b: 'World'}; //这是一个对象,注意键名也是可以使用引号包裹的
var json = '{"a": "Hello", "b": "World"}'; //这是一个 JSON 字符串,本质是一个字符串
JSON 和 JavaScript 对象互转
要实现从 JSON 字符串转换为 JavaScript 对象,使用 JSON.parse() 方法:
php
var obj = JSON.parse('{"a": "Hello", "b": "World"}');//结果是 {a: 'Hello', b: 'World'}
要实现从 JavaScript 对象转换为 JSON 字符串,使用 JSON.stringify() 方法:
php
var json = JSON.stringify({a: 'Hello', b: 'World'});//结果是 '{"a": "Hello", "b": "World"}'
AJAX
简介
- AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
- AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
- AJAX 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的 Web 应用程序的技术。
- 在 2005 年,Google 通过其 Google Suggest 使 AJAX 变得流行起来。Google Suggest 能够自动帮你完成搜索单词。
- Google Suggest 使用 AJAX 创造出动态性极强的 Web 界面:当您在谷歌的搜索框输入关键字时,JavaScript 会把这些字符发送到服务器,然后服务器会返回一个搜索建议的列表。
- 就和国内百度的搜索框一样!
- 传统的网页(即不用 AJAX 技术的网页),想要更新内容或者提交一个表单,都需要重新加载整个网页。
- 使用 AJAX 技术的网页,通过在后台服务器进行少量的数据交换,就可以实现异步局部更新。
- 使用 AJAX,用户可以创建接近本地桌面应用的直接、高可用、更丰富、更动态的 Web 用户界面。
参考文献
- 超市信息管理系统设计与实现(山东大学·王一君)
- 外贸订单管理系统的设计与实现(山东大学·曹美玉)
- 基于J2EE的订单管理系统的设计与实现(北京邮电大学·周伟)
- 超市商品销售管理系统的设计与实现(电子科技大学·刘华平)
- 企业订单跟踪管理系统的设计与实现(电子科技大学·吴曼霞)
- 基于J2EE架构的超市管理系统的研究与开发(西安电子科技大学·毛勇博)
- 基于J2EE架构的超市管理系统的研究与开发(西安电子科技大学·毛勇博)
- 基于JSP的采购管理系统的设计与实现(大连理工大学·于欢)
- 超市管理系统的设计与实现(吉林大学·白小凡)
- 基于J2EE架构的超市管理系统的研究与开发(西安电子科技大学·毛勇博)
- 基于.NET平台的积分商诚订单管理系统(大连理工大学·赵丹)
- 淄矿物资供应超市的设计与实现(西安科技大学·张恒)
- 超市信息管理系统设计与实现(山东大学·王一君)
- 基于J2EE架构的超市管理系统的研究与开发(西安电子科技大学·毛勇博)
- 基于J2EE架构的超市管理系统的研究与开发(西安电子科技大学·毛勇博)
本文内容包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主题。发布者:代码工厂 ,原文地址:https://m.bishedaima.com/yuanma/35939.html