当前位置:网站首页>Development of a horse tourism website (realization of login, registration and exit function)
Development of a horse tourism website (realization of login, registration and exit function)
2022-07-08 00:10:00 【Haohao likes pork】
Preface : This project is a review and consolidation of basic knowledge , Knowledge that does not involve the back-end framework , Use only maven Project management tools
Technology selection
- The network layer
- servlet: Front controller
- html: View
- filter: filter
- beanUtils: Data encapsulation
- jackson:json Serialization tool
- Service layer
- javamail:java Send mail tool
- redis:nosql In-memory database
- jedis:java Of redis client
- dao layer
- mysql: database
- druid: Database connection pool
- jdbcTemplate:jdbc Tools
Catalog
0.0.0 User entity class creation
0.0.2 Register the front page effect
0.0.3 Front end registration interface code (js part )
0.1.0 Register logical analysis
0.2.0 Back end code implementation
0.2.1 To write UserDao as well as UserDaoimp
0.2.2 To write UserService as well as UserServiceImpl
0.2.3 To write RegistUserServlet
0.2.4 To write ActiveUserServlet
2. Exit function implementation
2.1 Front end code implementation
2.2 Back end code implementation
0. Registration function
0.0 Get ready
Documents at all levels are created as follows

0.0.0 User entity class creation
package com.sixstar.travel.domain;
import java.io.Serializable;
/**
* User entity class
*/
public class User implements Serializable {
private int uid;// user id
private String username;// user name , account number
private String password;// password
private String name;// Real name
private String birthday;// Date of birth
private String sex;// Male or female
private String telephone;// cell-phone number
private String email;// mailbox
private String status;// active ,Y Represents activation ,N Indicates that... Is not activated
private String code;// Activation code ( Ask for the only )
/**
* Nonparametric construction method
*/
public User() {
}
/**
* Parametric construction method
* @param uid
* @param username
* @param password
* @param name
* @param birthday
* @param sex
* @param telephone
* @param email
* @param status
* @param code
*/
public User(int uid, String username, String password, String name, String birthday, String sex, String telephone, String email, String status, String code) {
this.uid = uid;
this.username = username;
this.password = password;
this.name = name;
this.birthday = birthday;
this.sex = sex;
this.telephone = telephone;
this.email = email;
this.status = status;
this.code = code;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
.............................
public void setCode(String code) {
this.code = code;
}
}
0.0.1 User table creation
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`uid` int(0) UNSIGNED NOT NULL AUTO_INCREMENT,
`username` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`password` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`birthday` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`sex` varchar(2) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`telephone` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`email` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`status` varchar(2) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`uid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;0.0.2 Register the front page effect

0.0.3 Front end registration interface code (js part )
/* Form validation :
* username:/^\w{8,20}/
* password:/^\w{8,20}/
* email:/^[A-Za-z0-9\u4e00-\u9fa5][email protected][a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/
* */
// User name validation
function checkUsername(){
var username = $("#username").val();
var reg_username = /^\w{8,20}/;
var flog = reg_username.test(username);
if (flog){
$("#username").css("border","")
}else{
$("#username").css("border","1px solid red")
}
return flog;
}
// Password validation
function checkPassword(){
var password = $("#password").val();
var reg_password = /^\w{8,20}/;
var flog = reg_password.test(password);
if (flog){
$("#password").css("border","")
}else{
$("#password").css("border","1px solid red")
}
return flog;
}
// Email validation
function checkEmail(){
var email = $("#email").val();
var reg_email = /^[A-Za-z0-9\u4e00-\u9fa5][email protected][a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;
var flog = reg_email.test(email);
if (flog){
$("#email").css("border","")
}else{
$("#email").css("border","1px solid red")
}
return flog;
}
// Form verification function
function checkAll() {
return checkUsername()&&checkPassword()&&checkEmail();
}
// Bind defocus events
$("#username").blur(checkUsername());
$("#password").blur(checkPassword());
$("#email").blur(checkEmail());
$(function () {
$("#registerForm").submit(function () {
if(checkAll()){
// All the form items have passed the verification
$.post("registUserServlet",$(this).serialize(),function (data) {
if(data.flag){
location.href="register_ok.html";
}else {
$("#errorMsg").html(data.errorMsg);
}
});
}
// Form item validation failed , Intercept submission requests
return false;
})
})0.1.0 Register logical analysis

0.2.0 Back end code implementation
0.2.1 To write UserDao as well as UserDaoimp
UserDao
package com.haohao.travel.dao;
import com.haohao.travel.domain.User;
public interface UserDao {
/**
* Query user information according to user name
* @param username
* @return
*/
User findByUsername(String username);
/**
* Save user information
* @param user
*/
void save(User user);
/**
* Find the user through the activation code
* @param code
* @return
*/
User findByCode(String code);
/**
* Modify the status code
* @param user
*/
void updataStatus(User user);
}
UserDaoimp
package com.haohao.travel.dao.impl;
import com.haohao.travel.dao.UserDao;
import com.haohao.travel.domain.User;
import com.haohao.travel.util.JDBCUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
public class UserDaoImpl implements UserDao {
// Define database templates , coordination Bean Object is used to convert the columns queried from the database into objects
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
/**
* Search for users by user name
* @param username
* @return
*/
@Override
public User findByUsername(String username) {
User user = null;
try{
// Definition sql
String sql = "select * from user where username=?";
// perform sql
user = template.queryForObject(sql,new BeanPropertyRowMapper<User>(User.class),username);
}catch (Exception e){
}
return user;
}
/**
* adopt user Objects are saved to the database
* @param user
*/
@Override
public void save(User user) {
//1. Definition sql
String sql = "insert into user(username,password,name,birthday,sex,telephone,email,status,code) values(?,?,?,?,?,?,?,?,?)";
// perform sql
template.update(sql,user.getUsername(), user.getPassword(), user.getName(), user.getBirthday(), user.getSex(), user.getTelephone(), user.getEmail(), user.getStatus(), user.getCode() );
}
/**
* Find user information through activation code
* @param code
* @return
*/
@Override
public User findByCode(String code) {
User user = null;
try{
// Definition sql
String sql = "select * from user where code=?";
// perform sql
user = template.queryForObject(sql,new BeanPropertyRowMapper<User>(User.class),code);
}catch (Exception e){
}
return user;
}
/**
* Modify the activation status of the user
* @param user
*/
@Override
public void updataStatus(User user) {
String sql = " update user set status = 'Y' where uid=?";
template.update(sql,user.getUid());
}
}0.2.2 To write UserService as well as UserServiceImpl
UserService
package com.haohao.travel.service;
import com.haohao.travel.domain.User;
public interface UserService {
/**
* User registration
* @param user
* @return
*/
boolean regist(User user);
/**
* User activation
* @param code
* @return
*/
boolean active(String code);
}
UserServiceImpl
package com.haohao.travel.service.impl;
import com.haohao.travel.dao.UserDao;
import com.haohao.travel.dao.impl.UserDaoImpl;
import com.haohao.travel.domain.User;
import com.haohao.travel.service.UserService;
import com.haohao.travel.util.MailUtils;
import com.haohao.travel.util.UuidUtil;
public class UserServiceImpl implements UserService {
// user dao object
private UserDao userDao = new UserDaoImpl();
@Override
public boolean regist(User user) {
User u = userDao.findByUsername(user.getUsername());
if (u!=null){
// The user name and already exist , Registration failed
return false;
}
// The username does not exist , Save user to database
// Set activation code and activation status
user.setCode(UuidUtil.getUuid());
user.setStatus("N");
userDao.save(user);
// Send activation email
String text = "<a href='http://localhost/travel/activeUserServlet?code="+user.getCode()+"'></a>";
MailUtils.sendMail(user.getEmail(),text," Activate mail ");
return true;
}
@Override
public boolean active(String code) {
User user = userDao.findByCode(code);
if (user!=null){
// Modify the activation status of this user
userDao.updataStatus(user);
return true;
}
return false;
}
}0.2.3 To write RegistUserServlet
package com.haohao.travel.web.servlet;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.haohao.travel.domain.ResultInfo;
import com.haohao.travel.domain.User;
import com.haohao.travel.service.UserService;
import com.haohao.travel.service.impl.UserServiceImpl;
import org.apache.commons.beanutils.BeanUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
@WebServlet("/registUserServlet")
public class RegistUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// Image verification code validation
String check = req.getParameter("check");
HttpSession session = req.getSession();
String checkcode_server = (String) session.getAttribute("CHECKCODE_SERVER");
// The image verification code is invalid once used
session.removeAttribute("CHECKCODE_SERVER");
if (checkcode_server==null||!checkcode_server.equals(check)){
// Picture verification code verification failed
// Result information object , Store the data that needs to be returned to the front end
ResultInfo info = new ResultInfo(false," Verification code error ");
// Convert the information result object to json Format
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(info);
// Set the response header to json Format
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write(json);
return;
}
// The graphic verification code is verified successfully , Get... In the request user Information , Encapsulated in the user object
Map<String,String[]> map = req.getParameterMap();
User user = new User();
try{
BeanUtils.populate(user,map);
} catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
// call UserService Register in
UserService userService = new UserServiceImpl();
boolean flag = userService.regist(user);
ResultInfo info = null;
if (flag){
// Registered successfully
info = new ResultInfo(true);
}else {
info = new ResultInfo(false," Registration failed ");
}
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(info);
// Set the response header to json Format
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write(json);
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// this.doPost(req,resp);
resp.setStatus(404);
resp.getWriter().write(" The request method is illegal ");
}
}
0.2.4 To write ActiveUserServlet
package com.haohao.travel.web.servlet;
import com.haohao.travel.service.UserService;
import com.haohao.travel.service.impl.UserServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ActiveUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// this.doGet(req,resp);
resp.getWriter().write(" Illegal access ");
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg =" Activation failed , Please check whether the activation code is valid ";
String code = req.getParameter("code");
if (code!=null){
UserService userService = new UserServiceImpl();
boolean flag = userService.active(code);
if (flag){
// Activation successful
msg = " Activation successful , please <a href='login.html'> Sign in </a>!";
}
}
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write(msg);
}
}
1. Login function realization
1.0 analysis
1.0.0 Page effects

1.1 Code implementation
1.1.0dao layer
userdao
/**
*
* Find users by user name and password
* @param username
* @param password
* @return
*/
User findByUsernameAndPassword(String username,String password); /**
* Search for users by username and password
* @param username
* @param password
* @return
*/
@Override
public User findByUsernameAndPassword(String username, String password) {
User user =null;
try{
// Definition sql
String sql = "select * from tab_user where username=? and password=?";
// perform sql
user = template.queryForObject(sql,new BeanPropertyRowMapper<User>(User.class),username,password);
}catch (Exception e){
}
return user;
}1.1.1server layer
userServer
/**
* The user login
* @param user
* @return
*/
User login(User user);userServerImpl
/**
* The user login
* @param user
* @return
*/
@Override
public User login(User user) {
return userDao.findByUsernameAndPassword(user.getUsername(),user.getPassword()); ;
}1.1.2web layer
LoginUserServlet
package com.haohao.travel.web.servlet;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.haohao.travel.domain.ResultInfo;
import com.haohao.travel.domain.User;
import com.haohao.travel.service.UserService;
import com.haohao.travel.service.impl.UserServiceImpl;
import org.apache.commons.beanutils.BeanUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
@WebServlet("/loginServlet")
public class LoginUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// Image verification code validation
String check = req.getParameter("check");
HttpSession session = req.getSession();
String checkcode_server = (String) session.getAttribute("CHECKCODE_SERVER");
// The image verification code is invalid once used
session.removeAttribute("CHECKCODE_SERVER");
if (checkcode_server==null||!checkcode_server.equals(check)){
// Picture verification code verification failed
// Result information object , Store the data that needs to be returned to the front end
ResultInfo info = new ResultInfo(false," Verification code error ");
// Convert the information result object to json Format
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(info);
// Set the response header to json Format
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write(json);
return;
}
// The graphic verification code is verified successfully , Get... In the request user Information , Encapsulated in the user object
Map<String,String[]> map = req.getParameterMap();
User user = new User();
try{
BeanUtils.populate(user,map);
} catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
// call UserService Login in
UserService userService = new UserServiceImpl();
// After landing, user object , by null Login fails
user = userService.login(user);
ResultInfo info = new ResultInfo();
if (user==null){
// Login failed
info.setFlag(false);
info.setErrorMsg(" Username or password incorrect ");
}else if(user.getStatus().equals("N")){
// User not activated
info.setFlag(false);
info.setErrorMsg(" Please go to the mailbox to activate ");
}else {
// Successfully logged in , State to keep
req.getSession().setAttribute("user",user);
info.setFlag(true);
}
// Respond to json data
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(info);
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write(json);
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// this.doPost(req,resp);
resp.getWriter().write(" Illegal access ");
}
}
findUserServlet
package com.haohao.travel.web.servlet;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.haohao.travel.domain.ResultInfo;
import com.haohao.travel.domain.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/findUserServlet")
public class FindUserServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
User user = null;
try{
user= (User) req.getSession().getAttribute("user");
}catch (Exception e){
}
ResultInfo info=new ResultInfo();
if(user!=null){
// Logged in
info.setFlag(true);
info.setData(user.getUsername());
}else {
info.setFlag(false);
}
ObjectMapper mapper=new ObjectMapper();
String json = mapper.writeValueAsString(info);
// Set the response header to json Format
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write(json);
}
}
1.1.3 The front-end code
Landing page
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title> Black horse travel network - Sign in </title>
<link rel="stylesheet" type="text/css" href="css/common.css">
<link rel="stylesheet" type="text/css" href="css/login.css">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<!-- Import angularJS file -->
<script src="js/angular.min.js"></script>
<!-- Import jquery-->
<script src="js/jquery-3.3.1.js"></script>
<script>
$(function () {
$("#btn_sub").submit(function () {
$.post("loginServlet",$("#loginForm").serialize(),function (data) {
if (data.flag){
// Landing successful , Jump to home page
location.href="index.html"
}else {
// Login failed
$("#errorMsg").html(data.errorMsg);
return false;
}
});
});
});
</script>
</head>
<body>
<!-- Introduce the head -->
<div id="header"></div>
<!-- Head end -->
<section id="login_wrap">
<div class="fullscreen-bg" style="background: url(images/login_bg.png);height: 532px;">
</div>
<div class="login-box">
<div class="title">
<img src="images/login_logo.png" alt="">
<span> Welcome to the dark horse travel account </span>
</div>
<div class="login_inner">
<!-- Login error message -->
<div id="errorMsg" class="alert alert-danger" ></div>
<form id="loginForm" action="" method="post" accept-charset="utf-8">
<input type="hidden" name="action" value="login"/>
<input name="username" type="text" placeholder=" Please enter your account number " autocomplete="off">
<input name="password" type="text" placeholder=" Please input a password " autocomplete="off">
<div class="verify">
<input name="check" type="text" placeholder=" Please enter the verification code " autocomplete="off">
<span><img src="checkCode" alt="" onclick="changeCheckCode(this)"></span>
<script type="text/javascript">
// Picture click event
function changeCheckCode(img) {
img.src="checkCode?"+new Date().getTime();
}
</script>
</div>
<div class="submit_btn">
<button type="button" id="btn_sub"> Sign in </button>
<div class="auto_login">
<input type="checkbox" name="" class="checkbox">
<span> automatic logon </span>
</div>
</div>
</form>
<div class="reg"> No account ?<a href="javascript:;"> Register now </a></div>
</div>
</div>
</section>
<!-- Introduce the tail -->
<div id="footer"></div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="js/jquery-1.11.0.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="js/bootstrap.min.js"></script>
<!-- Import layout js, share header and footer-->
<script type="text/javascript" src="js/include.js"></script>
</body>
</html>Homepage nickname display
<script>
// Get login user name
$(function () { $.get("findUserServlet",{},function (data) {
//{uid:1,name:' Li Si '}
if(data.flag){
// Has landed
var msg = " welcome back ,"+data.name;
$("#span_username").html(msg);
$(".login_out").hide()
$(".login").show()
}else {
// Not logged in
$(".login").hide()
$(".login_out").show()
}
});
});
</script>2. Exit function implementation
2.0 Get ready
2.0.0 Page effects

Click exit to delete the login information , And jump to the login interface
2.1 Front end code implementation
2.1.0 modify header.html
<a href="javascript:location.href='exitServlet';"> sign out </a>2.2 Back end code implementation
2.2.0exitServlet
package com.haohao.travel.web.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/exitServlet")
public class ExitUserServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// Destroy login data
req.getSession().invalidate();
// Jump to the login interface
resp.sendRedirect(req.getContextPath()+"/login.html");
}
}
边栏推荐
- 一个测试工程师的7年感悟 ---- 致在一路独行的你(别放弃)
- Is it safe for tongdaxin to buy funds?
- Restricted linear table
- STM32F1與STM32CubeIDE編程實例-旋轉編碼器驅動
- 35岁真就成了职业危机?不,我的技术在积累,我还越吃越香了
- 10 schemes to ensure interface data security
- Chisel tutorial - 00 Ex.scala metals plug-in (vs Code), SBT and coursier exchange endogenous
- C - linear table
- FFA and ICGA angiography
- 关于组织2021-2022全国青少年电子信息智能创新大赛西南赛区(四川)复赛的通知
猜你喜欢

DataGuard active / standby cleanup archive settings

Archery installation test

QT creator add JSON based Wizard

Anaconda+pycharm+pyqt5 configuration problem: pyuic5 cannot be found exe

Basic learning of SQL Server -- creating databases and tables with code

Chisel tutorial - 05 Sequential logic in chisel (including explicit multi clock, explicit synchronous reset and explicit asynchronous reset)

ROS from entry to mastery (IX) initial experience of visual simulation: turtlebot3
![[programming problem] [scratch Level 2] draw ten squares in December 2019](/img/4f/14ea8e786b7f8b0a263aa5c55abf15.png)
[programming problem] [scratch Level 2] draw ten squares in December 2019

Stm32f1 and stm32cubeide programming example - rotary encoder drive

Go learning notes (2) basic types and statements (1)
随机推荐
Resolve the URL of token
Les mots ont été écrits, la fonction est vraiment puissante!
The result of innovation in professional courses such as robotics (Automation)
Fully automated processing of monthly card shortage data and output of card shortage personnel information
Database interview questions + analysis
SQL knowledge summary 004: Postgres terminal command summary
Magic fast power
QT and OpenGL: load 3D models using the open asset import library (assimp)
[programming problem] [scratch Level 2] December 2019 flying birds
Go time package common functions
【编程题】【Scratch二级】2019.12 飞翔的小鸟
Emotional post station 010: things that contemporary college students should understand
95. (cesium chapter) cesium dynamic monomer-3d building (building)
Handwriting a simulated reentrantlock
Detailed explanation of interview questions: the history of blood and tears in implementing distributed locks with redis
STM32F1與STM32CubeIDE編程實例-旋轉編碼器驅動
Coindesk comments on the decentralization process of the wave field: let people see the future of the Internet
52岁的周鸿祎,还年轻吗?
快速回复二极管整流特性
Laser slam learning (2d/3d, partial practice)