├── README.md ├── images ├── 20200626165110.jpg ├── 20200626220115.jpg ├── 20200626220758.jpg └── 20200626222701.jpg ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── yusael │ │ └── travels │ │ ├── TravelsApplication.java │ │ ├── controller │ │ ├── PlaceController.java │ │ ├── ProvinceController.java │ │ └── UserController.java │ │ ├── dao │ │ ├── BaseDAO.java │ │ ├── PlaceDAO.java │ │ ├── ProvinceDAO.java │ │ └── UserDAO.java │ │ ├── entity │ │ ├── Place.java │ │ ├── Province.java │ │ ├── Result.java │ │ └── User.java │ │ ├── service │ │ ├── PlaceService.java │ │ ├── PlaceServiceImpl.java │ │ ├── ProvinceService.java │ │ ├── ProvinceServiceImpl.java │ │ ├── UserService.java │ │ └── UserServiceImpl.java │ │ └── utils │ │ └── CreateImageCode.java └── resources │ ├── application.properties │ ├── com │ └── yusael │ │ └── travels │ │ └── mapper │ │ ├── PlaceMapperDAO.xml │ │ ├── ProvinceMapperDAOMapper.xml │ │ └── UserMapperDAOMapper.xml │ └── static │ ├── css │ └── style.css │ ├── img │ ├── gug.jpg │ ├── tianam.jpg │ ├── timg.jpg │ ├── vcode.png │ └── yihy.jpg │ ├── js │ ├── axios.min.js │ └── vue.js │ ├── login.html │ ├── province │ ├── addprovince.html │ ├── provincelist.html │ └── updateprovince.html │ ├── reg.html │ └── viewspot │ ├── addviewspot.html │ ├── updateviewspot.html │ └── viewspotlist.html └── test └── java └── com └── yusael └── travels └── test └── TestUserService.java /README.md: -------------------------------------------------------------------------------- 1 | 2 | # 项目简介 3 | **所需技术栈**: 4 | * 后端技术栈:**springboot** + **mybatis** 5 | * 前后端分离:axios、json 6 | * 前端技术栈、技术架构:Vue、node.js 7 | 8 | **前置知识**: 9 | * 了解 Vue 组件之前的知识 10 | * 对 springboot + mybatis 较熟悉 11 | 12 | 开发流程: 13 | * 需求分析 14 | * 库表设计 15 | * 编码(项目环境搭建+编码) 16 | * 项目调试 17 | * 项目部署上线 18 | 19 | **需求分析**: 20 | * 用户模块:登录 + 注册 21 | * 省份模块:一个省份可能存在多个景点 22 | * 景点模块:一个景点对应多个省份 23 | 24 | ## 项目演示 25 | 进入系统需登录: 26 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200626223232435.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzczNDA5NQ==,size_16,color_FFFFFF,t_70) 27 | 用户注册页面: 28 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/2020062622343822.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzczNDA5NQ==,size_16,color_FFFFFF,t_70) 29 | 省份列表页面: 30 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200626223254725.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzczNDA5NQ==,size_16,color_FFFFFF,t_70) 31 | 添加省份页面: 32 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200626223631355.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzczNDA5NQ==,size_16,color_FFFFFF,t_70) 33 | 修改省份页面: 34 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200626223309698.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzczNDA5NQ==,size_16,color_FFFFFF,t_70) 35 | 景点列表页面: 36 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200626223346854.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzczNDA5NQ==,size_16,color_FFFFFF,t_70) 37 | 添加景点页面: 38 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200626223556642.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzczNDA5NQ==,size_16,color_FFFFFF,t_70) 39 | 修改景点页面: 40 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200626223359938.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzczNDA5NQ==,size_16,color_FFFFFF,t_70) 41 | ## 数据库建表 42 | 用户表 `t_user` —— 独立的表 43 | * id、username、password、email 44 | 45 | 省份表 `t_province` [省份表 : 景点表] —— [1 : N] 46 | * id、name、tags、placecounts 47 | 48 | 景点表 `t_place` 49 | * id、name、picpath、hottime、hotticket、dimticket、placedes、provinceid(外键) 50 | 51 | --- 52 | 53 | 数据库名:`travels` 54 | 55 | 用户表 SQL: 56 | ```sql 57 | CREATE TABLE t_user( 58 | id INT(6) PRIMARY KEY AUTO_INCREMENT, 59 | username VARCHAR(60), 60 | password VARCHAR(60), 61 | email VARCHAR(60) 62 | ); 63 | ``` 64 | 省份表 SQL: 65 | ```sql 66 | CREATE TABLE t_province( 67 | id INT(6) PRIMARY KEY AUTO_INCREMENT, 68 | name VARCHAR(60), 69 | tags VARCHAR(80), 70 | placecounts INT(4) 71 | ); 72 | ``` 73 | 景点表 SQL: 74 | ```sql 75 | CREATE TABLE t_place( 76 | id INT(6) PRIMARY KEY AUTO_INCREMENT, 77 | name VARCHAR(60), 78 | picpath MEDIUMTEXT, 79 | hottime TIMESTAMP, 80 | hotticket DOUBLE(7,2), 81 | dimticket DOUBLE(7,2), 82 | placedes VARCHAR(300), 83 | provinceid INT(6) REFERENCES t_province(id) 84 | ); 85 | ``` 86 | 87 | # 环境搭建 88 | 利用 **Spring Initializr** 快速搭建 SpringBoot 项目。 89 | 90 | ## 引入依赖(pom.xml) 91 | ```xml 92 | 93 | 95 | 4.0.0 96 | 97 | 98 | 99 | org.springframework.boot 100 | spring-boot-starter-parent 101 | 2.3.1.RELEASE 102 | 103 | 104 | 105 | com.yusael 106 | mytravels 107 | 0.0.1-SNAPSHOT 108 | mytravels 109 | springboot + vue 110 | 111 | 112 | 1.8 113 | 114 | 115 | 116 | 117 | 118 | org.springframework.boot 119 | spring-boot-starter-web 120 | 121 | 122 | 123 | org.mybatis.spring.boot 124 | mybatis-spring-boot-starter 125 | 2.1.2 126 | 127 | 128 | 129 | org.springframework.boot 130 | spring-boot-devtools 131 | runtime 132 | true 133 | 134 | 135 | 136 | mysql 137 | mysql-connector-java 138 | runtime 139 | 140 | 141 | 142 | org.projectlombok 143 | lombok 144 | true 145 | 146 | 147 | 148 | com.alibaba 149 | druid 150 | 1.1.12 151 | 152 | 153 | 154 | org.springframework.boot 155 | spring-boot-starter-test 156 | 157 | 158 | 159 | commons-fileupload 160 | commons-fileupload 161 | 1.4 162 | 163 | 164 | 165 | org.springframework.boot 166 | spring-boot-starter-test 167 | test 168 | 169 | 170 | org.junit.vintage 171 | junit-vintage-engine 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | org.springframework.boot 181 | spring-boot-maven-plugin 182 | 183 | 184 | 185 | 186 | 187 | ``` 188 | 189 | ## 配置文件(application.properties) 190 | application.properties: 191 | ```properties 192 | server.port=8989 193 | spring.application.name=travels 194 | 195 | spring.datasource.type=com.alibaba.druid.pool.DruidDataSource 196 | spring.datasource.driver-class-name=com.mysql.jdbc.Driver 197 | spring.datasource.url=jdbc:mysql://localhost:3306/travels?characterEncoding=UTF-8 198 | spring.datasource.username=root 199 | spring.datasource.password=1234 200 | 201 | mybatis.mapper-locations=classpath:com/yusael/travels/mapper/*.xml 202 | mybatis.type-aliases-package=com.yusael.travels.entity 203 | 204 | logging.level.root=info 205 | logging.level.com.yusael.travels.dao=debug 206 | 207 | # 上传的图片存放的路径 208 | upload.dir=D:/CodePro/IdeaPro/SpringBoot/travels/images 209 | spring.resources.static-locations=file:${upload.dir} 210 | ``` 211 | 212 | 213 | # href="javascript:;" 含义 214 | 代码中经常遇到这种写法: 215 | ```html 216 | 删除省份 217 | ``` 218 | 其中的 `href="javascript:;"` 是什么意思呢? 219 | * `javascript:` 表示在触发默认动作时,执行一段 JavaScript 代码; 220 | * `javascript:;` 表示什么都不执行,这样点击时就没有任何反应,相当于去掉 a 标签的默认行为。 221 | 222 | # select - option 绑定 Vue 实例 223 | `select` 中 通过 `v-model` 绑定当前的选项,`option` 中使用 `v-for` 遍历显示所有选项。 224 | ```html 225 | 231 | ``` 232 | 233 | # 删除时增加确认选项 234 | ```js 235 | if (confirm("确定要删除景点吗?")) { 236 | // code.... 237 | } 238 | ``` 239 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200626210046689.png) 240 | # Vue 获取地址栏跳转的参数 241 | 对于这么一个 a 标签,我们要在另一个页面获取这个 url 的参数 id: 242 | ```html 243 | 修改省份 244 | ``` 245 | 可以通过 `location.href` 获取 url 再进行截取: 246 | ```js 247 | var id = location.href.substring(location.href.indexOf("=") + 1); 248 | ``` 249 | 250 | # 前后端分离项目—验证码功能 251 | 验证码工具类: 252 | ```java 253 | package com.yusael.travels.utils; 254 | 255 | 256 | import java.awt.Color; 257 | import java.awt.Font; 258 | import java.awt.Graphics; 259 | import java.awt.image.BufferedImage; 260 | import java.io.IOException; 261 | import java.io.OutputStream; 262 | import java.util.Random; 263 | 264 | import javax.imageio.ImageIO; 265 | 266 | 267 | public class CreateImageCode { 268 | // 图片的宽度。 269 | private int width = 160; 270 | // 图片的高度。 271 | private int height = 40; 272 | // 验证码字符个数 273 | private int codeCount = 4; 274 | // 验证码干扰线数 275 | private int lineCount = 20; 276 | // 验证码 277 | private String code = null; 278 | // 验证码图片Buffer 279 | private BufferedImage buffImg = null; 280 | Random random = new Random(); 281 | 282 | public CreateImageCode() { 283 | creatImage(); 284 | } 285 | 286 | public CreateImageCode(int width, int height) { 287 | this.width = width; 288 | this.height = height; 289 | creatImage(); 290 | } 291 | 292 | public CreateImageCode(int width, int height, int codeCount) { 293 | this.width = width; 294 | this.height = height; 295 | this.codeCount = codeCount; 296 | creatImage(); 297 | } 298 | 299 | public CreateImageCode(int width, int height, int codeCount, int lineCount) { 300 | this.width = width; 301 | this.height = height; 302 | this.codeCount = codeCount; 303 | this.lineCount = lineCount; 304 | creatImage(); 305 | } 306 | 307 | // 生成图片 308 | private void creatImage() { 309 | int fontWidth = width / codeCount;// 字体的宽度 310 | int fontHeight = height - 5;// 字体的高度 311 | int codeY = height - 8; 312 | 313 | // 图像buffer 314 | buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 315 | Graphics g = buffImg.getGraphics(); 316 | //Graphics2D g = buffImg.createGraphics(); 317 | // 设置背景色 318 | g.setColor(getRandColor(200, 250)); 319 | g.fillRect(0, 0, width, height); 320 | 321 | 322 | 323 | // 设置字体 324 | //Font font1 = getFont(fontHeight); 325 | Font font = new Font("Fixedsys", Font.BOLD, fontHeight); 326 | g.setFont(font); 327 | 328 | // 设置干扰线 329 | for (int i = 0; i < lineCount; i++) { 330 | int xs = random.nextInt(width); 331 | int ys = random.nextInt(height); 332 | int xe = xs + random.nextInt(width); 333 | int ye = ys + random.nextInt(height); 334 | g.setColor(getRandColor(1, 255)); 335 | g.drawLine(xs, ys, xe, ye); 336 | } 337 | 338 | // 添加噪点 339 | float yawpRate = 0.01f;// 噪声率 340 | int area = (int) (yawpRate * width * height); 341 | for (int i = 0; i < area; i++) { 342 | int x = random.nextInt(width); 343 | int y = random.nextInt(height); 344 | 345 | buffImg.setRGB(x, y, random.nextInt(255)); 346 | } 347 | 348 | 349 | String str1 = randomStr(codeCount);// 得到随机字符 350 | this.code = str1; 351 | for (int i = 0; i < codeCount; i++) { 352 | String strRand = str1.substring(i, i + 1); 353 | g.setColor(getRandColor(1, 255)); 354 | // g.drawString(a,x,y); 355 | // a为要画出来的东西,x和y表示要画的东西最左侧字符的基线位于此图形上下文坐标系的 (x, y) 位置处 356 | 357 | g.drawString(strRand, i*fontWidth+3, codeY); 358 | } 359 | 360 | 361 | } 362 | 363 | // 得到随机字符 364 | private String randomStr(int n) { 365 | String str1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"; 366 | String str2 = ""; 367 | int len = str1.length() - 1; 368 | double r; 369 | for (int i = 0; i < n; i++) { 370 | r = (Math.random()) * len; 371 | str2 = str2 + str1.charAt((int) r); 372 | } 373 | return str2; 374 | } 375 | 376 | // 得到随机颜色 377 | private Color getRandColor(int fc, int bc) {// 给定范围获得随机颜色 378 | if (fc > 255) 379 | fc = 255; 380 | if (bc > 255) 381 | bc = 255; 382 | int r = fc + random.nextInt(bc - fc); 383 | int g = fc + random.nextInt(bc - fc); 384 | int b = fc + random.nextInt(bc - fc); 385 | return new Color(r, g, b); 386 | } 387 | 388 | /** 389 | * 产生随机字体 390 | */ 391 | private Font getFont(int size) { 392 | Random random = new Random(); 393 | Font font[] = new Font[5]; 394 | font[0] = new Font("Ravie", Font.PLAIN, size); 395 | font[1] = new Font("Antique Olive Compact", Font.PLAIN, size); 396 | font[2] = new Font("Fixedsys", Font.PLAIN, size); 397 | font[3] = new Font("Wide Latin", Font.PLAIN, size); 398 | font[4] = new Font("Gill Sans Ultra Bold", Font.PLAIN, size); 399 | return font[random.nextInt(5)]; 400 | } 401 | 402 | // 扭曲方法 403 | private void shear(Graphics g, int w1, int h1, Color color) { 404 | shearX(g, w1, h1, color); 405 | shearY(g, w1, h1, color); 406 | } 407 | 408 | private void shearX(Graphics g, int w1, int h1, Color color) { 409 | 410 | int period = random.nextInt(2); 411 | 412 | boolean borderGap = true; 413 | int frames = 1; 414 | int phase = random.nextInt(2); 415 | 416 | for (int i = 0; i < h1; i++) { 417 | double d = (double) (period >> 1) 418 | * Math.sin((double) i / (double) period 419 | + (6.2831853071795862D * (double) phase) 420 | / (double) frames); 421 | g.copyArea(0, i, w1, 1, (int) d, 0); 422 | if (borderGap) { 423 | g.setColor(color); 424 | g.drawLine((int) d, i, 0, i); 425 | g.drawLine((int) d + w1, i, w1, i); 426 | } 427 | } 428 | 429 | } 430 | 431 | private void shearY(Graphics g, int w1, int h1, Color color) { 432 | 433 | int period = random.nextInt(40) + 10; // 50; 434 | 435 | boolean borderGap = true; 436 | int frames = 20; 437 | int phase = 7; 438 | for (int i = 0; i < w1; i++) { 439 | double d = (double) (period >> 1) 440 | * Math.sin((double) i / (double) period 441 | + (6.2831853071795862D * (double) phase) 442 | / (double) frames); 443 | g.copyArea(i, 0, 1, h1, 0, (int) d); 444 | if (borderGap) { 445 | g.setColor(color); 446 | g.drawLine(i, (int) d, i, 0); 447 | g.drawLine(i, (int) d + h1, i, h1); 448 | } 449 | 450 | } 451 | 452 | } 453 | 454 | public void write(OutputStream sos) throws IOException { 455 | ImageIO.write(buffImg, "png", sos); 456 | sos.close(); 457 | } 458 | 459 | public BufferedImage getBuffImg() { 460 | return buffImg; 461 | } 462 | 463 | public String getCode() { 464 | return code.toLowerCase(); 465 | } 466 | 467 | //使用方法 468 | /*public void getCode3(HttpServletRequest req, HttpServletResponse response,HttpSession session) throws IOException{ 469 | // 设置响应的类型格式为图片格式 470 | response.setContentType("image/jpeg"); 471 | //禁止图像缓存。 472 | response.setHeader("Pragma", "no-cache"); 473 | response.setHeader("Cache-Control", "no-cache"); 474 | response.setDateHeader("Expires", 0); 475 | 476 | 477 | CreateImageCode vCode = new CreateImageCode(100,30,5,10); 478 | session.setAttribute("code", vCode.getCode()); 479 | vCode.write(response.getOutputStream()); 480 | }*/ 481 | 482 | } 483 | ``` 484 | 485 | 486 | 后台控制器:需要对生成的验证码图片进行 Base64 编码后传到前端页面,前端再解析展示图片。 487 | ```java 488 | @RestController 489 | @RequestMapping("/user") 490 | @CrossOrigin // 允许跨域(前后端分离) 491 | @Slf4j // 日志对象 492 | public class UserController { 493 | /** 494 | * 生成验证码 495 | * @throws IOException 496 | */ 497 | @GetMapping("/getImage") 498 | public Map getImage(HttpServletRequest request) throws IOException { 499 | Map result = new HashMap<>(); 500 | CreateImageCode createImageCode = new CreateImageCode(); 501 | // 获取验证码 502 | String securityCode = createImageCode.getCode(); 503 | // 验证码存入session 504 | String key = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); 505 | request.getServletContext().setAttribute(key, securityCode); 506 | // 生成图片 507 | BufferedImage image = createImageCode.getBuffImg(); 508 | //进行base64编码 509 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 510 | ImageIO.write(image, "png", bos); 511 | String string = Base64Utils.encodeToString(bos.toByteArray()); 512 | result.put("key", key); 513 | result.put("image", string); 514 | return result; 515 | } 516 | } 517 | ``` 518 | 前端页面: 519 | ```html 520 | 521 | 522 | 526 | ``` 527 | ```js 528 | 550 | ``` 551 | # 前后端分离项目—分页功能 552 | mysql 的 `LIMIT` 分页语句: 553 | * `LIMIT n`: 取前 n 个数据,相当于 `LIMIT 0, n`; 554 | * `LIMIT 2, 4`: 从第 ==3== 行开始检索 4 条数据; 555 | 556 | --- 557 | 558 | **分页查询的SQL语句**:参数1是开始查询的数据行,参数2是查询数据条数。 559 | ```xml 560 | 561 | 566 | ``` 567 | 568 | 后台**业务层**代码: 569 | 传入的参数是当前所在页数,以及页面显示数量,无法直接应用 MySQL 的 `limit` 查询子句中,需要转换一下:`start = (page - 1) * rows` 计算出 `limit` 字句的第一个参数。 570 | ```java 571 | @Override 572 | public List findByPage(Integer page, Integer rows) { 573 | // 传入的是当前页数, 以及页面显示的数量 574 | // 所以要根据这两个参数计算从mysql中查询数据要从第几行开始查几条 575 | int start = (page - 1) * rows; // 计算要查询的数据是从第几条数据开始的 576 | return provinceDAO.findByPage(start, rows); 577 | } 578 | ``` 579 | 580 | 后台**控制器**代码: 581 | ```java 582 | /** 583 | * 分页查询数据 584 | */ 585 | @GetMapping("/findByPage") 586 | public Map findByPage(Integer page, Integer rows) { 587 | page = page==null ? 1 : page; 588 | rows = rows==null ? 4 : rows; 589 | System.out.println(page + " : " + rows); 590 | HashMap map = new HashMap<>(); 591 | 592 | // 分页查询出当前页面显示的数据 593 | List provinces = provinceService.findByPage(page, rows); 594 | 595 | // 查询总数据条数, 用于计算总页数 596 | Integer totals = provinceService.findTotals(); 597 | // 计算总页数 598 | // 如果总数据条数可以整除每一页数据个数, 说明结果正好为总页数 599 | // 如果总数据条数无法整除每一页数据个数, 说明总页数需要结果 + 1 600 | Integer totalPage = totals % rows == 0 ? totals / rows : totals / rows + 1; 601 | 602 | map.put("provinces", provinces); 603 | map.put("totals", totals); 604 | map.put("totalPage", totalPage); 605 | map.put("page", page); 606 | 607 | map.forEach((k, v) -> { 608 | System.out.println(k + ": " + v); 609 | }); 610 | return map; 611 | } 612 | ``` 613 | 614 | 前端页面: 615 | ```html 616 |
617 | 618 | <上一页 619 | 620 | 621 | 622 | 下一页> 623 |
624 | ``` 625 | 超链接的写法可以更优化一下:优化后点击当前所在页数无效(不会发送任何请求)。 626 | ```html 627 |
628 | <上一页 629 | 630 | 631 | 632 | 633 | 下一页> 634 |
635 | ``` 636 | 637 | ```html 638 | 667 | ``` 668 | 669 | 670 | # 前后端分离项目—日期数据类型的处理 671 | 前后端数据交互采用的是 Json 的话,只需要在实体类中的属性加一个注解即可: 672 | ```java 673 | @Data 674 | @AllArgsConstructor 675 | @NoArgsConstructor 676 | @Accessors(chain = true) 677 | @ToString 678 | public class Place { 679 | private String id; 680 | private String name; 681 | private String picpath; 682 | 683 | @JsonFormat(pattern = "yyyy-MM-dd") 684 | private Date hottime; // 前后端分离项目对日期数据类型的处理 685 | 686 | private Double hotticket; 687 | private Double dimticket; 688 | private String placedes; 689 | private String provinceid; 690 | } 691 | ``` 692 | # 前后端分离项目—文件上传 693 | 注:由于我们往数据库中插入的是文件的 Base64 编码,因此需要将 数据库中 `picpath` 字段的大小设置的足够大,可以使用以下几个数据类型: 694 | | 数据类型 | 最大长度 | 近似值 | 695 | |--|--| -- | 696 | |TINYTEXT| 256 bytes | | 697 | | TEXT | 65,535 bytes | ~64kb | 698 | | MEDIUMTEXT | 16,777,215 bytes | ~16MB | 699 | | LONGTEXT | 4,294,967,295 bytes | ~4GB | 700 | 701 | 702 | 703 | 在配置文件 `application.properties` 中配置文件上传的路径: 704 | ```properties 705 | spring.resources.static-locations=file:${upload.dir} 706 | upload.dir=D:/CodePro/IdeaPro/SpringBoot/travels/images 707 | ``` 708 | 在后台控制器中 **注入路径**,并实现文件上传(用 Base64 编码进行处理): 709 | ```java 710 | @RestController 711 | @RequestMapping("/place") 712 | @CrossOrigin 713 | public class PlaceController { 714 | @Autowired 715 | private PlaceService placeService; 716 | @Value("${upload.dir}") // 注入 717 | private String realPath; 718 | /** 719 | * 保存景点信息 720 | * @param pic 721 | * @return 722 | */ 723 | @PostMapping("save") 724 | public Result save(MultipartFile pic, Place place) throws IOException { 725 | Result result = new Result(); 726 | try { 727 | // 文件上传 728 | String extension = FilenameUtils.getExtension(pic.getOriginalFilename()); 729 | String newFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + extension; 730 | // base64编码处理(注意, 这一步必须放在 transferTo 操作前面!) 731 | place.setPicpath(Base64Utils.encodeToString(pic.getBytes())); 732 | // 文件上传 733 | File file = new File(realPath); 734 | pic.transferTo(new File(file,newFileName)); 735 | // 保存place对象 736 | placeService.save(place); 737 | result.setMsg("保存景点信息成功!!!"); 738 | } catch (Exception e) { 739 | e.printStackTrace(); 740 | result.setState(false).setMsg(e.getMessage()); 741 | } 742 | return result; 743 | } 744 | } 745 | ``` 746 | 前端中上传文件:给标签添加属性 `ref="myFile"` 747 | ```html 748 |
印象图片:
749 |
750 |
+
751 | 752 | 753 |
754 | ``` 755 | ```html 756 | 799 | ``` 800 | 前端中展示 base64 格式的文件: 801 | ```html 802 | 803 | ``` 804 | -------------------------------------------------------------------------------- /images/20200626165110.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szluyu99/travels/2e7402f9097e7d01c3f046f8c198bdde7c10b245/images/20200626165110.jpg -------------------------------------------------------------------------------- /images/20200626220115.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szluyu99/travels/2e7402f9097e7d01c3f046f8c198bdde7c10b245/images/20200626220115.jpg -------------------------------------------------------------------------------- /images/20200626220758.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szluyu99/travels/2e7402f9097e7d01c3f046f8c198bdde7c10b245/images/20200626220758.jpg -------------------------------------------------------------------------------- /images/20200626222701.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szluyu99/travels/2e7402f9097e7d01c3f046f8c198bdde7c10b245/images/20200626222701.jpg -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.yusael 7 | travels 8 | 0.0.1-SNAPSHOT 9 | travels 10 | springboot + vue 11 | 12 | 13 | 1.8 14 | 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-parent 20 | 2.2.6.RELEASE 21 | 22 | 23 | 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | 30 | 31 | 32 | org.mybatis.spring.boot 33 | mybatis-spring-boot-starter 34 | 2.1.2 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-devtools 40 | runtime 41 | true 42 | 43 | 44 | 45 | mysql 46 | mysql-connector-java 47 | 5.1.43 48 | runtime 49 | 50 | 51 | 52 | com.alibaba 53 | druid 54 | 1.1.12 55 | 56 | 57 | 58 | org.projectlombok 59 | lombok 60 | true 61 | 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-starter-test 66 | 67 | 68 | 69 | commons-fileupload 70 | commons-fileupload 71 | 1.4 72 | 73 | 74 | 75 | 76 | 77 | 78 | org.springframework.boot 79 | spring-boot-maven-plugin 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/TravelsApplication.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class TravelsApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(TravelsApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/controller/PlaceController.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.controller; 2 | 3 | import com.yusael.travels.entity.Place; 4 | import com.yusael.travels.entity.Result; 5 | import com.yusael.travels.service.PlaceService; 6 | import org.apache.commons.io.FilenameUtils; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.util.Base64Utils; 10 | import org.springframework.web.bind.annotation.*; 11 | import org.springframework.web.multipart.MultipartFile; 12 | 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.text.SimpleDateFormat; 16 | import java.util.Date; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | @RestController 22 | @RequestMapping("/place") 23 | @CrossOrigin 24 | public class PlaceController { 25 | 26 | @Autowired 27 | private PlaceService placeService; 28 | 29 | @Value("${upload.dir}") 30 | private String realPath; 31 | 32 | /** 33 | * 修改景点信息 34 | */ 35 | @PostMapping("/update") 36 | public Result update(MultipartFile pic, Place place) throws IOException { 37 | Result result = new Result(); 38 | System.out.println(place); 39 | System.out.println(pic); 40 | try { 41 | // 对接收文件做 base64 编码 42 | String picpath = Base64Utils.encodeToString(pic.getBytes()); 43 | place.setPicpath(picpath); 44 | // 处理文件上传 45 | String extension = FilenameUtils.getExtension(pic.getOriginalFilename()); 46 | String newFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + extension; 47 | pic.transferTo(new File(realPath, newFileName)); 48 | // 修改景点信息 49 | placeService.update(place); 50 | result.setMsg("修改景点信息成功"); 51 | } catch (Exception e) { 52 | e.printStackTrace(); 53 | result.setState(false).setMsg(e.getMessage()); 54 | } 55 | return result; 56 | } 57 | 58 | /** 59 | * 根据 id 查询景点信息 60 | */ 61 | @GetMapping("/findOne") 62 | public Place findOne(String id) { 63 | return placeService.findOne(id); 64 | } 65 | 66 | /** 67 | * 删除景点信息 68 | */ 69 | @GetMapping("/delete") 70 | public Result delete(String id) { 71 | Result result = new Result(); 72 | try { 73 | placeService.delete(id); 74 | result.setMsg("删除景点信息成功!"); 75 | } catch (Exception e) { 76 | e.printStackTrace(); 77 | result.setState(false).setMsg("删除景点信息失败!"); 78 | } 79 | return result; 80 | } 81 | 82 | /** 83 | * 保存景点信息 84 | * 85 | * @param pic 86 | * @return 87 | */ 88 | @PostMapping("/save") 89 | public Result save(MultipartFile pic, Place place) throws IOException { 90 | Result result = new Result(); 91 | try { 92 | // 文件上传 93 | String extension = FilenameUtils.getExtension(pic.getOriginalFilename()); 94 | System.out.println(extension); 95 | String newFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "." + extension; 96 | System.out.println(newFileName); 97 | // base64编码处理(注意, 这一步必须放在 transferTo 操作前面!) 98 | place.setPicpath(Base64Utils.encodeToString(pic.getBytes())); 99 | // 文件上传 100 | File file = new File(realPath); 101 | pic.transferTo(new File(file, newFileName)); 102 | // 保存place对象 103 | placeService.save(place); 104 | result.setMsg("保存景点信息成功!!!"); 105 | } catch (Exception e) { 106 | e.printStackTrace(); 107 | result.setState(false).setMsg(e.getMessage()); 108 | } 109 | return result; 110 | } 111 | 112 | /** 113 | * 根据省份id查询景点的方法 114 | */ 115 | @GetMapping("/findAllPlace") 116 | public Map findAllPlace(Integer page, Integer rows, String provinceId) { 117 | HashMap map = new HashMap<>(); 118 | page = page == null ? 1 : page; 119 | rows = rows == null ? 4 : rows; 120 | // 景点集合 121 | List places = placeService.findByProvinceIdPage(page, rows, provinceId); 122 | 123 | // 处理分页 124 | Integer counts = placeService.findByProvinceIdCounts(provinceId); // 总页数 125 | Integer totalPage = counts % rows == 0 ? counts / rows : counts / rows + 1; 126 | 127 | map.put("places", places); 128 | map.put("page", page); 129 | map.put("counts", counts); 130 | map.put("totalPage", totalPage); 131 | 132 | return map; 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/controller/ProvinceController.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.controller; 2 | 3 | import com.yusael.travels.entity.Province; 4 | import com.yusael.travels.entity.Result; 5 | import com.yusael.travels.service.ProvinceService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | @RestController 14 | @CrossOrigin 15 | @RequestMapping("/province") 16 | public class ProvinceController { 17 | 18 | @Autowired 19 | private ProvinceService provinceService; 20 | 21 | /** 22 | * 修改省份信息 23 | */ 24 | @PostMapping("/update") 25 | public Result update(@RequestBody Province province) { 26 | Result result = new Result(); 27 | try { 28 | provinceService.update(province); 29 | result.setMsg("修改省份信息成功!"); 30 | } catch (Exception e) { 31 | e.printStackTrace(); 32 | result.setState(false).setMsg("修改省份信息失败!"); 33 | } 34 | return result; 35 | } 36 | 37 | /** 38 | * 查询一个省份信息 39 | */ 40 | @GetMapping("/findOne") 41 | public Province findOne(String id) { 42 | return provinceService.findOne(id); 43 | } 44 | 45 | /** 46 | * 删除省份 47 | */ 48 | @GetMapping("/delete") 49 | public Result delete(String id) { 50 | Result result = new Result(); 51 | try { 52 | provinceService.delete(id); 53 | result.setMsg("删除省份信息成功!"); 54 | } catch (Exception e) { 55 | e.printStackTrace(); 56 | result.setState(false).setMsg("删除省份信息失败!"); 57 | } 58 | return result; 59 | } 60 | 61 | /** 62 | * 添加省份 63 | */ 64 | @PostMapping("/save") 65 | public Result save(@RequestBody Province province) { 66 | Result result = new Result(); 67 | try { 68 | provinceService.save(province); 69 | result.setMsg("保存省份信息成功!"); 70 | } catch (Exception e) { 71 | e.printStackTrace(); 72 | result.setState(false).setMsg("保存省份信息失败!"); 73 | } 74 | return result; 75 | } 76 | 77 | /** 78 | * 分页查询数据 79 | */ 80 | @GetMapping("/findByPage") 81 | public Map findByPage(Integer page, Integer rows) { 82 | page = page==null ? 1 : page; 83 | rows = rows==null ? 4 : rows; 84 | System.out.println(page + " : " + rows); 85 | HashMap map = new HashMap<>(); 86 | 87 | // 分页查询出当前页面显示的数据 88 | List provinces = provinceService.findByPage(page, rows); 89 | 90 | // 查询总数据条数, 用于计算总页数 91 | Integer totals = provinceService.findTotals(); 92 | // 计算总页数 93 | // 如果总数据条数可以整除每一页数据个数, 说明结果正好为总页数 94 | // 如果总数据条数无法整除每一页数据个数, 说明总页数需要结果 + 1 95 | Integer totalPage = totals % rows == 0 ? totals / rows : totals / rows + 1; 96 | 97 | map.put("provinces", provinces); 98 | map.put("totals", totals); 99 | map.put("totalPage", totalPage); 100 | map.put("page", page); 101 | 102 | map.forEach((k, v) -> { 103 | System.out.println(k + ": " + v); 104 | }); 105 | return map; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.controller; 2 | 3 | import com.yusael.travels.entity.Result; 4 | import com.yusael.travels.entity.User; 5 | import com.yusael.travels.service.UserService; 6 | import com.yusael.travels.utils.CreateImageCode; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.util.Base64Utils; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import javax.imageio.ImageIO; 13 | import javax.servlet.http.HttpServletRequest; 14 | import java.awt.image.BufferedImage; 15 | import java.io.ByteArrayOutputStream; 16 | import java.io.IOException; 17 | import java.text.SimpleDateFormat; 18 | import java.util.Date; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | @RestController 23 | @RequestMapping("/user") 24 | @CrossOrigin // 允许跨域(前后端分离) 25 | @Slf4j // 日志对象 26 | public class UserController { 27 | 28 | @Autowired 29 | private UserService userService; 30 | 31 | /** 32 | * 用户登录 33 | */ 34 | @PostMapping("/login") 35 | public Result login(String code, String key, @RequestBody User user, HttpServletRequest request) { 36 | log.info("接收的验证码:" + code); 37 | log.info("接收的user对象:" + user); 38 | Result result = new Result(); 39 | String keyCode = (String) request.getServletContext().getAttribute(key); // 获取验证码 40 | try { 41 | if (code.equalsIgnoreCase(keyCode)) { // 验证码验证成功 42 | User userDB = userService.login(user); 43 | // 登录成功之后保存用户的标记 ServletContext application Redis userid userdb 44 | request.getServletContext().setAttribute(userDB.getId(), userDB); 45 | result.setMsg("登录成功!!!").setUserId(userDB.getId()); 46 | } else { 47 | throw new RuntimeException("验证码错误!"); 48 | } 49 | } catch (Exception e) { 50 | result.setState(false).setMsg(e.getMessage()); 51 | } 52 | return result; 53 | } 54 | 55 | /** 56 | * 用户注册 57 | * @param code 58 | * @param user 59 | * @return 60 | */ 61 | @PostMapping("/register") 62 | public Result register(String code, String key, @RequestBody User user, HttpServletRequest request) { // axios发送的是JSON数据, 需要加@RequestBody来接收 63 | log.info("接收的验证码:" + code); 64 | log.info("接收的user对象:" + user); 65 | Result result = new Result(); 66 | String keyCode = (String) request.getServletContext().getAttribute(key); // 获取验证码 67 | try { 68 | if (code.equalsIgnoreCase(keyCode)) { 69 | // 验证码正确, 注册用户 70 | userService.register(user); 71 | result.setMsg("注册成功!!!"); 72 | } else { 73 | throw new RuntimeException("验证码错误!!!"); 74 | } 75 | } catch (Exception e) { 76 | e.printStackTrace(); 77 | result.setMsg(e.getMessage()).setState(false); 78 | } 79 | return result; 80 | } 81 | 82 | /** 83 | * 生成验证码 84 | * @throws IOException 85 | */ 86 | @GetMapping("/getImage") 87 | public Map getImage(HttpServletRequest request) throws IOException { 88 | Map result = new HashMap<>(); 89 | CreateImageCode createImageCode = new CreateImageCode(); 90 | // 获取验证码 91 | String securityCode = createImageCode.getCode(); 92 | // 验证码存入session 93 | String key = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); 94 | request.getServletContext().setAttribute(key, securityCode); 95 | // 生成图片 96 | BufferedImage image = createImageCode.getBuffImg(); 97 | //进行base64编码 98 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 99 | ImageIO.write(image, "png", bos); 100 | String string = Base64Utils.encodeToString(bos.toByteArray()); 101 | result.put("key", key); 102 | result.put("image", string); 103 | return result; 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/dao/BaseDAO.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.dao; 2 | 3 | import org.apache.ibatis.annotations.Param; 4 | import java.util.List; 5 | 6 | public interface BaseDAO { 7 | 8 | void save(T t); 9 | 10 | void update(T t); 11 | 12 | void delete(K k); 13 | 14 | T findOne(K k); 15 | 16 | List findAll(); 17 | 18 | List findByPage(@Param("start") Integer start, @Param("rows") Integer rows); 19 | 20 | Integer findTotals(); 21 | 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/dao/PlaceDAO.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.dao; 2 | 3 | import com.yusael.travels.entity.Place; 4 | import org.apache.ibatis.annotations.Mapper; 5 | import org.apache.ibatis.annotations.Param; 6 | 7 | import java.util.List; 8 | 9 | @Mapper 10 | public interface PlaceDAO extends BaseDAO { 11 | // 根据省份Id 进行分页查询 12 | List findByProvinceIdPage(@Param("start") Integer start, @Param("rows") Integer rows, @Param("provinceId") String provinceId); 13 | // 查询总记录条数 14 | Integer findByProvinceIdCounts(String provinceId); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/dao/ProvinceDAO.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.dao; 2 | 3 | import com.yusael.travels.entity.Province; 4 | import org.apache.ibatis.annotations.Mapper; 5 | 6 | 7 | @Mapper 8 | public interface ProvinceDAO extends BaseDAO { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/dao/UserDAO.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.dao; 2 | 3 | import com.yusael.travels.entity.User; 4 | import org.apache.ibatis.annotations.Mapper; 5 | 6 | @Mapper 7 | public interface UserDAO { 8 | // 注册用户 9 | void save(User user); 10 | // 根据用户名查询用户 11 | User findByUsername(String username); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/entity/Place.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import lombok.ToString; 8 | import lombok.experimental.Accessors; 9 | 10 | import java.util.Date; 11 | 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | @Accessors(chain = true) 16 | @ToString 17 | public class Place { 18 | private String id; 19 | private String name; 20 | private String picpath; 21 | @JsonFormat(pattern = "yyyy/MM/dd") 22 | private Date hottime; 23 | private Double hotticket; 24 | private Double dimticket; 25 | private String placedes; 26 | private String provinceid; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/entity/Province.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import lombok.ToString; 7 | import lombok.experimental.Accessors; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | @ToString 13 | @Accessors(chain=true) 14 | public class Province { 15 | private String id; 16 | private String name; 17 | private String tags; 18 | private Integer placecounts; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/entity/Result.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import lombok.experimental.Accessors; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | @Accessors(chain = true) // 链式调用 12 | public class Result { 13 | private Boolean state = true; 14 | private String msg; 15 | private String userId; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/entity/User.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import lombok.ToString; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | @ToString 12 | public class User { 13 | private String id; 14 | private String username; 15 | private String password; 16 | private String email; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/service/PlaceService.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.service; 2 | 3 | import com.yusael.travels.entity.Place; 4 | 5 | import java.util.List; 6 | 7 | public interface PlaceService { 8 | 9 | List findByProvinceIdPage(Integer page, Integer rows, String provinceId); 10 | 11 | Integer findByProvinceIdCounts(String provinceId); 12 | 13 | void save(Place place); 14 | 15 | void delete(String id); 16 | 17 | Place findOne(String id); 18 | 19 | void update(Place place); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/service/PlaceServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.service; 2 | 3 | import com.yusael.travels.dao.PlaceDAO; 4 | import com.yusael.travels.dao.ProvinceDAO; 5 | import com.yusael.travels.entity.Place; 6 | import com.yusael.travels.entity.Province; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Transactional; 10 | 11 | import java.util.List; 12 | 13 | @Service 14 | @Transactional 15 | public class PlaceServiceImpl implements PlaceService { 16 | 17 | @Autowired 18 | private PlaceDAO placeDAO; 19 | @Autowired 20 | private ProvinceDAO provinceDAO; 21 | 22 | @Override 23 | public List findByProvinceIdPage(Integer page, Integer rows, String provinceId) { 24 | Integer start = (page - 1) * rows; 25 | return placeDAO.findByProvinceIdPage(start, rows, provinceId); 26 | } 27 | 28 | @Override 29 | public Integer findByProvinceIdCounts(String provinceId) { 30 | return placeDAO.findByProvinceIdCounts(provinceId); 31 | } 32 | 33 | @Override 34 | public void save(Place place) { 35 | // 保存景点信息后, 要更新对应省份的景点个数 +1 36 | placeDAO.save(place); 37 | // 查询原始省份信息 38 | Province province = provinceDAO.findOne(place.getProvinceid()); 39 | // 更新省份信息的景点个数 40 | province.setPlacecounts(province.getPlacecounts() + 1); 41 | provinceDAO.update(province); 42 | } 43 | 44 | @Override 45 | public void delete(String id) { 46 | // 不能直接删除景点, 要先让省份中的景点个数 -1, 再删除景点 47 | Place place = placeDAO.findOne(id); 48 | Province province = provinceDAO.findOne(place.getProvinceid()); 49 | province.setPlacecounts(province.getPlacecounts() - 1); // 让省份的景点个数 -1 50 | provinceDAO.update(province); 51 | // 删除景点信息 52 | placeDAO.delete(id); 53 | } 54 | 55 | @Override 56 | public Place findOne(String id) { 57 | return placeDAO.findOne(id); 58 | } 59 | 60 | @Override 61 | public void update(Place place) { 62 | placeDAO.update(place); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/service/ProvinceService.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.service; 2 | 3 | import com.yusael.travels.entity.Province; 4 | 5 | import java.util.List; 6 | 7 | public interface ProvinceService { 8 | //page:当前页 //rows:每页显示记录数 9 | List findByPage(Integer page, Integer rows); 10 | 11 | //查询总跳数 12 | Integer findTotals(); 13 | 14 | //保存省份方法 15 | void save(Province province); 16 | 17 | //删除省份的方法 18 | void delete(String id); 19 | 20 | //查询省份信息 21 | Province findOne(String id); 22 | 23 | //修改省份信息 24 | void update(Province province); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/service/ProvinceServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.service; 2 | 3 | import com.yusael.travels.dao.ProvinceDAO; 4 | import com.yusael.travels.entity.Province; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.transaction.annotation.Transactional; 8 | 9 | import java.util.List; 10 | 11 | @Service 12 | @Transactional 13 | public class ProvinceServiceImpl implements ProvinceService { 14 | 15 | @Autowired 16 | private ProvinceDAO provinceDAO; 17 | 18 | @Override 19 | public List findByPage(Integer page, Integer rows) { 20 | // 传入的是当前页数, 以及页面显示的数量 21 | // 所以要根据这两个参数计算从mysql中查询数据要从第几行开始查几条 22 | int start = (page - 1) * rows; // 计算要查询的数据是从第几条数据开始的 23 | return provinceDAO.findByPage(start, rows); 24 | } 25 | 26 | @Override 27 | public Province findOne(String id) { 28 | return provinceDAO.findOne(id); 29 | } 30 | 31 | @Override 32 | public void update(Province province) { 33 | provinceDAO.update(province); 34 | } 35 | 36 | @Override 37 | public void delete(String id) { 38 | provinceDAO.delete(id); 39 | } 40 | 41 | @Override 42 | public void save(Province province) { 43 | province.setPlacecounts(0); //景点个数为零 44 | provinceDAO.save(province); 45 | } 46 | 47 | @Override 48 | public Integer findTotals() { 49 | return provinceDAO.findTotals(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.service; 2 | 3 | import com.yusael.travels.entity.User; 4 | 5 | public interface UserService { 6 | void register(User user); 7 | User login(User user); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/service/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.service; 2 | 3 | import com.yusael.travels.dao.UserDAO; 4 | import com.yusael.travels.entity.User; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.transaction.annotation.Transactional; 8 | 9 | @Service 10 | @Transactional 11 | public class UserServiceImpl implements UserService{ 12 | 13 | @Autowired 14 | private UserDAO userDAO; 15 | 16 | @Override 17 | public void register(User user) { 18 | if (userDAO.findByUsername(user.getUsername()) == null) { 19 | userDAO.save(user); 20 | } else { 21 | throw new RuntimeException("用户名已存在!"); 22 | } 23 | } 24 | 25 | @Override 26 | public User login(User user) { 27 | User userDB = userDAO.findByUsername(user.getUsername()); 28 | if (userDB != null) { 29 | if (userDB.getPassword().equals(user.getPassword())) { 30 | return userDB; 31 | } 32 | throw new RuntimeException("密码输入错误!"); 33 | } else { 34 | throw new RuntimeException("用户名输入错误!"); 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/yusael/travels/utils/CreateImageCode.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.utils; 2 | 3 | 4 | import java.awt.Color; 5 | import java.awt.Font; 6 | import java.awt.Graphics; 7 | import java.awt.image.BufferedImage; 8 | import java.io.IOException; 9 | import java.io.OutputStream; 10 | import java.util.Random; 11 | 12 | import javax.imageio.ImageIO; 13 | 14 | 15 | public class CreateImageCode { 16 | // 图片的宽度。 17 | private int width = 160; 18 | // 图片的高度。 19 | private int height = 40; 20 | // 验证码字符个数 21 | private int codeCount = 4; 22 | // 验证码干扰线数 23 | private int lineCount = 20; 24 | // 验证码 25 | private String code = null; 26 | // 验证码图片Buffer 27 | private BufferedImage buffImg = null; 28 | Random random = new Random(); 29 | 30 | public CreateImageCode() { 31 | creatImage(); 32 | } 33 | 34 | public CreateImageCode(int width, int height) { 35 | this.width = width; 36 | this.height = height; 37 | creatImage(); 38 | } 39 | 40 | public CreateImageCode(int width, int height, int codeCount) { 41 | this.width = width; 42 | this.height = height; 43 | this.codeCount = codeCount; 44 | creatImage(); 45 | } 46 | 47 | public CreateImageCode(int width, int height, int codeCount, int lineCount) { 48 | this.width = width; 49 | this.height = height; 50 | this.codeCount = codeCount; 51 | this.lineCount = lineCount; 52 | creatImage(); 53 | } 54 | 55 | // 生成图片 56 | private void creatImage() { 57 | int fontWidth = width / codeCount;// 字体的宽度 58 | int fontHeight = height - 5;// 字体的高度 59 | int codeY = height - 8; 60 | 61 | // 图像buffer 62 | buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 63 | Graphics g = buffImg.getGraphics(); 64 | //Graphics2D g = buffImg.createGraphics(); 65 | // 设置背景色 66 | g.setColor(getRandColor(200, 250)); 67 | g.fillRect(0, 0, width, height); 68 | 69 | 70 | 71 | // 设置字体 72 | //Font font1 = getFont(fontHeight); 73 | Font font = new Font("Fixedsys", Font.BOLD, fontHeight); 74 | g.setFont(font); 75 | 76 | // 设置干扰线 77 | for (int i = 0; i < lineCount; i++) { 78 | int xs = random.nextInt(width); 79 | int ys = random.nextInt(height); 80 | int xe = xs + random.nextInt(width); 81 | int ye = ys + random.nextInt(height); 82 | g.setColor(getRandColor(1, 255)); 83 | g.drawLine(xs, ys, xe, ye); 84 | } 85 | 86 | // 添加噪点 87 | float yawpRate = 0.01f;// 噪声率 88 | int area = (int) (yawpRate * width * height); 89 | for (int i = 0; i < area; i++) { 90 | int x = random.nextInt(width); 91 | int y = random.nextInt(height); 92 | 93 | buffImg.setRGB(x, y, random.nextInt(255)); 94 | } 95 | 96 | 97 | String str1 = randomStr(codeCount);// 得到随机字符 98 | this.code = str1; 99 | for (int i = 0; i < codeCount; i++) { 100 | String strRand = str1.substring(i, i + 1); 101 | g.setColor(getRandColor(1, 255)); 102 | // g.drawString(a,x,y); 103 | // a为要画出来的东西,x和y表示要画的东西最左侧字符的基线位于此图形上下文坐标系的 (x, y) 位置处 104 | 105 | g.drawString(strRand, i*fontWidth+3, codeY); 106 | } 107 | 108 | 109 | } 110 | 111 | // 得到随机字符 112 | private String randomStr(int n) { 113 | String str1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"; 114 | String str2 = ""; 115 | int len = str1.length() - 1; 116 | double r; 117 | for (int i = 0; i < n; i++) { 118 | r = (Math.random()) * len; 119 | str2 = str2 + str1.charAt((int) r); 120 | } 121 | return str2; 122 | } 123 | 124 | // 得到随机颜色 125 | private Color getRandColor(int fc, int bc) {// 给定范围获得随机颜色 126 | if (fc > 255) 127 | fc = 255; 128 | if (bc > 255) 129 | bc = 255; 130 | int r = fc + random.nextInt(bc - fc); 131 | int g = fc + random.nextInt(bc - fc); 132 | int b = fc + random.nextInt(bc - fc); 133 | return new Color(r, g, b); 134 | } 135 | 136 | /** 137 | * 产生随机字体 138 | */ 139 | private Font getFont(int size) { 140 | Random random = new Random(); 141 | Font font[] = new Font[5]; 142 | font[0] = new Font("Ravie", Font.PLAIN, size); 143 | font[1] = new Font("Antique Olive Compact", Font.PLAIN, size); 144 | font[2] = new Font("Fixedsys", Font.PLAIN, size); 145 | font[3] = new Font("Wide Latin", Font.PLAIN, size); 146 | font[4] = new Font("Gill Sans Ultra Bold", Font.PLAIN, size); 147 | return font[random.nextInt(5)]; 148 | } 149 | 150 | // 扭曲方法 151 | private void shear(Graphics g, int w1, int h1, Color color) { 152 | shearX(g, w1, h1, color); 153 | shearY(g, w1, h1, color); 154 | } 155 | 156 | private void shearX(Graphics g, int w1, int h1, Color color) { 157 | 158 | int period = random.nextInt(2); 159 | 160 | boolean borderGap = true; 161 | int frames = 1; 162 | int phase = random.nextInt(2); 163 | 164 | for (int i = 0; i < h1; i++) { 165 | double d = (double) (period >> 1) 166 | * Math.sin((double) i / (double) period 167 | + (6.2831853071795862D * (double) phase) 168 | / (double) frames); 169 | g.copyArea(0, i, w1, 1, (int) d, 0); 170 | if (borderGap) { 171 | g.setColor(color); 172 | g.drawLine((int) d, i, 0, i); 173 | g.drawLine((int) d + w1, i, w1, i); 174 | } 175 | } 176 | 177 | } 178 | 179 | private void shearY(Graphics g, int w1, int h1, Color color) { 180 | 181 | int period = random.nextInt(40) + 10; // 50; 182 | 183 | boolean borderGap = true; 184 | int frames = 20; 185 | int phase = 7; 186 | for (int i = 0; i < w1; i++) { 187 | double d = (double) (period >> 1) 188 | * Math.sin((double) i / (double) period 189 | + (6.2831853071795862D * (double) phase) 190 | / (double) frames); 191 | g.copyArea(i, 0, 1, h1, 0, (int) d); 192 | if (borderGap) { 193 | g.setColor(color); 194 | g.drawLine(i, (int) d, i, 0); 195 | g.drawLine(i, (int) d + h1, i, h1); 196 | } 197 | 198 | } 199 | 200 | } 201 | 202 | public void write(OutputStream sos) throws IOException { 203 | ImageIO.write(buffImg, "png", sos); 204 | sos.close(); 205 | } 206 | 207 | public BufferedImage getBuffImg() { 208 | return buffImg; 209 | } 210 | 211 | public String getCode() { 212 | return code.toLowerCase(); 213 | } 214 | 215 | //使用方法 216 | /*public void getCode3(HttpServletRequest req, HttpServletResponse response,HttpSession session) throws IOException{ 217 | // 设置响应的类型格式为图片格式 218 | response.setContentType("image/jpeg"); 219 | //禁止图像缓存。 220 | response.setHeader("Pragma", "no-cache"); 221 | response.setHeader("Cache-Control", "no-cache"); 222 | response.setDateHeader("Expires", 0); 223 | 224 | 225 | CreateImageCode vCode = new CreateImageCode(100,30,5,10); 226 | session.setAttribute("code", vCode.getCode()); 227 | vCode.write(response.getOutputStream()); 228 | }*/ 229 | 230 | } -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8989 2 | spring.application.name=travels 3 | 4 | spring.datasource.type=com.alibaba.druid.pool.DruidDataSource 5 | spring.datasource.driver-class-name=com.mysql.jdbc.Driver 6 | spring.datasource.url=jdbc:mysql://localhost:3306/travels?characterEncoding=UTF-8 7 | spring.datasource.username=root 8 | spring.datasource.password=1234 9 | 10 | mybatis.mapper-locations=classpath:com/yusael/travels/mapper/*.xml 11 | mybatis.type-aliases-package=com.yusael.travels.entity 12 | 13 | logging.level.root=info 14 | logging.level.com.yusael.travels.dao=debug 15 | 16 | # \u4E0A\u4F20\u7684\u56FE\u7247\u5B58\u653E\u7684\u8DEF\u5F84 17 | upload.dir=D:/CodePro/IdeaPro/SpringBoot/travels/images 18 | spring.resources.static-locations=file:${upload.dir} -------------------------------------------------------------------------------- /src/main/resources/com/yusael/travels/mapper/PlaceMapperDAO.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | INSERT INTO t_place 21 | VALUES (#{id}, #{name}, #{picpath}, #{hottime}, #{hotticket}, #{dimticket}, #{placedes}, #{provinceid}) 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | DELETE FROM t_place WHERE id = #{id} 34 | 35 | 36 | 37 | 38 | UPDATE t_place SET 39 | name = #{name}, 40 | picpath = #{picpath}, 41 | hottime = #{hottime}, 42 | hotticket = #{hotticket}, 43 | dimticket = #{dimticket}, 44 | placedes= #{placedes}, 45 | provinceid = #{provinceid} 46 | WHERE id = #{id} 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/main/resources/com/yusael/travels/mapper/ProvinceMapperDAOMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | INSERT INTO t_province VALUES (#{id}, #{name}, #{tags}, #{placecounts}) 21 | 22 | 23 | 24 | 25 | DELETE FROM t_province WHERE id = #{id} 26 | 27 | 28 | 29 | 34 | 35 | 36 | 37 | UPDATE t_province 38 | SET name = #{name}, tags = #{tags}, placecounts = #{placecounts} 39 | WHERE id = #{id} 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/main/resources/com/yusael/travels/mapper/UserMapperDAOMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | /*id自动生成, 生成之后放到id属性*/ 6 | INSERT INTO t_user 7 | VALUES (#{id}, #{username}, #{password}, #{email}) 8 | 9 | 10 | 11 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/resources/static/css/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | box-sizing: border-box; 3 | } 4 | body{ 5 | background: #eee; 6 | padding-top: 10px; 7 | } 8 | #wrap{ 9 | width: 1160px; 10 | margin: 0 auto; 11 | box-shadow: 5px 5px 6px 6px #ccc,-5px 5px 6px 6px #ccc; 12 | } 13 | #header,#footer{ 14 | color:#fff; 15 | font-size:12px; 16 | } 17 | #header{ 18 | padding: 2px 16px; 19 | background: #85C329; 20 | background: linear-gradient(#0a4b80,#65bcdd); 21 | } 22 | #footer{ 23 | background: #85C329; 24 | background: linear-gradient(#0a4b80,#65bcdd); 25 | padding: 15px 15px 25px 15px; 26 | } 27 | #header-bar{ 28 | background: linear-gradient(#ddd1ab,#fff); 29 | border-top: 1px solid #ffffff; 30 | height: 25px; 31 | position: relative; 32 | z-index: 9999; 33 | } 34 | #content{ 35 | padding: 8px 20px; 36 | background: #fff; 37 | color: #666666; 38 | } 39 | #content h2{ 40 | color:#74A8F5; 41 | } -------------------------------------------------------------------------------- /src/main/resources/static/img/gug.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szluyu99/travels/2e7402f9097e7d01c3f046f8c198bdde7c10b245/src/main/resources/static/img/gug.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/tianam.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szluyu99/travels/2e7402f9097e7d01c3f046f8c198bdde7c10b245/src/main/resources/static/img/tianam.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szluyu99/travels/2e7402f9097e7d01c3f046f8c198bdde7c10b245/src/main/resources/static/img/timg.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/vcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szluyu99/travels/2e7402f9097e7d01c3f046f8c198bdde7c10b245/src/main/resources/static/img/vcode.png -------------------------------------------------------------------------------- /src/main/resources/static/img/yihy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szluyu99/travels/2e7402f9097e7d01c3f046f8c198bdde7c10b245/src/main/resources/static/img/yihy.jpg -------------------------------------------------------------------------------- /src/main/resources/static/js/axios.min.js: -------------------------------------------------------------------------------- 1 | /* axios v0.19.2 | (c) 2020 by Matt Zabriskie */ 2 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new s(e),n=i(s.prototype.request,t);return o.extend(n,s.prototype,t),o.extend(n,t),n}var o=n(2),i=n(3),s=n(4),a=n(22),u=n(10),c=r(u);c.Axios=s,c.create=function(e){return r(a(c.defaults,e))},c.Cancel=n(23),c.CancelToken=n(24),c.isCancel=n(9),c.all=function(e){return Promise.all(e)},c.spread=n(25),e.exports=c,e.exports.default=c},function(e,t,n){"use strict";function r(e){return"[object Array]"===j.call(e)}function o(e){return"undefined"==typeof e}function i(e){return null!==e&&!o(e)&&null!==e.constructor&&!o(e.constructor)&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}function s(e){return"[object ArrayBuffer]"===j.call(e)}function a(e){return"undefined"!=typeof FormData&&e instanceof FormData}function u(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function c(e){return"string"==typeof e}function f(e){return"number"==typeof e}function p(e){return null!==e&&"object"==typeof e}function d(e){return"[object Date]"===j.call(e)}function l(e){return"[object File]"===j.call(e)}function h(e){return"[object Blob]"===j.call(e)}function m(e){return"[object Function]"===j.call(e)}function y(e){return p(e)&&m(e.pipe)}function g(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function v(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function x(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function w(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},i.forEach(["delete","get","head"],function(e){u.headers[e]={}}),i.forEach(["post","put","patch"],function(e){u.headers[e]=i.merge(a)}),e.exports=u},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){r.forEach(e,function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])})}},function(e,t,n){"use strict";var r=n(2),o=n(13),i=n(5),s=n(16),a=n(19),u=n(20),c=n(14);e.exports=function(e){return new Promise(function(t,f){var p=e.data,d=e.headers;r.isFormData(p)&&delete d["Content-Type"];var l=new XMLHttpRequest;if(e.auth){var h=e.auth.username||"",m=e.auth.password||"";d.Authorization="Basic "+btoa(h+":"+m)}var y=s(e.baseURL,e.url);if(l.open(e.method.toUpperCase(),i(y,e.params,e.paramsSerializer),!0),l.timeout=e.timeout,l.onreadystatechange=function(){if(l&&4===l.readyState&&(0!==l.status||l.responseURL&&0===l.responseURL.indexOf("file:"))){var n="getAllResponseHeaders"in l?a(l.getAllResponseHeaders()):null,r=e.responseType&&"text"!==e.responseType?l.response:l.responseText,i={data:r,status:l.status,statusText:l.statusText,headers:n,config:e,request:l};o(t,f,i),l=null}},l.onabort=function(){l&&(f(c("Request aborted",e,"ECONNABORTED",l)),l=null)},l.onerror=function(){f(c("Network Error",e,null,l)),l=null},l.ontimeout=function(){var t="timeout of "+e.timeout+"ms exceeded";e.timeoutErrorMessage&&(t=e.timeoutErrorMessage),f(c(t,e,"ECONNABORTED",l)),l=null},r.isStandardBrowserEnv()){var g=n(21),v=(e.withCredentials||u(y))&&e.xsrfCookieName?g.read(e.xsrfCookieName):void 0;v&&(d[e.xsrfHeaderName]=v)}if("setRequestHeader"in l&&r.forEach(d,function(e,t){"undefined"==typeof p&&"content-type"===t.toLowerCase()?delete d[t]:l.setRequestHeader(t,e)}),r.isUndefined(e.withCredentials)||(l.withCredentials=!!e.withCredentials),e.responseType)try{l.responseType=e.responseType}catch(t){if("json"!==e.responseType)throw t}"function"==typeof e.onDownloadProgress&&l.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&l.upload&&l.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then(function(e){l&&(l.abort(),f(e),l=null)}),void 0===p&&(p=null),l.send(p)})}},function(e,t,n){"use strict";var r=n(14);e.exports=function(e,t,n){var o=n.config.validateStatus;!o||o(n.status)?e(n):t(r("Request failed with status code "+n.status,n.config,null,n.request,n))}},function(e,t,n){"use strict";var r=n(15);e.exports=function(e,t,n,o,i){var s=new Error(e);return r(s,t,n,o,i)}},function(e,t){"use strict";e.exports=function(e,t,n,r,o){return e.config=t,n&&(e.code=n),e.request=r,e.response=o,e.isAxiosError=!0,e.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code}},e}},function(e,t,n){"use strict";var r=n(17),o=n(18);e.exports=function(e,t){return e&&!r(t)?o(e,t):t}},function(e,t){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,n){"use strict";var r=n(2),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,n,i,s={};return e?(r.forEach(e.split("\n"),function(e){if(i=e.indexOf(":"),t=r.trim(e.substr(0,i)).toLowerCase(),n=r.trim(e.substr(i+1)),t){if(s[t]&&o.indexOf(t)>=0)return;"set-cookie"===t?s[t]=(s[t]?s[t]:[]).concat([n]):s[t]=s[t]?s[t]+", "+n:n}}),s):s}},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){function e(e){var t=e;return n&&(o.setAttribute("href",t),t=o.href),o.setAttribute("href",t),{href:o.href,protocol:o.protocol?o.protocol.replace(/:$/,""):"",host:o.host,search:o.search?o.search.replace(/^\?/,""):"",hash:o.hash?o.hash.replace(/^#/,""):"",hostname:o.hostname,port:o.port,pathname:"/"===o.pathname.charAt(0)?o.pathname:"/"+o.pathname}}var t,n=/(msie|trident)/i.test(navigator.userAgent),o=document.createElement("a");return t=e(window.location.href),function(n){var o=r.isString(n)?e(n):n;return o.protocol===t.protocol&&o.host===t.host}}():function(){return function(){return!0}}()},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){return{write:function(e,t,n,o,i,s){var a=[];a.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&a.push("expires="+new Date(n).toGMTString()),r.isString(o)&&a.push("path="+o),r.isString(i)&&a.push("domain="+i),s===!0&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){t=t||{};var n={},o=["url","method","params","data"],i=["headers","auth","proxy"],s=["baseURL","url","transformRequest","transformResponse","paramsSerializer","timeout","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","maxContentLength","validateStatus","maxRedirects","httpAgent","httpsAgent","cancelToken","socketPath"];r.forEach(o,function(e){"undefined"!=typeof t[e]&&(n[e]=t[e])}),r.forEach(i,function(o){r.isObject(t[o])?n[o]=r.deepMerge(e[o],t[o]):"undefined"!=typeof t[o]?n[o]=t[o]:r.isObject(e[o])?n[o]=r.deepMerge(e[o]):"undefined"!=typeof e[o]&&(n[o]=e[o])}),r.forEach(s,function(r){"undefined"!=typeof t[r]?n[r]=t[r]:"undefined"!=typeof e[r]&&(n[r]=e[r])});var a=o.concat(i).concat(s),u=Object.keys(t).filter(function(e){return a.indexOf(e)===-1});return r.forEach(u,function(r){"undefined"!=typeof t[r]?n[r]=t[r]:"undefined"!=typeof e[r]&&(n[r]=e[r])}),n}},function(e,t){"use strict";function n(e){this.message=e}n.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},n.prototype.__CANCEL__=!0,e.exports=n},function(e,t,n){"use strict";function r(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise(function(e){t=e});var n=this;e(function(e){n.reason||(n.reason=new o(e),t(n.reason))})}var o=n(23);r.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},r.source=function(){var e,t=new r(function(t){e=t});return{token:t,cancel:e}},e.exports=r},function(e,t){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}}])}); 3 | //# sourceMappingURL=axios.min.map -------------------------------------------------------------------------------- /src/main/resources/static/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 登录页面 6 | 7 | 44 | 45 | 46 |
47 |
48 | 52 |
53 |
54 | 55 |

登录

56 |
57 | 61 | 65 | 66 | 70 |   71 | 去注册 72 |
73 |
74 | 77 |
78 |
79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/main/resources/static/province/addprovince.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 添加省份页面 6 | 7 | 35 | 36 | 37 |
38 |
39 | 43 |
44 |
45 | 46 |

添加省份

47 |
48 | 52 | 56 |   57 | 返回 58 |
59 |
60 | 63 |
64 |
65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/main/resources/static/province/provincelist.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 省份列表 6 | 7 | 41 | 42 | 43 |
44 |
45 | 52 |
53 |
54 |

省份列表

55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 76 | 77 | 78 |
ID省份标签景点个数操作
72 | 删除省份 73 | 景点列表 74 | 修改省份 75 |
79 | 80 | 81 | 82 |
83 | 84 | <上一页 85 | 86 | 88 | 89 | 下一页> 90 |
91 |
92 | 95 |
96 |
97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/main/resources/static/province/updateprovince.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 修改省份页面 6 | 7 | 35 | 36 | 37 |
38 |
39 | 43 |
44 |
45 | 46 |

修改省份

47 |
48 | 52 | 56 |   57 | 返回 58 |
59 |
60 | 63 |
64 |
65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/main/resources/static/reg.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 注册页面 6 | 7 | 44 | 45 | 46 |
47 |
48 | 52 |
53 |
54 | 55 |

注册

56 |
57 | 61 | 65 | 69 | 70 | 71 | 75 |   76 | 去登录 77 |
78 |
79 | 82 |
83 |
84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/main/resources/static/viewspot/addviewspot.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 53 | 67 | 68 | 69 |
70 |
71 | 75 |
76 |
77 | 78 |

添加景点

79 |
80 | 84 | 92 | 96 | 100 | 104 | 108 | 114 |   115 | 返回 116 |
117 |
118 | 121 |
122 |
123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /src/main/resources/static/viewspot/updateviewspot.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 修改景点页面 6 | 7 | 43 | 54 | 55 | 56 |
57 |
58 | 62 |
63 |
64 | 65 |

修改景点

66 |
67 | 71 | 78 | 82 | 86 | 90 | 94 | 100 |   101 | 返回 102 |
103 |
104 | 107 |
108 |
109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /src/main/resources/static/viewspot/viewspotlist.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 景点列表页面 6 | 7 | 49 | 50 | 51 |
52 |
53 | 60 |
61 |
62 |

景点列表

63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 88 | 89 | 90 | 95 | 96 | 97 |
ID景点印象图旺季时间旺季门票淡季门票操作
85 | 删除景点
86 | 修改景点 87 |
91 |
92 | 简介: 93 |
94 |
98 | 99 | 100 |   101 | 返回省份列表 102 |
103 | <上一页 104 | 105 | 106 | 108 | 109 | 下一页> 110 |
111 |
112 | 115 |
116 |
117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /src/test/java/com/yusael/travels/test/TestUserService.java: -------------------------------------------------------------------------------- 1 | package com.yusael.travels.test; 2 | 3 | import com.yusael.travels.TravelsApplication; 4 | import com.yusael.travels.entity.User; 5 | import com.yusael.travels.service.UserService; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | 12 | @SpringBootTest(classes = TravelsApplication.class) 13 | @RunWith(SpringRunner.class) 14 | public class TestUserService { 15 | 16 | @Autowired 17 | private UserService userService; 18 | 19 | @Test 20 | public void testSave() { 21 | User user = new User(); 22 | user.setUsername("zhenyu"); 23 | user.setPassword("123"); 24 | user.setEmail("123@qq.com"); 25 | userService.register(user); 26 | } 27 | 28 | } 29 | --------------------------------------------------------------------------------