├── src ├── main │ ├── resources │ │ ├── config │ │ │ └── cron.setting │ │ ├── static │ │ │ ├── favicon.ico │ │ │ ├── static │ │ │ │ ├── img │ │ │ │ │ ├── 500.png │ │ │ │ │ ├── car.png │ │ │ │ │ ├── background.jpg │ │ │ │ │ └── bg.86ca37d.jpg │ │ │ │ ├── fonts │ │ │ │ │ ├── typora-vue.woff2 │ │ │ │ │ ├── element-icons.535877f.woff │ │ │ │ │ └── element-icons.732389d.ttf │ │ │ │ └── js │ │ │ │ │ ├── manifest.3ad1d5771e9b13dbdad2.js │ │ │ │ │ └── manifest.3ad1d5771e9b13dbdad2.js.map │ │ │ ├── live2d-widget │ │ │ │ ├── assets │ │ │ │ │ ├── screenshot-1.png │ │ │ │ │ ├── screenshot-2.png │ │ │ │ │ └── screenshot-3.png │ │ │ │ ├── demo │ │ │ │ │ ├── demo.html │ │ │ │ │ └── login.html │ │ │ │ ├── autoload.js │ │ │ │ ├── waifu.css │ │ │ │ ├── README.md │ │ │ │ └── waifu-tips.json │ │ │ ├── index.js │ │ │ ├── index.html │ │ │ └── error │ │ │ │ ├── 500.html │ │ │ │ └── 404.html │ │ ├── lib │ │ │ └── javax.mail-1.6.2.jar │ │ ├── mapper │ │ │ ├── EchartsMapper.xml │ │ │ ├── CodeMapper.xml │ │ │ ├── AdminMapper.xml │ │ │ ├── UserMapper.xml │ │ │ └── SendMapper.xml │ │ ├── banner.txt │ │ ├── META-INF │ │ │ └── spring-configuration-metadata.json │ │ ├── spy.properties │ │ ├── logback-spring.xml │ │ ├── logback-old.xml │ │ └── application.yml │ └── java │ │ └── per │ │ └── kirito │ │ └── pack │ │ ├── util │ │ ├── Constant.java │ │ ├── cron │ │ │ └── CronUtil.java │ │ ├── IpAddressUtil.java │ │ ├── PickCodeUtil.java │ │ ├── SendUtil.java │ │ ├── PackIdUtil.java │ │ ├── SendMailUtil.java │ │ ├── PackUtil.java │ │ ├── TypeConversion.java │ │ └── CheckCodeUtil.java │ │ ├── mapper │ │ ├── EchartsMapper.java │ │ ├── CodeMapper.java │ │ ├── AdminMapper.java │ │ ├── UserMapper.java │ │ ├── SendMapper.java │ │ └── PackMapper.java │ │ ├── service │ │ ├── inter │ │ │ ├── CheckCodeService.java │ │ │ ├── EchartsService.java │ │ │ ├── MailService.java │ │ │ ├── AccountService.java │ │ │ ├── SendService.java │ │ │ └── PackService.java │ │ └── impl │ │ │ ├── CheckCodeServiceImpl.java │ │ │ ├── EchartsServiceImpl.java │ │ │ ├── MailServiceImpl.java │ │ │ └── AdminServiceImpl.java │ │ ├── annotation │ │ └── Comment.java │ │ ├── pojo │ │ ├── utilpojo │ │ │ ├── EchartsDO.java │ │ │ ├── EchartsVO.java │ │ │ ├── Page.java │ │ │ └── SendRequest.java │ │ ├── Code.java │ │ ├── Admin.java │ │ ├── Send.java │ │ ├── Pack.java │ │ ├── Echarts.java │ │ └── User.java │ │ ├── ServletInitializer.java │ │ ├── myenum │ │ ├── Express.java │ │ ├── PackStatusEnum.java │ │ └── Status.java │ │ ├── properties │ │ └── MailProperties.java │ │ ├── config │ │ ├── MybatisPlusConfig.java │ │ └── SwaggerConfiguration.java │ │ ├── controller │ │ ├── ApiController.java │ │ ├── CheckCodeController.java │ │ ├── EchartsController.java │ │ ├── MailController.java │ │ ├── ExcelController.java │ │ ├── AdminController.java │ │ └── UserController.java │ │ └── PackApplication.java └── test │ └── java │ ├── CheckCodeUtilTest.java │ ├── JasyptTest.java │ ├── PackServiceTest.java │ └── PackIdUtilTest.java ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ ├── maven-wrapper.properties │ └── MavenWrapperDownloader.java ├── .gitignore ├── README.md ├── mvnw.cmd └── pom.xml /src/main/resources/config/cron.setting: -------------------------------------------------------------------------------- 1 | [per.kirito.pack.util.cron] 2 | CronUtil.printInfo = 0 0 12 * * ? -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirito66465/pack-java/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirito66465/pack-java/HEAD/src/main/resources/static/favicon.ico -------------------------------------------------------------------------------- /src/main/resources/lib/javax.mail-1.6.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirito66465/pack-java/HEAD/src/main/resources/lib/javax.mail-1.6.2.jar -------------------------------------------------------------------------------- /src/main/resources/static/static/img/500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirito66465/pack-java/HEAD/src/main/resources/static/static/img/500.png -------------------------------------------------------------------------------- /src/main/resources/static/static/img/car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirito66465/pack-java/HEAD/src/main/resources/static/static/img/car.png -------------------------------------------------------------------------------- /src/main/resources/static/static/img/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirito66465/pack-java/HEAD/src/main/resources/static/static/img/background.jpg -------------------------------------------------------------------------------- /src/main/resources/static/static/img/bg.86ca37d.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirito66465/pack-java/HEAD/src/main/resources/static/static/img/bg.86ca37d.jpg -------------------------------------------------------------------------------- /src/main/resources/static/static/fonts/typora-vue.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirito66465/pack-java/HEAD/src/main/resources/static/static/fonts/typora-vue.woff2 -------------------------------------------------------------------------------- /src/main/resources/static/live2d-widget/assets/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirito66465/pack-java/HEAD/src/main/resources/static/live2d-widget/assets/screenshot-1.png -------------------------------------------------------------------------------- /src/main/resources/static/live2d-widget/assets/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirito66465/pack-java/HEAD/src/main/resources/static/live2d-widget/assets/screenshot-2.png -------------------------------------------------------------------------------- /src/main/resources/static/live2d-widget/assets/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirito66465/pack-java/HEAD/src/main/resources/static/live2d-widget/assets/screenshot-3.png -------------------------------------------------------------------------------- /src/main/resources/static/static/fonts/element-icons.535877f.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirito66465/pack-java/HEAD/src/main/resources/static/static/fonts/element-icons.535877f.woff -------------------------------------------------------------------------------- /src/main/resources/static/static/fonts/element-icons.732389d.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirito66465/pack-java/HEAD/src/main/resources/static/static/fonts/element-icons.732389d.ttf -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/util/Constant.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.util; 2 | 3 | /** 4 | * @author kirito 5 | * @date 2021/2/24 6 | * @time 17:10 7 | * 常量类 8 | */ 9 | public class Constant { 10 | 11 | /** 12 | * 登录有效时间,单位分钟 13 | */ 14 | public static Long LOGIN_VALID_MINUTE = 30L; 15 | 16 | /** 17 | * 验证码有效时间,单位分钟 18 | */ 19 | public static Long VERIFY_VALID_MINUTE = 2L; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/mapper/EchartsMapper.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import per.kirito.pack.pojo.Echarts; 5 | 6 | import java.util.Map; 7 | 8 | /** 9 | * @author kirito 10 | * @date 2021/3/28 11 | * @time 17:12 12 | * Echarts 的 Mapper 层接口 13 | */ 14 | public interface EchartsMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/resources/mapper/EchartsMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | id, datee, card, nine, ten, eleven, twelve, thirteen, fourteen, fifteen, sixteen, seventeen, eighteen, nineteen 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/service/inter/CheckCodeService.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.service.inter; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * @author kirito 7 | * @date 2021/1/8 8 | * @time 16:27 9 | * 验证码的 Service 层接口 10 | */ 11 | public interface CheckCodeService { 12 | 13 | /** 14 | * 获取验证码字符串和图片 15 | * 16 | * @param token 令牌 17 | * @return java.util.Map 18 | */ 19 | Map getCheckCode(String token); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/util/cron/CronUtil.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.util.cron; 2 | 3 | import cn.hutool.system.SystemUtil; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | /** 7 | * @author kirito 8 | * @date 2021/3/3 9 | * @time 21:36 10 | * 定时任务 11 | */ 12 | @Slf4j 13 | public class CronUtil { 14 | 15 | /** 16 | * 定时输出系统信息 17 | * 每天中午 12 点触发 18 | */ 19 | public static void printInfo() { 20 | log.info("\n{}", String.valueOf(SystemUtil.getRuntimeInfo())); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/service/inter/EchartsService.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.service.inter; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * @author kirito 7 | * @date 2021/3/28 8 | * @time 17:29 9 | * Mail 的 Service 层接口 10 | */ 11 | public interface EchartsService { 12 | 13 | /** 14 | * 获取数据 15 | * 16 | * @param datee 日期 17 | * @param token 令牌 18 | * @return java.util.Map 19 | */ 20 | Map getData(String datee, String token); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/annotation/Comment.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * @author kirito 7 | * @date 2021/3/30 8 | * @time 16:13 9 | * 自定义注释注解 10 | */ 11 | @Target({ElementType.FIELD, ElementType.PARAMETER}) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Documented 14 | public @interface Comment { 15 | 16 | /** 17 | * 属性注释 18 | */ 19 | String msg() default ""; 20 | 21 | /** 22 | * 方法参数详细注释 23 | */ 24 | String[] detail() default {""}; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/service/inter/MailService.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.service.inter; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * @author kirito 7 | * @date 2021/1/12 8 | * @time 14:08 9 | * Mail 的 Service 层接口 10 | */ 11 | public interface MailService { 12 | 13 | /** 14 | * 根据快递单号查询出收件人邮箱并发送取件通知邮件 15 | * 16 | * @param ids 快递单号 17 | * @param token 令牌 18 | * @return java.util.Map 19 | */ 20 | Map sendMail(String ids, String token); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/mapper/CodeMapper.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import per.kirito.pack.pojo.Code; 5 | 6 | /** 7 | * @author kirito 8 | * @date 2020/12/23 9 | * @time 17:21 10 | * Code 的 Mapper 层接口 11 | */ 12 | public interface CodeMapper extends BaseMapper { 13 | 14 | /** 15 | * 当最大取件码被使用过时,根据驿站地址查询出当前最早被释放的取件码 16 | * 17 | * @param addr 驿站地址 18 | * @return per.kirito.pack.pojo.Code 19 | */ 20 | Code findCodeFreeMin(String addr); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/pojo/utilpojo/EchartsDO.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.pojo.utilpojo; 2 | 3 | import lombok.*; 4 | import per.kirito.pack.annotation.Comment; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * @author kirito 10 | * @date 2021/3/28 11 | * @time 17:32 12 | * Echarts 饼图数据 13 | */ 14 | @Data 15 | @NonNull 16 | @AllArgsConstructor 17 | public class EchartsDO implements Serializable { 18 | 19 | @Comment(msg = "时间点对应的数量") 20 | private Integer value; 21 | 22 | @Comment(msg = "时间点") 23 | private String name; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/ServletInitializer.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack; 2 | 3 | import org.springframework.boot.builder.SpringApplicationBuilder; 4 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 5 | 6 | /** 7 | * @author kirito 8 | * @date 2020/12/4 9 | * @time 20:37 10 | */ 11 | public class ServletInitializer extends SpringBootServletInitializer { 12 | 13 | @Override 14 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 15 | return application.sources(PackApplication.class); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/pojo/utilpojo/EchartsVO.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.pojo.utilpojo; 2 | 3 | import lombok.*; 4 | import per.kirito.pack.annotation.Comment; 5 | 6 | import java.io.Serializable; 7 | import java.util.List; 8 | 9 | /** 10 | * @author kirito 11 | * @date 2021/3/28 12 | * @time 17:32 13 | * Echarts 数据 14 | */ 15 | @Data 16 | @NonNull 17 | @NoArgsConstructor 18 | public class EchartsVO implements Serializable { 19 | 20 | @Comment(msg = "饼图数据") 21 | private List data; 22 | 23 | @Comment(msg = "折现图数据") 24 | private List count; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | __ ___ __ .______ __ .___________. ______ 2 | | |/ / | | | _ \ | | | |/ __ \ 3 | | ' / | | | |_) || | `---| |----| | | | 4 | | < | | | / | | | | | | | | 5 | | . \ | | | |\ \-| | | | | `--' | 6 | |__|\__\ |__| | _| `._||__| |__| \______/ 7 | :: 8 | :: ${spring.application.name} ${desc.title} ${desc.version} 9 | :: Java Version : ${java.version} ${java.vm.vendor} 10 | :: Spring Boot : (${spring-boot.version}) 11 | :: Author : ${desc.author} ${desc.mail} 12 | :: License : ${desc.license} 13 | :: 14 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/mapper/AdminMapper.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import per.kirito.pack.pojo.Admin; 5 | 6 | /** 7 | * @author kirito 8 | * @date 2020/12/11 9 | * @time 19:54 10 | * Admin 的 Mapper 层接口 11 | */ 12 | public interface AdminMapper extends BaseMapper { 13 | 14 | /** 15 | * 更新 count 数-1 16 | * 17 | * @param id 快递单号 18 | */ 19 | void updateCountLessByPackId(String id); 20 | 21 | /** 22 | * 更新 count 数+1 23 | * 24 | * @param id 快递单号 25 | */ 26 | void updateCountPlusByPackId(String id); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/resources/static/index.js: -------------------------------------------------------------------------------- 1 | 校园快递管理系统
-------------------------------------------------------------------------------- /src/test/java/CheckCodeUtilTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | import per.kirito.pack.util.CheckCodeUtil; 3 | 4 | /** 5 | * @author kirito 6 | * @date 2021/4/1 7 | * @time 9:17:13 8 | */ 9 | public class CheckCodeUtilTest { 10 | 11 | @Test 12 | public void imageToBase64() throws Exception { 13 | String base64 = CheckCodeUtil.imageToBase64(120, 40, CheckCodeUtil.getStringRandom(4)); 14 | System.out.println(base64); 15 | } 16 | 17 | @Test 18 | public void base64ToImage() throws Exception { 19 | String base64 = CheckCodeUtil.imageToBase64(120, 40, CheckCodeUtil.getStringRandom(4)); 20 | String file = "D:/code.png"; 21 | CheckCodeUtil.base64ToImage(base64, file); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/pojo/utilpojo/Page.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.pojo.utilpojo; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | import per.kirito.pack.annotation.Comment; 6 | 7 | import java.io.Serializable; 8 | import java.util.List; 9 | 10 | /** 11 | * @author kirito 12 | * @date 2020/12/28 13 | * @time 14:48 14 | * 自定义分页 15 | */ 16 | @Data 17 | @NoArgsConstructor 18 | public class Page implements Serializable { 19 | 20 | @Comment(msg = "当前页数") 21 | private Integer currentPage; 22 | 23 | @Comment(msg = "每页的记录条数") 24 | private Integer pageSize; 25 | 26 | @Comment(msg = "总记录条数") 27 | private Long total; 28 | 29 | @Comment(msg = "当前页的记录") 30 | private List list; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/resources/mapper/CodeMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | id, code, addr, status, free 7 | 8 | 9 | 10 | 11 | 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/myenum/Express.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.myenum; 2 | 3 | /** 4 | * @author kirito 5 | * @date 2020/12/23 6 | * @time 15:13 7 | * 快递名字枚举类 8 | */ 9 | public enum Express { 10 | 11 | /** 中通:中文名/英文名 */ 12 | 中通("ZTO"), 13 | /** 申通:中文名/英文名 */ 14 | 申通("STO"), 15 | /** 圆通:中文名/英文名 */ 16 | 圆通("YTO"), 17 | /** 京东:中文名/英文名 */ 18 | 京东("JD"), 19 | /**顺丰:中文名/英文名 */ 20 | 顺丰("SF"), 21 | /** 韵达:中文名/英文名 */ 22 | 韵达("YD"), 23 | /** 天天:中文名/英文名 */ 24 | 天天("TT"), 25 | /** EMS:中文名/英文名 */ 26 | EMS("EMS"); 27 | 28 | private final String expressEngName; 29 | 30 | private Express(String expressEngName) { 31 | this.expressEngName = expressEngName; 32 | } 33 | 34 | public String getExpressEngName() { 35 | return expressEngName; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/myenum/PackStatusEnum.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.myenum; 2 | 3 | import com.baomidou.mybatisplus.annotation.EnumValue; 4 | import com.fasterxml.jackson.annotation.JsonValue; 5 | 6 | /** 7 | * author: kirito 8 | * date: 2021/3/30 9 | * time: 16:42 10 | * 取件码状态枚举 11 | */ 12 | public enum PackStatusEnum { 13 | 14 | /** 未有取件码 */ 15 | PACK_STATUS__1(-1, "未有取件码"), 16 | /** 已取出 */ 17 | PACK_STATUS_0(0, "已取出"), 18 | /** 未取出 */ 19 | PACK_STATUS_1(1, "未取出"); 20 | 21 | @EnumValue 22 | private final Integer code; 23 | 24 | @JsonValue 25 | private final String desc; 26 | 27 | PackStatusEnum(Integer code, String desc) { 28 | this.code = code; 29 | this.desc = desc; 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return this.desc; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 校园快递管理系统 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 23 | -------------------------------------------------------------------------------- /src/main/resources/mapper/AdminMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | card, password, phone, name, addr, count 7 | 8 | 9 | 10 | 11 | UPDATE t_admin 12 | SET count = count - 1 13 | WHERE addr IN ( 14 | SELECT addr 15 | FROM t_pack 16 | WHERE id = #{id} 17 | ) 18 | 19 | 20 | 21 | 22 | UPDATE t_admin 23 | SET count = count + 1 24 | WHERE addr IN ( 25 | SELECT addr 26 | FROM t_pack 27 | WHERE id = #{id} 28 | ) 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/resources/static/static/js/manifest.3ad1d5771e9b13dbdad2.js: -------------------------------------------------------------------------------- 1 | !function(r){var n=window.webpackJsonp;window.webpackJsonp=function(e,u,c){for(var f,i,p,a=0,l=[];a { 15 | 16 | /** 17 | * 随机获取一个 User 信息 18 | * 19 | * @return per.kirito.pack.pojo.User 20 | */ 21 | User getUserByRand(); 22 | 23 | /** 24 | * 更新 count 数-1 25 | * 26 | * @param id 快递单号 27 | */ 28 | void updateCountLessByPackId(String id); 29 | 30 | /** 31 | * 更新 count 数+1 32 | * 33 | * @param id 快递单号 34 | */ 35 | void updateCountPlusByPackId(String id); 36 | 37 | /** 38 | * 根据快递单号查询出收件人邮箱、取件码、驿站地址、快递公司信息 39 | * 40 | * @param id 快递单号 41 | * @return java.util.Map 42 | */ 43 | Map getMailById(String id); 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/util/IpAddressUtil.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.util; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | 5 | /** 6 | * @author kirito 7 | * @date 2021/3/9 8 | * @time 22:37 9 | * IP 地址 工具类 10 | */ 11 | public class IpAddressUtil { 12 | 13 | /** 14 | * 获取发送请求的客户端地址 15 | * 16 | * @param request http 请求对象 17 | * @return java.lang.String 18 | */ 19 | public static String getIpAddress(HttpServletRequest request) { 20 | String ip = request.getHeader("X-Forwarded-For"); 21 | if (ip != null && !ip.equals("") && !"unKnown".equalsIgnoreCase(ip)) { 22 | // 多次反向代理后会有多个 ip 值,第一个 ip 才是真实 ip 23 | int index = ip.indexOf(","); 24 | if (index != -1) { 25 | return ip.substring(0, index); 26 | } else { 27 | return ip; 28 | } 29 | } 30 | ip = request.getHeader("X-Real-IP"); 31 | if (ip != null && !"".equals(ip) && !"unKnown".equalsIgnoreCase(ip)) { 32 | return ip; 33 | } 34 | return request.getRemoteAddr(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/resources/static/error/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 校园快递管理系统 6 | 7 | 8 |
9 | 10 |
11 |
12 |

SERVER ERROR

13 |
14 |
15 |

哎呀···服务器出现错误了!

16 |
17 | 25 | 36 | 37 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/pojo/Code.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.pojo; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.*; 8 | import per.kirito.pack.annotation.Comment; 9 | 10 | import java.io.Serializable; 11 | 12 | /** 13 | * @author kirito 14 | * @date 2020/12/23 15 | * @time 17:20 16 | * 取件码存放表 17 | */ 18 | @Data 19 | @NonNull 20 | @NoArgsConstructor 21 | @AllArgsConstructor 22 | @TableName(value = "t_code") 23 | public class Code implements Serializable { 24 | 25 | @Comment(msg = "ID") 26 | @TableId(value = "ID", type = IdType.AUTO) 27 | private Integer id; 28 | 29 | @Comment(msg = "取件码") 30 | @TableField(value = "code") 31 | private String code; 32 | 33 | @Comment(msg = "所在驿站") 34 | @TableField(value = "addr") 35 | private String addr; 36 | 37 | @Comment(msg = "状态,0:未被使用,1:已被使用") 38 | @TableField(value = "status") 39 | private Integer status; 40 | 41 | @Comment(msg = "释放时间") 42 | @TableField(value = "free") 43 | private String free; 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/resources/static/error/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 校园快递管理系统 6 | 7 | 9 |
10 | 11 |
12 |
13 |

404

14 |
15 |
16 |

非常抱歉

17 |
18 |
19 |

您访问的页面不存在

20 |
21 | 26 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/properties/MailProperties.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.properties; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | import org.springframework.stereotype.Component; 5 | 6 | /** 7 | * @author kirito 8 | * @date 2021/1/22 9 | * @time 11:22 10 | * 发送邮件配置参数类 11 | */ 12 | @Component 13 | @ConfigurationProperties(prefix = "mail") 14 | public class MailProperties { 15 | 16 | /** 17 | * 发件人邮箱 18 | */ 19 | private String senderMail; 20 | 21 | /** 22 | * 发件邮箱主机 23 | */ 24 | private String host; 25 | 26 | /** 27 | * 发件邮箱密钥授权码 28 | */ 29 | private String password; 30 | 31 | public MailProperties() { 32 | } 33 | 34 | public String getSenderMail() { 35 | return senderMail; 36 | } 37 | 38 | public void setSenderMail(String senderMail) { 39 | this.senderMail = senderMail; 40 | } 41 | 42 | public String getHost() { 43 | return host; 44 | } 45 | 46 | public void setHost(String host) { 47 | this.host = host; 48 | } 49 | 50 | public String getPassword() { 51 | return password; 52 | } 53 | 54 | public void setPassword(String password) { 55 | this.password = password; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/pojo/Admin.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.pojo; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.*; 8 | import per.kirito.pack.annotation.Comment; 9 | 10 | import java.io.Serializable; 11 | 12 | /** 13 | * @author kirito 14 | * @date 2020/12/4 15 | * @time 20:37 16 | * 管理员实体类 17 | */ 18 | @Data 19 | @NonNull 20 | @TableName(value = "t_admin") 21 | public class Admin implements Serializable { 22 | 23 | @Comment(msg = "编号") 24 | @TableId(value = "card", type = IdType.NONE) 25 | private String card; 26 | 27 | @Comment(msg = "密码") 28 | @TableField(value = "password") 29 | private String password; 30 | 31 | @Comment(msg = "手机号") 32 | @TableField(value = "phone") 33 | private String phone; 34 | 35 | @Comment(msg = "姓名") 36 | @TableField(value = "name") 37 | private String name; 38 | 39 | @Comment(msg = "地址") 40 | @TableField(value = "addr") 41 | private String addr; 42 | 43 | @Comment(msg = "包裹件数(仅限已入站但未取出的快递)") 44 | @TableField(value = "count") 45 | private Integer count; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/config/MybatisPlusConfig.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.config; 2 | 3 | import com.baomidou.mybatisplus.annotation.DbType; 4 | import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer; 5 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 6 | import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; 7 | import org.mybatis.spring.annotation.MapperScan; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | /** 12 | * @author kirito 13 | * @date 2021/3/30 14 | * @time 16:24 15 | * MybatisPlus 配置类 16 | */ 17 | @Configuration 18 | @MapperScan(basePackages = "per.kirito.pack.mapper") 19 | public class MybatisPlusConfig { 20 | 21 | /** 22 | * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 23 | * 避免缓存出现问题(该属性会在旧插件移除后一同移除) 24 | */ 25 | @Bean 26 | public MybatisPlusInterceptor mybatisPlusInterceptor() { 27 | MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); 28 | interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); 29 | return interceptor; 30 | } 31 | 32 | @Bean 33 | public ConfigurationCustomizer configurationCustomizer() { 34 | return configuration -> configuration.setUseDeprecatedExecutor(false); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/controller/ApiController.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.controller; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | import io.swagger.annotations.ApiResponse; 6 | import io.swagger.annotations.ApiResponses; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import per.kirito.pack.util.IpAddressUtil; 11 | 12 | import javax.servlet.http.HttpServletRequest; 13 | 14 | /** 15 | * @author kirito 16 | * @date 2021/4/1 17 | * @time 10:50:48 18 | */ 19 | @Slf4j 20 | @Api(tags = {"API 文档"}) 21 | @Controller 22 | @RequestMapping(value = "/api") 23 | public class ApiController { 24 | 25 | /** 26 | * API 文档 27 | * @return java.lang.String 28 | */ 29 | @ApiOperation(value = "API 文档", notes = "获取 API 文档", httpMethod = "GET") 30 | @ApiResponses(value = { 31 | @ApiResponse(code = 200, message = "响应成功"), 32 | @ApiResponse(code = 201, message = "响应创建"), 33 | @ApiResponse(code = 401, message = "没有权限"), 34 | @ApiResponse(code = 403, message = "请求被拒绝"), 35 | @ApiResponse(code = 404, message = "资源不存在") 36 | }) 37 | @RequestMapping(value = "") 38 | public String api(HttpServletRequest request) { 39 | log.info("请求 URL[/api]"); 40 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 41 | return "/api.html"; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/JasyptTest.java: -------------------------------------------------------------------------------- 1 | import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; 2 | import org.jasypt.encryption.pbe.config.EnvironmentPBEConfig; 3 | import org.junit.Test; 4 | 5 | /** 6 | * author: kirito 7 | * date: 2021/3/8 8 | * time: 8:46 9 | */ 10 | public class JasyptTest { 11 | 12 | @Test 13 | public void testEncrypt() throws Exception { 14 | StandardPBEStringEncryptor standardPBEStringEncryptor = new StandardPBEStringEncryptor(); 15 | EnvironmentPBEConfig config = new EnvironmentPBEConfig(); 16 | 17 | config.setAlgorithm("PBEWithMD5AndDES"); // 加密的算法,这个算法是默认的 18 | config.setPassword("xxx"); // 加密的密钥 19 | standardPBEStringEncryptor.setConfig(config); 20 | String plainText = "xxx"; 21 | String encryptedText = standardPBEStringEncryptor.encrypt(plainText); 22 | System.out.println(encryptedText); 23 | } 24 | 25 | @Test 26 | public void testDecrypt() throws Exception { 27 | StandardPBEStringEncryptor standardPBEStringEncryptor = new StandardPBEStringEncryptor(); 28 | EnvironmentPBEConfig config = new EnvironmentPBEConfig(); 29 | 30 | config.setAlgorithm("PBEWithMD5AndDES"); 31 | config.setPassword("ljk"); 32 | standardPBEStringEncryptor.setConfig(config); 33 | String encryptedText = "aHsFtlQjatrOP2s8bfLGkUG55z53KLNi"; 34 | String plainText = standardPBEStringEncryptor.decrypt(encryptedText); 35 | System.out.println(plainText); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/resources/mapper/UserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | card, password, phone, name, addr, count, mail 7 | 8 | 9 | 10 | 16 | 17 | 18 | 19 | UPDATE t_user 20 | SET count = count - 1 21 | WHERE phone IN ( 22 | SELECT per_tel 23 | FROM t_pack 24 | WHERE id = #{id} 25 | ) 26 | 27 | 28 | 29 | 30 | UPDATE t_user 31 | SET count = count + 1 32 | WHERE phone IN ( 33 | SELECT per_tel 34 | FROM t_pack 35 | WHERE id = #{id} 36 | ) 37 | 38 | 39 | 40 | 50 | 51 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/PackApplication.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack; 2 | 3 | import cn.hutool.cron.CronUtil; 4 | import cn.hutool.system.SystemUtil; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.CommandLineRunner; 8 | import org.springframework.boot.SpringApplication; 9 | import org.springframework.boot.autoconfigure.SpringBootApplication; 10 | import org.springframework.stereotype.Component; 11 | import per.kirito.pack.properties.MailProperties; 12 | 13 | /** 14 | * @author kirito 15 | * @date 2020/12/4 16 | * @time 20:37 17 | * Spring Boot 项目启动类 18 | */ 19 | @Slf4j 20 | @SpringBootApplication(scanBasePackages = "per.kirito.pack") 21 | public class PackApplication { 22 | 23 | public static void main(String[] args) { 24 | log.info("程序启动"); 25 | SpringApplication.run(PackApplication.class, args); 26 | per.kirito.pack.util.cron.CronUtil.printInfo(); 27 | CronUtil.setCronSetting("cron.setting"); 28 | CronUtil.start(); 29 | } 30 | 31 | /** 32 | * 测试 Properties 配置类 33 | */ 34 | @Component 35 | public static class OrderPropertiesCommandLineRunner implements CommandLineRunner { 36 | 37 | @Autowired 38 | private MailProperties mailProperties; 39 | 40 | @Override 41 | public void run(String... args) { 42 | log.info("测试 Properties 配置类"); 43 | log.info("---------- MailProperties ----------"); 44 | log.info("senderMail: {}", mailProperties.getSenderMail()); 45 | log.info("host: {}", mailProperties.getHost()); 46 | log.info("password: {}", mailProperties.getPassword()); 47 | } 48 | 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/util/PickCodeUtil.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.util; 2 | 3 | /** 4 | * @author kirito 5 | * @date 2020/12/23 6 | * @time 16:12 7 | * 取件码工具类,从 1-1-1 到 20-6-20 一共2400件快递有取件码 8 | */ 9 | public class PickCodeUtil { 10 | 11 | // 每个驿站有20个货架,每个货架有6层,每层能放20个快递,每个驿站最多有2400件快递有取件码,每个货架最多能放120件快递 12 | private static final int MAX_1 = 20; 13 | private static final int MAX_2 = 6; 14 | private static final int MAX_3 = 20; 15 | private static final int MAX_PACKS = 2400; 16 | private static final int MAX_SHELF = 120; 17 | 18 | /** 19 | * 根据驿站已有包裹数生成取件码 20 | * 21 | * @param count 快递入站前,驿站未取快递总数 22 | * @return java.lang.String 23 | */ 24 | public static String generate(int count) { 25 | // 新入站的快递还未计算在 count 中 26 | int all = count + 1; 27 | if (count >= MAX_PACKS) { // 如果货架已满,则不生成取件码 28 | return null; 29 | } 30 | // 该摆放的货架快递数量,假设 all 为1253, 那么取件码应为11-3-13 31 | int have = all % MAX_SHELF; // 那么该摆放的这个货架有53件快递 32 | int code1; 33 | int code2; 34 | int code3; 35 | if (all % MAX_SHELF == 0) { 36 | code1 = all / MAX_SHELF; 37 | code2 = MAX_2; 38 | code3 = MAX_3; 39 | } else { 40 | have = all % MAX_SHELF; 41 | code1 = all / MAX_SHELF + 1; 42 | if (have % 20 == 0) { 43 | code2 = have / MAX_3; 44 | code3 = MAX_3; 45 | } else { 46 | code2 = have / MAX_3 + 1; 47 | code3 = have % MAX_3; 48 | } 49 | } 50 | // 防止生成的第一位取件码大于20的异常 51 | if (code1 > MAX_3) { 52 | return null; 53 | } else { 54 | if (code3 < 10) { 55 | return String.valueOf(code1 + "-" + code2 + "-0" + code3); 56 | } else { 57 | return String.valueOf(code1 + "-" + code2 + "-" + code3); 58 | } 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/mapper/SendMapper.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.baomidou.mybatisplus.core.metadata.IPage; 5 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 6 | import org.apache.ibatis.annotations.Param; 7 | import per.kirito.pack.pojo.Send; 8 | 9 | /** 10 | * @author kirito 11 | * @date 2021/1/5 12 | * @time 11:14 13 | * Send 的 Mapper 层接口 14 | */ 15 | public interface SendMapper extends BaseMapper { 16 | 17 | /** 18 | * 获取 User 的寄件集合 19 | * 20 | * @param page 分页 21 | * @param card 学号 22 | * @param org 快递公司 23 | * @param status 寄件状态 24 | * @param search 搜索 25 | * @return com.baomidou.mybatisplus.extension.plugins.pagination.Page 26 | */ 27 | Page getSendByUser(IPage page, String card, String org, String status, String search); 28 | 29 | /** 30 | * 获取 Admin 的寄件集合 31 | * 32 | * @param page 分页 33 | * @param org 快递公司 34 | * @param status 寄件状态 35 | * @param search 搜索 36 | * @return com.baomidou.mybatisplus.extension.plugins.pagination.Page 37 | */ 38 | Page getSendByAdmin(IPage page, String org, String status, String search); 39 | 40 | /** 41 | * 传入状态,获取 User 的该状态寄件数量 42 | * 43 | * @param card 学号 44 | * @param status 状态 45 | * @return int 46 | */ 47 | int getUserTotal(@Param("card") String card, @Param("status") String status); 48 | 49 | /** 50 | * 传入状态,获取 Admin 的该状态寄件数量 51 | * 52 | * @param org 快递公司 53 | * @param status 状态 54 | * @return int 55 | */ 56 | int getAdminTotal(@Param("org") String org, @Param("status") String status); 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/config/SwaggerConfiguration.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.config; 2 | 3 | import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import springfox.documentation.builders.ApiInfoBuilder; 7 | import springfox.documentation.builders.PathSelectors; 8 | import springfox.documentation.builders.RequestHandlerSelectors; 9 | import springfox.documentation.service.ApiInfo; 10 | import springfox.documentation.service.Contact; 11 | import springfox.documentation.spi.DocumentationType; 12 | import springfox.documentation.spring.web.plugins.Docket; 13 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 14 | 15 | /** 16 | * @author kirito 17 | * @date 2021/2/24 18 | * @time 16:15 19 | * swagger 配置类 20 | */ 21 | @Configuration 22 | @EnableSwagger2 23 | @EnableSwaggerBootstrapUI 24 | public class SwaggerConfiguration { 25 | 26 | @Bean 27 | public Docket createRestApi() { 28 | return new Docket(DocumentationType.SWAGGER_2) 29 | .apiInfo(apiInfo()) 30 | .select() 31 | .apis(RequestHandlerSelectors.basePackage("per.kirito.pack.controller")) 32 | .paths(PathSelectors.any()) 33 | .build(); 34 | } 35 | 36 | private ApiInfo apiInfo() { 37 | Contact contact = new Contact("kirito", "https://github.com/kirito66465", "kirito1204505357@gmail.com"); 38 | return new ApiInfoBuilder() 39 | .title("APIs 接口文档") 40 | .description("swagger-bootstrap-ui") 41 | .termsOfServiceUrl("http://localhost:5050/") 42 | .contact(contact) 43 | .version("1.0") 44 | .license("Apache License") 45 | .build(); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/resources/static/live2d-widget/demo/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Live2D 看板娘 / Demo 6 | 7 | 24 | 25 | 26 | 27 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/pojo/Send.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.pojo; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.*; 8 | import per.kirito.pack.annotation.Comment; 9 | 10 | import java.io.Serializable; 11 | 12 | /** 13 | * @author kirito 14 | * @date 2020/12/6 15 | * @time 16:08 16 | * 寄件实体类 17 | */ 18 | @Data 19 | @NonNull 20 | @NoArgsConstructor 21 | @AllArgsConstructor 22 | @TableName(value = "t_send") 23 | public class Send implements Serializable { 24 | 25 | @Comment(msg = "寄件人姓名") 26 | @TableField(value = "from_name") 27 | private String fromName; 28 | 29 | @Comment(msg = "寄件人手机号") 30 | @TableField(value = "from_tel") 31 | private String fromTel; 32 | 33 | @Comment(msg = "寄件人地址") 34 | @TableField(value = "from_addr") 35 | private String fromAddr; 36 | 37 | @Comment(msg = "收件人姓名") 38 | @TableField(value = "to_name") 39 | private String toName; 40 | 41 | @Comment(msg = "收件人手机号") 42 | @TableField(value = "to_tel") 43 | private String toTel; 44 | 45 | @Comment(msg = "收件人地址") 46 | @TableField(value = "to_addr") 47 | private String toAddr; 48 | 49 | @Comment(msg = "运费") 50 | @TableField(value = "price") 51 | private Double price; 52 | 53 | @Comment(msg = "快递单号") 54 | @TableId(value = "id", type = IdType.NONE) 55 | private String id; 56 | 57 | @Comment(msg = "快递公司") 58 | @TableField(value = "org") 59 | private String org; 60 | 61 | @Comment(msg = "快递状态") 62 | @TableField(value = "status") 63 | private String status; 64 | 65 | @Comment(msg = "时间戳") 66 | @TableField(value = "dt") 67 | private String dt; 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/pojo/utilpojo/SendRequest.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.pojo.utilpojo; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | import lombok.*; 6 | import per.kirito.pack.annotation.Comment; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * @author kirito 12 | * @date 2021/1/4 13 | * @time 15:35 14 | * 前台 User 进行寄件的请求参数 15 | */ 16 | @Data 17 | @NonNull 18 | @ApiModel(value = "SendRequest", description = "寄件表单对象") 19 | public class SendRequest implements Serializable { 20 | 21 | @Comment(msg = "驿站选择") 22 | @ApiModelProperty(value = "驿站", name = "admin", dataType = "String", required = true) 23 | private String admin; 24 | 25 | @Comment(msg = "收件人姓名") 26 | @ApiModelProperty(value = "收件人姓名", name = "name", dataType = "String", required = true) 27 | private String name; 28 | 29 | @Comment(msg = "收件人联系方式") 30 | @ApiModelProperty(value = "收件人联系方式", name = "phone", dataType = "String", required = true) 31 | private String phone; 32 | 33 | @Comment(msg = "收件人地址,省/市/区") 34 | @ApiModelProperty(value = "收件人地址", name = "addr", dataType = "String", required = true, example = "江苏省南京市栖霞区") 35 | private String addr; 36 | 37 | @Comment(msg = "物品信息") 38 | @ApiModelProperty(value = "物品信息", name = "info", dataType = "String", required = true) 39 | private String info; 40 | 41 | @Comment(msg = "物品重量") 42 | @ApiModelProperty(value = "物品重量", name = "weight", dataType = "Integer", required = true) 43 | private Integer weight; 44 | 45 | @Comment(msg = "运费") 46 | @ApiModelProperty(value = "运费", name = "price", dataType = "Double", required = true) 47 | private Double price; 48 | 49 | @Comment(msg = "登录状态令牌") 50 | @ApiModelProperty(value = "登录状态令牌", name = "token", dataType = "String", required = true) 51 | private String token; 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/controller/CheckCodeController.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.controller; 2 | 3 | import io.swagger.annotations.*; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.*; 7 | import per.kirito.pack.service.inter.CheckCodeService; 8 | import per.kirito.pack.util.IpAddressUtil; 9 | 10 | import javax.servlet.http.HttpServletRequest; 11 | import java.util.Map; 12 | 13 | /** 14 | * @author kirito 15 | * @date 2021/1/8 16 | * @time 16:26 17 | * 验证码的 controller 层 18 | */ 19 | @Slf4j 20 | @Api(tags = {"验证码"}, produces = "application/json", consumes = "application/json") 21 | // @CrossOrigin 22 | @RestController 23 | @RequestMapping(value = "/get-check-code") 24 | public class CheckCodeController { 25 | 26 | @Autowired 27 | private CheckCodeService checkCodeService; 28 | 29 | /** 30 | * 获取验证码字符串和图片 31 | * 32 | * @param token 令牌 33 | * @param request http 请求 34 | * @return java.util.Map 35 | */ 36 | @ApiOperation(value = "获取验证码字符串和图片", notes = "获取验证码请求,在登录情况下返回验证码生成成功与否", httpMethod = "POST") 37 | @ApiResponses(value = { 38 | @ApiResponse(code = 200, message = "响应成功"), 39 | @ApiResponse(code = 201, message = "响应创建"), 40 | @ApiResponse(code = 401, message = "没有权限"), 41 | @ApiResponse(code = 403, message = "请求被拒绝"), 42 | @ApiResponse(code = 404, message = "资源不存在") 43 | }) 44 | @PostMapping(value = "") 45 | public Map getCheckCode( 46 | @ApiParam(required = true, name = "token", value = "token 令牌") @RequestParam(value = "token") String token, 47 | HttpServletRequest request) { 48 | log.info("请求 URL[/get-check-code];参数[token={}]", token); 49 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 50 | return checkCodeService.getCheckCode(token); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/controller/EchartsController.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.controller; 2 | 3 | import io.swagger.annotations.*; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.*; 7 | import per.kirito.pack.service.inter.EchartsService; 8 | import per.kirito.pack.util.IpAddressUtil; 9 | 10 | import javax.servlet.http.HttpServletRequest; 11 | import java.util.Map; 12 | 13 | /** 14 | * @author kirito 15 | * @date 2021/3/28 16 | * @time 17:27 17 | * Echarts 的 Controller 层 18 | */ 19 | @Slf4j 20 | @Api(tags = {"Echarts"}) 21 | // @CrossOrigin 22 | @RestController 23 | @RequestMapping(value = "/echarts") 24 | public class EchartsController { 25 | 26 | @Autowired 27 | private EchartsService echartsService; 28 | 29 | /** 30 | * 获取 Echarts 数据 31 | * 32 | * @param datee 日期 33 | * @param token 令牌 34 | * @return java.util.Map 35 | */ 36 | @ApiOperation(value = "获取 Echarts 数据", notes = "获取 Echarts 数据,包括饼图和平滑折线图所需数据", httpMethod = "POST") 37 | @ApiResponses(value = { 38 | @ApiResponse(code = 200, message = "响应成功"), 39 | @ApiResponse(code = 201, message = "响应创建"), 40 | @ApiResponse(code = 401, message = "没有权限"), 41 | @ApiResponse(code = 403, message = "请求被拒绝"), 42 | @ApiResponse(code = 404, message = "资源不存在") 43 | }) 44 | @PostMapping(value = "/get-data") 45 | public Map getData( 46 | @ApiParam(required = false, name = "datee", value = "日期") String datee, 47 | @ApiParam(required = true, name = "token", value = "token 令牌") @RequestParam(value = "token") String token, 48 | HttpServletRequest request) { 49 | log.info("请求 URL[/echarts/get-data];参数[datee={}, token={}]", datee, token); 50 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 51 | return echartsService.getData(datee, token); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/service/inter/AccountService.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.service.inter; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * @author kirito 7 | * @date 2020/12/28 8 | * @time 15:21 9 | * 账户管理相关泛型接口 10 | */ 11 | public interface AccountService { 12 | 13 | /** 14 | * 登录 15 | * 16 | * @param card 编号 17 | * @param password 密码 18 | * @return java.util.Map 19 | */ 20 | Map login(String card, String password); 21 | 22 | /** 23 | * 退出登录 24 | * 25 | * @param token 令牌 26 | * @return java.lang.String 27 | */ 28 | String logout(String token); 29 | 30 | /** 31 | * 获取账户信息 32 | * 33 | * @param token 令牌 34 | * @return java.util.Map 35 | */ 36 | Map getInfo(String token); 37 | 38 | /** 39 | * 账户注册 40 | * 41 | * @param entity 账户实体信息 42 | * @return java.util.Map 43 | */ 44 | Map register(E entity); 45 | 46 | /** 47 | * 账户忘记密码 48 | * 49 | * @param card 编号 50 | * @param phone 手机号 51 | * @param password 新密码 52 | * @return java.util.Map 53 | */ 54 | Map forgetPwd(String card, String phone, String password); 55 | 56 | /** 57 | * 账户修改密码 58 | * 59 | * @param card 编号 60 | * @param oldPwd 原密码 61 | * @param newPwd 新密码 62 | * @param checkCode 验证码 63 | * @param token 令牌 64 | * @return java.util.Map 65 | */ 66 | Map resetPwd(String card, String oldPwd, String newPwd, String checkCode, String token); 67 | 68 | /** 69 | * 更新用户信息 70 | * 71 | * @param name 姓名 72 | * @param addr 地址 73 | * @param mail 邮箱 74 | * @param token 令牌 75 | * @return java.util.Map 76 | */ 77 | Map updateInfo(String name, String addr, String mail, String token); 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/util/SendUtil.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.util; 2 | 3 | import per.kirito.pack.myenum.Express; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * @author kirito 10 | * @date 2021/1/5 11 | * @time 17:15 12 | * 寄件工具类 13 | */ 14 | public class SendUtil { 15 | 16 | private static final String ZTO = String.valueOf(Express.中通); 17 | private static final String YD = String.valueOf(Express.韵达); 18 | private static final String EMS = String.valueOf(Express.EMS); 19 | 20 | private static final String ZTO_TYPE = Express.中通.getExpressEngName(); 21 | private static final String YD_TYPE = Express.韵达.getExpressEngName(); 22 | private static final String EMS_TYPE = Express.EMS.getExpressEngName(); 23 | 24 | /** 25 | * 生成寄件的快递单号和快递公司 26 | * 27 | * @param postAddr 驿站地址 28 | * @return java.util.Map 29 | */ 30 | public static Map getSendIdAndOrg(String postAddr) { 31 | Map map = new HashMap<>(); 32 | String id = ""; 33 | String org = ""; 34 | switch (postAddr) { 35 | case "中苑": 36 | id = PackIdUtil.generate(ZTO_TYPE); 37 | org = ZTO; 38 | break; 39 | case "西苑": 40 | id = PackIdUtil.generate(YD_TYPE); 41 | org = YD; 42 | break; 43 | case "北苑": 44 | id = PackIdUtil.generate(EMS_TYPE); 45 | org = EMS; 46 | break; 47 | default: 48 | break; 49 | } 50 | map.put("id", id); 51 | map.put("org", org); 52 | return map; 53 | } 54 | 55 | /** 56 | * 抽取出来的传入驿站编号,返回寄件快递公司/种类方法 57 | * 58 | * @param card 驿站编号 59 | * @return java.lang.String 60 | */ 61 | public static String getSendOrg(String card) { 62 | String org = ""; 63 | switch (card) { 64 | case "2101": 65 | org = ZTO; 66 | break; 67 | case "2102": 68 | org = YD; 69 | break; 70 | case "2103": 71 | org = EMS; 72 | break; 73 | default: 74 | break; 75 | } 76 | return org; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/controller/MailController.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.controller; 2 | 3 | import io.swagger.annotations.*; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.*; 7 | import per.kirito.pack.service.inter.MailService; 8 | import per.kirito.pack.util.IpAddressUtil; 9 | 10 | import javax.servlet.http.HttpServletRequest; 11 | import java.util.Map; 12 | 13 | /** 14 | * @author kirito 15 | * @date 2021/1/12 16 | * @time 14:54 17 | * Mail 的 Controller 层 18 | */ 19 | @Slf4j 20 | @Api(tags = {"发送邮件"}, produces = "application/json", consumes = "application/json") 21 | // @CrossOrigin 22 | @RestController 23 | @RequestMapping(value = "/mail") 24 | public class MailController { 25 | 26 | @Autowired 27 | private MailService mailService; 28 | 29 | /** 30 | * 根据快递单号查询出收件人邮箱并发送取件通知邮件 31 | * 32 | * @param ids 快递单号 33 | * @param token 令牌 34 | * @param request http 请求 35 | * @return java.util.Map 36 | */ 37 | @ApiOperation(value = "邮件通知", notes = "发送邮件请求,返回发送成功与否", httpMethod = "POST") 38 | @ApiResponses(value = { 39 | @ApiResponse(code = 200, message = "响应成功"), 40 | @ApiResponse(code = 201, message = "响应创建"), 41 | @ApiResponse(code = 401, message = "没有权限"), 42 | @ApiResponse(code = 403, message = "请求被拒绝"), 43 | @ApiResponse(code = 404, message = "资源不存在") 44 | }) 45 | @PostMapping(value = "/notice") 46 | public Map sendMail( 47 | @ApiParam(required = true, name = "ids", value = "快递单号") @RequestParam(value = "ids") String ids, 48 | @ApiParam(required = true, name = "token", value = "token 令牌") @RequestParam(value = "token") String token, 49 | HttpServletRequest request) { 50 | log.info("请求 URL[/mail/notice];参数[ids={}, token={}]", ids, token); 51 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 52 | return mailService.sendMail(ids, token); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/pojo/Pack.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.pojo; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.*; 8 | import per.kirito.pack.annotation.Comment; 9 | import per.kirito.pack.myenum.PackStatusEnum; 10 | 11 | import java.io.Serializable; 12 | 13 | /** 14 | * @author kirito 15 | * @date 2020/12/4 16 | * @time 20:41 17 | * 快递实体类 18 | */ 19 | @Data 20 | @NonNull 21 | @NoArgsConstructor 22 | @AllArgsConstructor 23 | @TableName(value = "t_pack") 24 | public class Pack implements Serializable { 25 | 26 | @Comment(msg = "快递单号") 27 | @TableId(value = "id", type = IdType.NONE) 28 | private String id; 29 | 30 | @Comment(msg = "快递公司") 31 | @TableField(value = "org") 32 | private String org; 33 | 34 | @Comment(msg = "收件人") 35 | @TableField(value = "per_name") 36 | private String perName; 37 | 38 | @Comment(msg = "收件手机号") 39 | @TableField(value = "per_tel") 40 | private String perTel; 41 | 42 | @Comment(msg = "收件地址") 43 | @TableField(value = "per_addr") 44 | private String perAddr; 45 | 46 | @Comment(msg = "所在驿站") 47 | @TableField(value = "addr") 48 | private String addr; 49 | 50 | @Comment(msg = "取件码") 51 | @TableField(value = "code") 52 | private String code; 53 | 54 | @Comment(msg = "驿站联系人") 55 | @TableField(value = "cont_name") 56 | private String contName; 57 | 58 | @Comment(msg = "驿站联系方式") 59 | @TableField(value = "cont_tel") 60 | private String contTel; 61 | 62 | @Comment(msg = "快递状态") 63 | @TableField(value = "status") 64 | private PackStatusEnum status; 65 | 66 | @Comment(msg = "入站时间") 67 | @TableField(value = "start") 68 | private String start; 69 | 70 | @Comment(msg = "取件时间") 71 | @TableField(value = "end") 72 | private String end; 73 | 74 | @Comment(msg = "取件人") 75 | @TableField(value = "pick") 76 | private String pick; 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/pojo/Echarts.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.pojo; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.*; 8 | import per.kirito.pack.annotation.Comment; 9 | 10 | import java.io.Serializable; 11 | 12 | /** 13 | * @author kirito 14 | * @date 2021/3/28 15 | * @time 17:07 16 | * Echarts 数据统计实体类 17 | */ 18 | @Data 19 | @NonNull 20 | @NoArgsConstructor 21 | @AllArgsConstructor 22 | @TableName(value = "t_echarts") 23 | public class Echarts implements Serializable { 24 | 25 | @Comment(msg = "ID") 26 | @TableId(value = "ID", type = IdType.AUTO) 27 | private Integer id; 28 | 29 | @Comment(msg = "日期") 30 | @TableField(value = "datee") 31 | private String datee; 32 | 33 | @Comment(msg = "编号") 34 | @TableField(value = "card") 35 | private String card; 36 | 37 | @Comment(msg = "9 点数据") 38 | @TableField(value = "nine") 39 | private Integer nine; 40 | 41 | @Comment(msg = "10 点数据") 42 | @TableField(value = "ten") 43 | private Integer ten; 44 | 45 | @Comment(msg = "11 点数据") 46 | @TableField(value = "eleven") 47 | private Integer eleven; 48 | 49 | @Comment(msg = "12 点数据") 50 | @TableField(value = "twelve") 51 | private Integer twelve; 52 | 53 | @Comment(msg = "13 点数据") 54 | @TableField(value = "thirteen") 55 | private Integer thirteen; 56 | 57 | @Comment(msg = "14 点数据") 58 | @TableField(value = "fourteen") 59 | private Integer fourteen; 60 | 61 | @Comment(msg = "15 点数据") 62 | @TableField(value = "fifteen") 63 | private Integer fifteen; 64 | 65 | @Comment(msg = "16 点数据") 66 | @TableField(value = "sixteen") 67 | private Integer sixteen; 68 | 69 | @Comment(msg = "17 点数据") 70 | @TableField(value = "seventeen") 71 | private Integer seventeen; 72 | 73 | @Comment(msg = "18 点数据") 74 | @TableField(value = "eighteen") 75 | private Integer eighteen; 76 | 77 | @Comment(msg = "19 点数据") 78 | @TableField(value = "nineteen") 79 | private Integer nineteen; 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/PackServiceTest.java: -------------------------------------------------------------------------------- 1 | import cn.hutool.core.io.file.FileReader; 2 | import org.junit.Test; 3 | import org.junit.runner.RunWith; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | import per.kirito.pack.PackApplication; 8 | import per.kirito.pack.service.inter.PackService; 9 | 10 | /** 11 | * author: kirito 12 | * date: 2021/2/23 13 | * time: 9:29 14 | */ 15 | @RunWith(SpringRunner.class) 16 | @SpringBootTest(classes = PackApplication.class) 17 | public class PackServiceTest { 18 | 19 | @Autowired 20 | private PackService packService; 21 | 22 | @Test 23 | public void test1() { 24 | System.out.println("==================================="); 25 | FileReader fileReader = new FileReader("D:/IdeaProjects/pack/src/main/resources/file/单号/3000/2101.txt"); 26 | String result = fileReader.readString(); 27 | String[] array = result.split(","); 28 | for(String a : array) { 29 | String s = packService.addPack(a, "a"); 30 | System.out.println(s); 31 | } 32 | System.out.println("==================================="); 33 | 34 | System.out.println("==================================="); 35 | FileReader fileReader2 = new FileReader("D:/IdeaProjects/pack/src/main/resources/file/单号/3000/2102.txt"); 36 | String result2 = fileReader2.readString(); 37 | String[] array2 = result2.split(","); 38 | for(String a : array2) { 39 | String s = packService.addPack(a, "b"); 40 | System.out.println(s); 41 | } 42 | System.out.println("==================================="); 43 | 44 | System.out.println("==================================="); 45 | FileReader fileReader3 = new FileReader("D:/IdeaProjects/pack/src/main/resources/file/单号/3000/2103.txt"); 46 | String result3 = fileReader3.readString(); 47 | String[] array3 = result3.split(","); 48 | for(String a : array3) { 49 | String s = packService.addPack(a, "c"); 50 | System.out.println(s); 51 | } 52 | System.out.println("==================================="); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/pojo/User.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.pojo; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.*; 10 | import per.kirito.pack.annotation.Comment; 11 | 12 | import java.io.Serializable; 13 | 14 | /** 15 | * @author kirito 16 | * @date 2020/12/4 17 | * @time 20:37 18 | * 用户实体类 19 | */ 20 | @Data 21 | @NonNull 22 | @ApiModel(value = "User", description = "学生对象") 23 | @TableName(value = "t_user") 24 | public class User implements Serializable { 25 | 26 | @Comment(msg = "学号") 27 | @ApiModelProperty(value = "学号", name = "card", dataType = "String", required = true) 28 | @TableId(value = "card", type = IdType.NONE) 29 | private String card; 30 | 31 | @Comment(msg = "密码") 32 | @ApiModelProperty(value = "密码", name = "password", dataType = "String", required = true) 33 | @TableField(value = "password") 34 | private String password; 35 | 36 | @Comment(msg = "手机号") 37 | @ApiModelProperty(value = "手机号", name = "phone", dataType = "String", required = true) 38 | @TableField(value = "phone") 39 | private String phone; 40 | 41 | @Comment(msg = "姓名") 42 | @ApiModelProperty(value = "姓名", name = "name", dataType = "String", required = false) 43 | @TableField(value = "name") 44 | private String name; 45 | 46 | @Comment(msg = "收货地址") 47 | @ApiModelProperty(value = "手机号", name = "phone", dataType = "String", required = true) 48 | @TableField(value = "addr") 49 | private String addr; 50 | 51 | @Comment(msg = "包裹件数(仅限已入站但未取出的快递)") 52 | @ApiModelProperty(value = "包裹件数(仅限已入站但未取出的快递)", name = "count", dataType = "Integer", required = true) 53 | @TableField(value = "count") 54 | private Integer count; 55 | 56 | @Comment(msg = "电子邮箱") 57 | @ApiModelProperty(value = "电子邮箱", name = "mail", dataType = "String", required = true) 58 | @TableField(value = "mail") 59 | private String mail; 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/resources/static/live2d-widget/autoload.js: -------------------------------------------------------------------------------- 1 | // 注意:live2d_path 参数应使用绝对路径 2 | // const live2d_path = "https://cdn.jsdelivr.net/gh/stevenjoezhang/live2d-widget@latest/"; 3 | const live2d_path = "/pack/live2d-widget/"; 4 | 5 | // 封装异步加载资源的方法 6 | function loadExternalResource(url, type) { 7 | return new Promise((resolve, reject) => { 8 | let tag; 9 | 10 | if (type === "css") { 11 | tag = document.createElement("link"); 12 | tag.rel = "stylesheet"; 13 | tag.href = url; 14 | } 15 | else if (type === "js") { 16 | tag = document.createElement("script"); 17 | tag.src = url; 18 | } 19 | if (tag) { 20 | tag.onload = () => resolve(url); 21 | tag.onerror = () => reject(url); 22 | document.head.appendChild(tag); 23 | } 24 | }); 25 | } 26 | 27 | // 加载 waifu.css live2d.min.js waifu-tips.js 28 | if (screen.width >= 768) { 29 | Promise.all([ 30 | loadExternalResource(live2d_path + "waifu.css", "css"), 31 | loadExternalResource(live2d_path + "live2d.min.js", "js"), 32 | loadExternalResource(live2d_path + "waifu-tips.js", "js") 33 | ]).then(() => { 34 | initWidget({ 35 | waifuPath: live2d_path + "waifu-tips.json", 36 | //apiPath: "https://live2d.fghrsh.net/api/", 37 | cdnPath: "https://cdn.jsdelivr.net/gh/fghrsh/live2d_api/" 38 | }); 39 | }); 40 | } 41 | // initWidget 第一个参数为 waifu-tips.json 的路径,第二个参数为 API 地址 42 | // API 后端可自行搭建,参考 https://github.com/fghrsh/live2d_api 43 | // 初始化看板娘会自动加载指定目录下的 waifu-tips.json 44 | 45 | console.log(` 46 | く__,.ヘヽ. / ,ー、 〉 47 | \ ', !-─‐-i / /´ 48 | /`ー' L//`ヽ、 49 | / /, /| , , ', 50 | イ / /-‐/ i L_ ハ ヽ! i 51 | レ ヘ 7イ`ト レ'ァ-ト、!ハ| | 52 | !,/7 '0' ´0iソ| | 53 | |.从" _ ,,,, / |./ | 54 | レ'| i>.、,,__ _,.イ / .i | 55 | レ'| | / k_7_/レ'ヽ, ハ. | 56 | | |/i 〈|/ i ,.ヘ | i | 57 | .|/ / i: ヘ! \ | 58 | kヽ>、ハ _,.ヘ、 /、! 59 | !'〈//`T´', \ `'7'ーr' 60 | レ'ヽL__|___i,___,ンレ|ノ 61 | ト-,/ |___./ 62 | 'ー' !_,.: 63 | `); 64 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/service/impl/CheckCodeServiceImpl.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.service.impl; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.data.redis.core.StringRedisTemplate; 6 | import org.springframework.stereotype.Service; 7 | import per.kirito.pack.myenum.Status; 8 | import per.kirito.pack.util.CheckCodeUtil; 9 | import per.kirito.pack.service.inter.CheckCodeService; 10 | import per.kirito.pack.util.Constant; 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | /** 17 | * @author kirito 18 | * @date 2021/1/8 19 | * @time 16:28 20 | * 验证码的 Service 实现类 21 | */ 22 | @Slf4j 23 | @Service 24 | public class CheckCodeServiceImpl implements CheckCodeService { 25 | 26 | @Autowired 27 | private StringRedisTemplate stringRedisTemplate; 28 | 29 | private static final String INFO_SUCCESS = Status.INFO_SUCCESS.getEnMsg(); 30 | private static final String INFO_FAIL = Status.INFO_FAIL.getEnMsg(); 31 | private static final String LOGIN_TO_DO = Status.LOGIN_TO_DO.getEnMsg(); 32 | 33 | /** 34 | * 获取验证码字符串和图片 35 | * 36 | * @param token 令牌 37 | * @return java.util.Map 38 | */ 39 | @Override 40 | public Map getCheckCode(String token) { 41 | Map map = new HashMap<>(); 42 | try { 43 | if (stringRedisTemplate.hasKey(token)) { 44 | // 生成验证码 45 | String code = CheckCodeUtil.getStringRandom(4); 46 | // 生成验证码图片 47 | String pic = CheckCodeUtil.imageToBase64(120, 40, code); 48 | String tokenCode = token + "-code"; 49 | // 判断 Redis 中有无存储 50 | if (stringRedisTemplate.hasKey(tokenCode)) { 51 | // 当请求新的验证码时,如果旧的验证码存在则删除 52 | stringRedisTemplate.delete(tokenCode); 53 | } 54 | // 将验证码存入 Redis,并设置有效期2分钟 55 | stringRedisTemplate.opsForValue().set(tokenCode, code, Constant.VERIFY_VALID_MINUTE, TimeUnit.MINUTES); 56 | map.put("codePic", pic); 57 | log.info("token: {}, code: {}", token, code); 58 | map.put("result", INFO_SUCCESS); 59 | } else { 60 | log.info("token: {} 获取验证码失败,因为登录状态失效!", token); 61 | map.put("result", LOGIN_TO_DO); 62 | } 63 | return map; 64 | } catch (Exception e) { 65 | log.error("error: {}", e.getMessage(), e); 66 | log.info("token: {} 获取验证码失败,因为发生了异常!", token); 67 | map.put("result", INFO_FAIL); 68 | return map; 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ${GTC_PATTERN} 18 | UTF-8 19 | 20 | 21 | 22 | 23 | 24 | ${PATH}/${appName}.log 25 | 26 | 27 | ${PATH}/${appName}-%d{yyyy-MM-dd}.%i.log 28 | 29 | 60 30 | 31 | 32 | 512MB 33 | 34 | 35 | 20GB 36 | 37 | 38 | ${GTC_PATTERN} 39 | UTF-8 40 | 41 | 42 | 43 | 44 | ACCEPT 45 | DENY 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/myenum/Status.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.myenum; 2 | 3 | /** 4 | * @author kirito 5 | * @date 2020/12/24 6 | * @time 10:30 7 | * 状态码与值枚举类 8 | */ 9 | public enum Status { 10 | 11 | /** 登录 */ 12 | LOGIN_SUCCESS(1, "login success", "登录成功!"), 13 | LOGIN_FAIL(0, "login fail", "登录失败!"), 14 | /** 注册 */ 15 | REGISTER_SUCCESS(1, "register success", "注册成功!"), 16 | REGISTER_FAIL(0, "register fail", "注册失败!"), 17 | /** 退出 */ 18 | EXIT_SUCCESS(1, "exit success", "退出成功!"), 19 | EXIT_FAIL(0, "exit fail", "退出失败!"), 20 | /** 修改密码 */ 21 | PWD_SUCCESS(1, "update password success", "修改密码成功!"), 22 | PWD_FAIL(0, "update password fail", "修改密码失败!"), 23 | /** 取件 */ 24 | PICK_SUCCESS(1, "pick up the package success", "取件成功!"), 25 | PICK_FAIL(0, "pick up the package fail", "取件失败!"), 26 | /** 入站 */ 27 | INTO_SUCCESS(1, "the package enter addr success", "快递入站成功!"), 28 | INTO_FAIL(0, "the package enter addr fail", "快递入站失败!"), 29 | /** 获取信息 */ 30 | INFO_SUCCESS(1, "get info success", "获取信息成功!"), 31 | INFO_FAIL(0, "get info fail", "获取信息失败!"), 32 | /** 是否存在 */ 33 | IS_EXIST(1, "is exist", "存在!"), 34 | NOT_EXIST(0, "not exist", "不存在!"), 35 | /** 操作失败 */ 36 | DO_SUCCESS(1, "do success", "操作成功!"), 37 | DO_FAIL(0, "do fail", "操作失败!"), 38 | /** 取件码使用状态 */ 39 | IS_USE(1, "has been used", "已被使用"), 40 | NOT_USE(0, "has not been used", "已被使用"), 41 | /** 快递状态 */ 42 | PACK_STATUS_1(1, "未取出", "未取出"), 43 | PACK_STATUS_0(0, "已取出", "已取出"), 44 | PACK_STATUS__1(-1, "未有取件码", "未有取件码"), 45 | /** 寄件状态 */ 46 | SEND_STATUS_0(0, "已提交", "已提交"), 47 | SEND_STATUS_1(1, "已支付", "已支付"), 48 | SEND_STATUS_2(2, "已确认", "已确认"), 49 | SEND_STATUS_3(3, "已发出", "已发出"), 50 | /** 取件码使用状态 */ 51 | CODE_STATUS_1(1, "已被使用过", "已被使用过"), 52 | CODE_STATUS_0(0, "未被使用过", "未被使用过"), 53 | /** 其他 */ 54 | TAKE_SUCCESS(1, "take over", "代取成功"), 55 | LOGIN_TO_DO(-1, "please login to operate", "请登录再操作"), 56 | CODE_ERR(-1, "check code is wrong", "验证码输入错误"), 57 | CODE_INVALID(0, "code has expired", "验证码已过期"), 58 | PWD_ERR(-1, "password not same", "原密码输入错误"); 59 | 60 | 61 | private final int code; 62 | private final String enMsg; 63 | private final String zhMsg; 64 | 65 | private Status(int code, String enMsg, String zhMsg) { 66 | this.code = code; 67 | this.enMsg = enMsg; 68 | this.zhMsg = zhMsg; 69 | } 70 | 71 | public int getCode() { 72 | return this.code; 73 | } 74 | 75 | public String getEnMsg() { 76 | return this.enMsg; 77 | } 78 | 79 | public String getZhMsg() { 80 | return this.zhMsg; 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/service/impl/EchartsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.service.impl; 2 | 3 | import cn.hutool.core.date.DateUtil; 4 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.data.redis.core.StringRedisTemplate; 8 | import org.springframework.stereotype.Service; 9 | import per.kirito.pack.mapper.EchartsMapper; 10 | import per.kirito.pack.myenum.Status; 11 | import per.kirito.pack.pojo.Echarts; 12 | import per.kirito.pack.pojo.utilpojo.EchartsVO; 13 | import per.kirito.pack.service.inter.EchartsService; 14 | import per.kirito.pack.util.TypeConversion; 15 | 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | 19 | /** 20 | * @author kirito 21 | * @date 2021/3/28 22 | * @time 17:36 23 | * Echarts 的 Service 层 24 | */ 25 | @Slf4j 26 | @Service 27 | public class EchartsServiceImpl implements EchartsService { 28 | 29 | @Autowired 30 | private EchartsMapper echartsMapper; 31 | 32 | @Autowired 33 | private StringRedisTemplate stringRedisTemplate; 34 | 35 | private static final String INFO_FAIL = Status.INFO_FAIL.getEnMsg(); 36 | private static final String NOT_EXIST = Status.NOT_EXIST.getEnMsg(); 37 | 38 | /** 39 | * 获取数据 40 | * 41 | * @param datee 日期 42 | * @param token 令牌 43 | * @return java.util.Map 44 | */ 45 | @Override 46 | public Map getData(String datee, String token) { 47 | Map map = new HashMap<>(); 48 | if (stringRedisTemplate.hasKey(token)) { 49 | // 取出 Admin 登录的 card 50 | String card = stringRedisTemplate.opsForValue().get(token); 51 | String date = ""; 52 | if (datee == null || "".equals(datee)) { 53 | date = DateUtil.today(); 54 | } else { 55 | date = datee; 56 | } 57 | QueryWrapper echartsQueryWrapper = new QueryWrapper<>(); 58 | echartsQueryWrapper.eq("datee", date).eq("card", card); 59 | Echarts echarts = echartsMapper.selectOne(echartsQueryWrapper); 60 | if (echarts == null) { 61 | map.put("fail", NOT_EXIST); 62 | log.info("date: {}, card: {}, 此日暂无人员取件!", date, card); 63 | } else { 64 | EchartsVO echartsVO = TypeConversion.getData(echarts); 65 | map.put("result", echartsVO); 66 | log.info("date: {}, card: {}, echarts-data: {}", date, card, echartsVO.toString()); 67 | } 68 | } else { 69 | log.info("token: {} 获取 Echarts 数据失败,因为登录状态失效!", token); 70 | map.put("fail", INFO_FAIL); 71 | } 72 | return map; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/service/inter/SendService.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.service.inter; 2 | 3 | import per.kirito.pack.pojo.utilpojo.SendRequest; 4 | 5 | import java.util.Map; 6 | 7 | /** 8 | * @author kirito 9 | * @date 2021/1/4 10 | * @time 15:52 11 | * 寄件 Send 的 Service 层接口 12 | */ 13 | public interface SendService { 14 | 15 | /** 16 | * ----------------------------------------------------------------------------------------------------------------- 17 | * 流程相关 18 | */ 19 | 20 | /** 21 | * User 寄件下单 22 | * 23 | * @param request 寄件请求实体 24 | * @return java.util.Map 25 | */ 26 | Map sendPack(SendRequest request); 27 | 28 | /** 29 | * User 支付寄件 30 | * 31 | * @param id 学号 32 | * @param token 令牌 33 | * @return java.lang.String 34 | */ 35 | String sendPay(String id, String token); 36 | 37 | /** 38 | * Admin 确认寄件 39 | * 40 | * @param ids 快递单号 41 | * @param token 令牌 42 | * @return java.lang.String 43 | */ 44 | String sendConfirm(String ids, String token); 45 | 46 | /** 47 | * Admin 发出寄件 48 | * 49 | * @param ids 快递单号 50 | * @param token 令牌 51 | * @return java.lang.String 52 | */ 53 | String sendOut(String ids, String token); 54 | 55 | /** 56 | * User 取消寄件 57 | * 58 | * @param ids 快递单号 59 | * @param token 令牌 60 | * @return java.lang.String 61 | */ 62 | String sendCancel(String ids, String token); 63 | 64 | /** 65 | * ----------------------------------------------------------------------------------------------------------------- 66 | * User 相关 67 | */ 68 | 69 | /** 70 | * 分页方式获取 User 寄件集合 71 | * 72 | * @param json 参数{currentPage:当前页, pageSize:每页大小, token:令牌, org:快递公司, status:寄件状态, search:搜索} 73 | * @return java.util.Map 74 | */ 75 | Map getSendByUser(String json); 76 | 77 | /** 78 | * 获取 User 寄件数量 79 | * 80 | * @param token 令牌 81 | * @return java.util.Map 82 | */ 83 | Map getTotalByUser(String token); 84 | 85 | /** 86 | * ----------------------------------------------------------------------------------------------------------------- 87 | * Admin 相关 88 | */ 89 | 90 | /** 91 | * 分页方式获取 Admin 寄件集合 92 | * 93 | * @param json 参数{currentPage:当前页, pageSize:每页大小, token:令牌, status:寄件状态, search:搜索} 94 | * @return java.util.Map 95 | */ 96 | Map getSendByAdmin(String json); 97 | 98 | /** 99 | * 获取 Admin 寄件数量 100 | * 101 | * @param token 令牌 102 | * @return java.util.Map 103 | */ 104 | Map getTotalByAdmin(String token); 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/test/java/PackIdUtilTest.java: -------------------------------------------------------------------------------- 1 | import cn.hutool.core.date.DateUtil; 2 | import cn.hutool.core.io.file.FileWriter; 3 | import org.junit.Test; 4 | import per.kirito.pack.util.PackIdUtil; 5 | 6 | /** 7 | * author: kirito 8 | * date: 2021/3/10 9 | * time: 9:15 10 | */ 11 | public class PackIdUtilTest { 12 | 13 | @Test 14 | public void generateIdToFile() throws InterruptedException { 15 | writeToFile("中苑", 1000); 16 | } 17 | 18 | /** 19 | * 传入驿站地址,生成对应的快递单号并写入文件中 20 | * @param addr 驿站地址 21 | * @param quantity 生成总数量 22 | */ 23 | public static void writeToFile(String addr, int quantity) throws InterruptedException { 24 | System.out.println("==========开始生成 " + addr + " 的快递单号=========="); 25 | System.out.println(DateUtil.now()); 26 | long start = System.currentTimeMillis(); 27 | FileWriter writer = new FileWriter("D://pack/" + addr +".txt"); 28 | int count; 29 | switch (addr) { 30 | case "中苑": 31 | count = quantity / 3; 32 | for (int i = 0; i < count; i++) { 33 | String id = PackIdUtil.generate("ZTO"); 34 | writer.append(id + ","); 35 | Thread.sleep(100); 36 | } 37 | for (int i = 0; i < count; i++) { 38 | String id = PackIdUtil.generate("STO"); 39 | writer.append(id + ","); 40 | Thread.sleep(100); 41 | } 42 | for (int i = 0; i < count; i++) { 43 | String id = PackIdUtil.generate("YTO"); 44 | writer.append(id + ","); 45 | Thread.sleep(100); 46 | } 47 | break; 48 | case "西苑": 49 | count = quantity / 3; 50 | for (int i = 0; i < count; i++) { 51 | String id = PackIdUtil.generate("JD"); 52 | writer.append(id + ","); 53 | Thread.sleep(100); 54 | } 55 | for (int i = 0; i < count; i++) { 56 | String id = PackIdUtil.generate("SF"); 57 | writer.append(id + ","); 58 | Thread.sleep(100); 59 | } 60 | for (int i = 0; i < count; i++) { 61 | String id = PackIdUtil.generate("YD"); 62 | writer.append(id + ","); 63 | Thread.sleep(100); 64 | } 65 | break; 66 | case "北苑": 67 | count = quantity / 2; 68 | for (int i = 0; i < count; i++) { 69 | String id = PackIdUtil.generate("TT"); 70 | writer.append(id + ","); 71 | Thread.sleep(100); 72 | } 73 | for (int i = 0; i < count; i++) { 74 | String id = PackIdUtil.generate("EMS"); 75 | writer.append(id + ","); 76 | Thread.sleep(100); 77 | } 78 | break; 79 | default: 80 | count = quantity; 81 | for (int i = 0; i < count; i++) { 82 | String id = PackIdUtil.generate("ZTO"); 83 | writer.append(id + ","); 84 | Thread.sleep(100); 85 | } 86 | break; 87 | } 88 | long end = System.currentTimeMillis(); 89 | System.out.println("耗时: " + (end - start) + "ms"); 90 | System.out.println(DateUtil.now()); 91 | System.out.println("==========生成结束=========="); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/util/PackIdUtil.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.util; 2 | 3 | import java.util.Calendar; 4 | import java.util.Date; 5 | 6 | /** 7 | * @author kirito 8 | * @date 2020/12/23 9 | * @time 13:39 10 | * 校园快递,单号规则仅供参考,不完全与实际相符! 11 | * 中苑:中通、申通、圆通 12 | * 西苑:京东、顺丰、韵达 13 | * 北苑:天天、EMS 14 | */ 15 | public class PackIdUtil { 16 | 17 | /** 中通单号由14位数字组成,以7542开头,再生成10位数字 */ 18 | private static final String ZTO = "7542"; 19 | /** 申通单号由15位数字组成,以77307开头,再生成10位数字 */ 20 | private static final String STO = "77307"; 21 | /** 圆通单号由2位字母和13位数字组成,以YT开头,再生成13位数字 */ 22 | private static final String YTO = "YT"; 23 | 24 | /** 京东单号由2位字母和13位数字组成,以JD002开头,再生成10位数字 */ 25 | private static final String JD = "JD002"; 26 | /** 顺丰单号由2位字母和13位数字组成,以SF1开头,再生成12位数字 */ 27 | private static final String SF = "SF1"; 28 | /** 韵达单号由13位数字组成,以43110开头,再生成8位数字 */ 29 | private static final String YD = "43110"; 30 | 31 | /** 天天单号由14位数字组成,以888开头,再生成11位数字 */ 32 | private static final String TT = "888"; 33 | /** EMS单号由4位字母和9位数字组成,以EA开头,以CN结尾,再生成9位数字 */ 34 | private static final String EMS_BEGIN = "EA"; 35 | private static final String EMS_END = "CN"; 36 | 37 | /** 状态码:有错误,生成失败 */ 38 | private static final String STATUS_ERROR = "error"; 39 | 40 | /** 41 | * 根据当前日期获取前两位数字 42 | * 43 | * @param date 当前日期 44 | * @return java.lang.String 45 | */ 46 | public static String getTwoId(Date date) { 47 | Calendar calendar = Calendar.getInstance(); 48 | calendar.setTime(date); 49 | return String.valueOf(calendar.get(Calendar.DAY_OF_MONTH)); 50 | } 51 | 52 | /** 53 | * 根据索引值截取13位时间戳以获取剩余数字 54 | * 55 | * @param index 索引位置 56 | * @return java.lang.String 57 | */ 58 | public static String getLeftId(int index) { 59 | String timestamp = String.valueOf(System.currentTimeMillis()); 60 | return timestamp.substring(index); 61 | } 62 | 63 | /** 64 | * 传入快递公司生成取件码 65 | * 66 | * @param type 快递公司 67 | * @return java.lang.String 68 | */ 69 | public static String generate(String type) { 70 | String UUID = ""; 71 | Date date = new Date(); 72 | String two = getTwoId(date); 73 | switch (type) { 74 | case "ZTO": 75 | UUID += ZTO + two + getLeftId(5); 76 | break; 77 | case "STO": 78 | UUID += STO + two + getLeftId(5); 79 | break; 80 | case "YTO": 81 | UUID += YTO + two + getLeftId(2); 82 | break; 83 | case "JD": 84 | UUID += JD + two + getLeftId(5); 85 | break; 86 | case "SF": 87 | UUID += SF + two + getLeftId(3); 88 | break; 89 | case "YD": 90 | UUID += YD + two + getLeftId(7); 91 | break; 92 | case "TT": 93 | UUID += TT + two + getLeftId(4); 94 | break; 95 | case "EMS": 96 | UUID += EMS_BEGIN + two + getLeftId(6) + EMS_END; 97 | break; 98 | default: 99 | UUID = STATUS_ERROR; 100 | break; 101 | } 102 | return UUID; 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/service/impl/MailServiceImpl.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.service.impl; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.data.redis.core.StringRedisTemplate; 6 | import org.springframework.stereotype.Service; 7 | import per.kirito.pack.mapper.UserMapper; 8 | import per.kirito.pack.myenum.Status; 9 | import per.kirito.pack.util.SendMailUtil; 10 | import per.kirito.pack.service.inter.MailService; 11 | 12 | import javax.mail.MessagingException; 13 | import java.security.GeneralSecurityException; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import java.util.Objects; 17 | 18 | /** 19 | * @author kirito 20 | * @date 2021/1/12 21 | * @time 14:09 22 | * Mail 的 Service 层,MailService 接口的实现类 23 | */ 24 | @Slf4j 25 | @Service 26 | public class MailServiceImpl implements MailService { 27 | 28 | @Autowired 29 | private UserMapper userMapper; 30 | 31 | @Autowired 32 | private StringRedisTemplate stringRedisTemplate; 33 | 34 | @Autowired 35 | private SendMailUtil sendMailUtil; 36 | 37 | private static final String DO_SUCCESS = Status.DO_SUCCESS.getEnMsg(); 38 | private static final String DO_FAIL = Status.DO_FAIL.getEnMsg(); 39 | private static final String NOT_EXIST = Status.NOT_EXIST.getEnMsg(); 40 | private static final String LOGIN_TO_DO = Status.LOGIN_TO_DO.getEnMsg(); 41 | 42 | /** 43 | * 根据快递单号查询出收件人邮箱并发送取件通知邮件 44 | * 45 | * @param ids 快递单号 46 | * @param token 令牌 47 | * @return java.util.Map 48 | */ 49 | @Override 50 | public Map sendMail(String ids, String token) { 51 | Map map = new HashMap<>(); 52 | try { 53 | if (stringRedisTemplate.hasKey(token)) { 54 | String[] idArr = ids.split(","); 55 | int noExistCount = 0; 56 | for (String id : idArr) { 57 | Map result = userMapper.getMailById(id); 58 | String mail = result.get("mail"); 59 | if (mail != null && !Objects.equals(mail, "")) { 60 | String code = result.get("code"); 61 | String addr = result.get("addr"); 62 | String org = result.get("org"); 63 | sendMailUtil.sendMail(mail, addr, code, org); 64 | } else { 65 | noExistCount++; 66 | } 67 | } 68 | if (noExistCount > 0) { 69 | String result = "有 " + noExistCount + " 件快递通知取件失败,因为快递所属学生账号没有绑定邮箱!"; 70 | log.info("token: {} {}", token, result); 71 | map.put("result", result); 72 | } else { 73 | map.put("result", DO_SUCCESS); 74 | } 75 | } else { 76 | log.info("token: {} 发送通知邮件失败,因为登录状态失效!", token); 77 | map.put("result", LOGIN_TO_DO); 78 | } 79 | return map; 80 | } catch (MessagingException | GeneralSecurityException e) { 81 | log.error("error: {}", e.getMessage(), e); 82 | log.info("token: {} 发送通知邮件失败,因为发生了异常!", token); 83 | map.put("result", DO_FAIL); 84 | return map; 85 | } 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/util/SendMailUtil.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.util; 2 | 3 | import com.sun.mail.util.MailSSLSocketFactory; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Component; 7 | import per.kirito.pack.properties.MailProperties; 8 | 9 | import java.security.GeneralSecurityException; 10 | import java.util.Properties; 11 | 12 | import javax.mail.Authenticator; 13 | import javax.mail.Message; 14 | import javax.mail.MessagingException; 15 | import javax.mail.PasswordAuthentication; 16 | import javax.mail.Session; 17 | import javax.mail.Transport; 18 | import javax.mail.internet.InternetAddress; 19 | import javax.mail.internet.MimeMessage; 20 | 21 | /** 22 | * @author kirito 23 | * @date 2021/1/12 24 | * @time 14:17 25 | * 发送邮件工具类 26 | */ 27 | @Slf4j 28 | @Component 29 | public class SendMailUtil { 30 | 31 | @Autowired 32 | private MailProperties mailProperties; 33 | 34 | /** 35 | * 发送邮件 36 | * 37 | * @param mail 收件人邮箱 38 | * @param addr 驿站地址 39 | * @param code 取件码 40 | * @param org 快递公司 41 | */ 42 | public void sendMail(String mail, String addr, String code, String org) throws MessagingException, GeneralSecurityException { 43 | String senderMail = mailProperties.getSenderMail(); // 发件人电子邮箱 44 | String mailHost = mailProperties.getHost(); // 指定发送邮件的主机 45 | String password = mailProperties.getPassword(); // 发件邮箱的密钥授权码 46 | 47 | String to = mail; // 收件人电子邮箱 48 | // 获取系统属性 49 | Properties properties = System.getProperties(); 50 | properties.setProperty("mail.smtp.host", mailHost); 51 | properties.put("mail.smtp.auth", "true"); 52 | 53 | MailSSLSocketFactory sf = new MailSSLSocketFactory(); 54 | sf.setTrustAllHosts(true); 55 | properties.put("mail.smtp.ssl.enable", "true"); 56 | properties.put("mail.smtp.ssl.socketFactory", sf); 57 | 58 | // 获取默认 session 对象 59 | Session session = Session.getDefaultInstance(properties, new Authenticator() { 60 | 61 | @Override 62 | public PasswordAuthentication getPasswordAuthentication() { 63 | //发件人邮件用户名、授权码 64 | return new PasswordAuthentication(senderMail, password); 65 | } 66 | 67 | }); 68 | // 创建默认的 MimeMessage 对象 69 | MimeMessage message = new MimeMessage(session); 70 | // Set From: 头部头字段 71 | message.setFrom(new InternetAddress(senderMail)); 72 | // Set To: 头部头字段 73 | message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); 74 | // Set Subject: 头部头字段 75 | String sub = "[基于Web的校园快递管理系统]-取件通知"; 76 | message.setSubject(sub); 77 | // 设置消息体 78 | String text = "您好,您有一个" + org + "快递包裹已到达" + addr + "菜鸟驿站,请凭取件码" + code + "在19:00前尽早取!"; 79 | if ("".equals(code) || code == null) { 80 | text = "您好,您有一个" + org + "快递包裹已到达" + addr + "菜鸟驿站,但由于驿站快递爆满,请联系驿站管理员尽早取件!"; 81 | } 82 | message.setText(text); 83 | // 发送消息 84 | Transport.send(message); 85 | String dt = TypeConversion.getTime(); 86 | log.info("发送邮件:{}\tfrom: {}\tto: {}\tsub: {}\ttext: {}\tSend message successfully....", dt, senderMail, mail, sub, text); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/resources/mapper/SendMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | from_name, from_tel, from_addr, to_name, to_tel, to_addr, price, id, org, status, dt 7 | 8 | 9 | 10 | 50 | 51 | 52 | 80 | 81 | 82 | 93 | 94 | 95 | 101 | 102 | -------------------------------------------------------------------------------- /src/main/resources/logback-old.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | %date{yyyy-MM-dd HH:mm:ss} %highlight(%-5level) (%file:%line\)- %m%n 17 | 18 | UTF-8 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ${PATH}base.log 27 | 28 | 29 | 30 | 31 | 32 | ${PATH}base.%d.%i.log 33 | 34 | 30 35 | 36 | 37 | 10MB 38 | 39 | 40 | 41 | 42 | 43 | %d %p (%file:%line\)- %m%n 44 | 45 | 46 | UTF-8 47 | 48 | 49 | 50 | 51 | ${PATH}all.log 52 | 53 | ${PATH}all.%d.%i.log 54 | 30 55 | 56 | 10MB 57 | 58 | 59 | 60 | 61 | %d %p (%file:%line\)- %m%n 62 | 63 | UTF-8 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/util/PackUtil.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.util; 2 | 3 | import per.kirito.pack.myenum.Express; 4 | import per.kirito.pack.myenum.PackStatusEnum; 5 | import per.kirito.pack.myenum.Status; 6 | import per.kirito.pack.pojo.Pack; 7 | 8 | /** 9 | * @author kirito 10 | * @date 2020/12/23 11 | * @time 15:48 12 | * 包裹信息完善:自动生成取件码、更新取件状态、添加地址信息 13 | */ 14 | public class PackUtil { 15 | 16 | // 快递公司的中文名与英文名 17 | private static final String ZTO = String.valueOf(Express.中通); 18 | private static final String STO = String.valueOf(Express.申通); 19 | private static final String YTO = String.valueOf(Express.圆通); 20 | private static final String JD = String.valueOf(Express.京东); 21 | private static final String SF = String.valueOf(Express.顺丰); 22 | private static final String YD = String.valueOf(Express.韵达); 23 | private static final String TT = String.valueOf(Express.天天); 24 | private static final String EMS = String.valueOf(Express.EMS); 25 | 26 | // 所在驿站 27 | private static final String ADDR_ZY = "中苑"; 28 | private static final String ADDR_XY = "西苑"; 29 | private static final String ADDR_BY = "北苑"; 30 | 31 | // 驿站联系人 32 | private static final String CONT_ZY = "中苑快递员"; 33 | private static final String CONT_XY = "西苑快递员"; 34 | private static final String CONT_BY = "北苑快递员"; 35 | 36 | // 驿站联系方式 37 | private static final String TEL_ZY = "13300000001"; 38 | private static final String TEL_XY = "13300000002"; 39 | private static final String TEL_BY = "13300000003"; 40 | 41 | /** 42 | * 添加入站信息(入站时只有快递单号这一条信息) 43 | * 44 | * @param pack 入站快递实体 45 | * @return per.kirito.pack.pojo.Pack 46 | */ 47 | public static Pack addInfo(Pack pack) { 48 | String idAll = pack.getId(); 49 | String id = idAll.substring(0, 2); 50 | String org = ""; 51 | String addr = ""; 52 | String contPer = ""; 53 | String contTel = ""; 54 | // 根据快递单号前2位获取快递公司和驿站相关信息 55 | switch (id) { 56 | case "75": 57 | org = ZTO; 58 | addr = ADDR_ZY; 59 | contPer = CONT_ZY; 60 | contTel = TEL_ZY; 61 | break; 62 | case "77": 63 | org = STO; 64 | addr = ADDR_ZY; 65 | contPer = CONT_ZY; 66 | contTel = TEL_ZY; 67 | break; 68 | case "YT": 69 | org = YTO; 70 | addr = ADDR_ZY; 71 | contPer = CONT_ZY; 72 | contTel = TEL_ZY; 73 | break; 74 | case "JD": 75 | org = JD; 76 | addr = ADDR_XY; 77 | contPer = CONT_XY; 78 | contTel = TEL_XY; 79 | break; 80 | case "SF": 81 | org = SF; 82 | addr = ADDR_XY; 83 | contPer = CONT_XY; 84 | contTel = TEL_XY; 85 | break; 86 | case "43": 87 | org = YD; 88 | addr = ADDR_XY; 89 | contPer = CONT_XY; 90 | contTel = TEL_XY; 91 | break; 92 | case "88": 93 | org = TT; 94 | addr = ADDR_BY; 95 | contPer = CONT_BY; 96 | contTel = TEL_BY; 97 | break; 98 | case "EA": 99 | org = EMS; 100 | addr = ADDR_BY; 101 | contPer = CONT_BY; 102 | contTel = TEL_BY; 103 | break; 104 | default: 105 | break; 106 | } 107 | // 添加快递公司信息 108 | pack.setOrg(org); 109 | // 添加所在驿站信息 110 | pack.setAddr(addr); 111 | // 添加驿站联系人信息 112 | pack.setContName(contPer); 113 | // 添加驿站联系方式信息 114 | pack.setContTel(contTel); 115 | // 添加快递状态信息 116 | Pack newPack = updateStatus(pack, "入站"); 117 | String time = TypeConversion.getTime(); 118 | // 添加快递入站时间 119 | newPack.setStart(time); 120 | return newPack; 121 | } 122 | 123 | /** 124 | * 仅更新快递状态 125 | * 126 | * @param pack 快递实体 127 | * @param operate 操作码 128 | * @return per.kirito.pack.pojo.Pack 129 | */ 130 | public static Pack updateStatus(Pack pack, String operate) { 131 | if ("取件".equals(operate)) { 132 | pack.setStatus(PackStatusEnum.PACK_STATUS_0); 133 | } else if ("入站".equals(operate)) { 134 | pack.setStatus(PackStatusEnum.PACK_STATUS__1); 135 | } 136 | return pack; 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/controller/ExcelController.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.controller; 2 | 3 | import cn.hutool.core.io.IoUtil; 4 | import cn.hutool.poi.excel.ExcelUtil; 5 | import cn.hutool.poi.excel.ExcelWriter; 6 | import io.swagger.annotations.*; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.web.bind.annotation.*; 10 | import per.kirito.pack.pojo.Pack; 11 | import per.kirito.pack.service.inter.PackService; 12 | import per.kirito.pack.util.IpAddressUtil; 13 | 14 | import javax.servlet.ServletOutputStream; 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletResponse; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | /** 21 | * @author kirito 22 | * @date 2021/2/26 23 | * @time 9:38 24 | * Excel 的 Controller 层 25 | */ 26 | @Slf4j 27 | @Api(tags = {"Excel"}) 28 | // @CrossOrigin 29 | @RestController 30 | @RequestMapping(value = "/excel") 31 | public class ExcelController { 32 | 33 | @Autowired 34 | private PackService packService; 35 | 36 | /** 37 | * 导出 Excel 到输出流 38 | * 39 | * @param token 令牌 40 | * @param type 快递类型 41 | * @param response http 响应 42 | * @param request http 请求 43 | */ 44 | @ApiOperation(value = "下载 Excel", notes = "生成对应 Excel,返回输出流", httpMethod = "POST") 45 | @ApiResponses(value = { 46 | @ApiResponse(code = 200, message = "响应成功"), 47 | @ApiResponse(code = 201, message = "响应创建"), 48 | @ApiResponse(code = 401, message = "没有权限"), 49 | @ApiResponse(code = 403, message = "请求被拒绝"), 50 | @ApiResponse(code = 404, message = "资源不存在") 51 | }) 52 | @PostMapping(value = "") 53 | public void download( 54 | @ApiParam(required = true, name = "token", value = "token 令牌") @RequestParam(value = "token") String token, 55 | @ApiParam(required = true, name = "type", value = "获取类型") @RequestParam(value = "type") String type, 56 | HttpServletResponse response, 57 | HttpServletRequest request) { 58 | log.info("请求 URL[/excel];参数[token={}, type={}]", token, type); 59 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 60 | // 通过工具类创建 writer 61 | ExcelWriter writer = ExcelUtil.getWriter(true); 62 | writer.addHeaderAlias("id", "快递单号"); 63 | writer.addHeaderAlias("org", "快递公司"); 64 | writer.addHeaderAlias("perName", "收件人"); 65 | writer.addHeaderAlias("perTel", "收件手机号"); 66 | writer.addHeaderAlias("perAddr", "收件地址"); 67 | writer.addHeaderAlias("addr", "所在驿站"); 68 | writer.addHeaderAlias("code", "取件码"); 69 | writer.addHeaderAlias("contName", "驿站联系人"); 70 | writer.addHeaderAlias("contTel", "驿站联系方式"); 71 | writer.addHeaderAlias("status", "快递状态"); 72 | writer.addHeaderAlias("start", "入站时间"); 73 | writer.addHeaderAlias("end", "取件时间"); 74 | writer.addHeaderAlias("pick", "签收人"); 75 | 76 | ServletOutputStream out = null; 77 | try { 78 | String content = ""; 79 | List packs = new ArrayList<>(); 80 | switch (type) { 81 | case "all": 82 | content = "全部快递"; 83 | packs = packService.getAllPacksByExcelOfAdmin(token); 84 | break; 85 | case "is": 86 | content = "已取快递"; 87 | packs = packService.getIsPacksByExcelOfAdmin(token); 88 | break; 89 | case "no": 90 | content = "未取快递"; 91 | packs = packService.getNoPacksByExcelOfAdmin(token); 92 | break; 93 | default: 94 | packs = null; 95 | break; 96 | } 97 | // 合并单元格后的标题行,使用默认标题样式 98 | writer.merge(12, content); 99 | // 一次性写出内容,使用默认样式,强制输出标题 100 | writer.write(packs, true); 101 | 102 | response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"); 103 | // 弹出下载对话框的文件名,不能为中文,中文需要二次编码 104 | response.setHeader("Content-Disposition", "attachment;filename=" + type + ".xlsx"); 105 | 106 | out = response.getOutputStream(); 107 | writer.flush(out, true); 108 | log.info("生成 Excel 成功!"); 109 | } catch (Exception e) { 110 | log.error("error: {}", e.getMessage(), e); 111 | e.printStackTrace(); 112 | } finally { 113 | // 关闭 writer,释放内存 114 | writer.close(); 115 | IoUtil.close(out); 116 | } 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 基于web的校园快递管理系统 2 | 3 | ## 调试运行步骤 4 | 1. 所需环境:JDK1.8、MySQL 5.x.x、Redis、Maven、IDEA 5 | 1. MySQL 5.x.x 版本 导入 `./database/pack-prod-2400.sql` SQL文件; 6 | 2. 修改 `application.yml` 配置文件中的数据库 `url`、`username`、`password`; 7 | 3. 修改 `application.yml` 配置文件中的 Redis 的 `host`、`port`、`password`(Redis 密码可选项); 8 | 4. 如果 `8080` 端口被占用,修改 `application.yml` 配置文件中的 `server.port`; 9 | 5. SpringBoot 项目选择 `PackApplication` ,带参数[--jasypt.encryptor.password=${JASYPT_PASSWORD}]进行调试运行; 10 | 6. 浏览器输入 `127.0.0.1:8080/pack` 访问 `基于 Web 的校园快递管理系统`; 11 | 学生账号(用户名:2120172171 密码:123456) 12 | 驿站管理员账号1:(用户名:2101 密码:admin) 13 | 驿站管理员账号2:(用户名:2102 密码:admin) 14 | 驿站管理员账号3:(用户名:2103 密码:admin) 15 | 16 | ## 进度记录 17 | - 2020.12.29
18 | ```markdown 19 | 1. 实现学生/管理员的登录与登录,学生的注册与忘记密码 20 | 2. 实现查询当前驿站/学生所有的快递,并可以分页展示 21 | ``` 22 | - 2021.01.02
23 | ```markdown 24 | 1. 实现学生取件(本人取件和他人代取),并同步在相应快递信息上添加签收人 25 | ``` 26 | - 2021.01.08
27 | ```markdown 28 | 1. 完成修改密码时的验证码与图片生成 29 | 2. 完成修改密码与更新信息 30 | ``` 31 | - 2021.01.11
32 | ```markdown 33 | 1. 完成寄件逻辑,包括学生提交寄件表单、确认支付,管理员确认寄件、发出寄件 34 | 2. 完成 Aside 部分获取4中状态寄件数量并显示 35 | ``` 36 | - 2021.01.12
37 | ```markdown 38 | 1. 实现管理员邮件通知未取快递的收件人 39 | ``` 40 | - 2021.01.22
41 | ```markdown 42 | 1. 使用自定义配置文件完成邮件发送邮箱配置 43 | 2. 使用 jasypt 对 application.yml 中的配置进行加密 44 | 3. 开启 Druid 的 SQL 监控功能 45 | # 加密 46 | $ .\encrypt input=demo-input password=demo-pwd algorithm=PBEWithMD5AndDES 47 | 输出结果为:`av8dLw8htTU7k5AQaoMgGzI5KLaj9Glp` 48 | # 解密 49 | $ .\decrypt input="av8dLw8htTU7k5AQaoMgGzI5KLaj9Glp" password="demo-pwd" algorithm=PBEWithMD5AndDES 50 | 输出结果为: `demo-input` 51 | ``` 52 | - 2021.02.19
53 | ```markdown 54 | 1. 优化代码逻辑,在 NoPick 部分添加学生在根据快递单号取件 55 | 2. npm 打包文件放入 `static` 文件夹下 56 | ``` 57 | - 2021.02.22
58 | ```markdown 59 | 1. 完成后端数据筛选过滤;优化代码逻辑 60 | ``` 61 | - 2021.02.23
62 | ```markdown 63 | 1. 优化代码逻辑;添加单元测试;造取数据测试 64 | 2. 优化多参数方法为传入 JSON 字符串再转 Map 解析参数 65 | 3. 不同环境不同配置文件,并方便切换 66 | ``` 67 | - 2021.02.24
68 | ```markdown 69 | 1. 添加快递单号生成页面;完成快递状态的筛选过滤 70 | 2. 添加 swagger 使用,可查询 API;优化代码逻辑 71 | ``` 72 | - 2021.02.25
73 | ```markdown 74 | 1. 优化 swagger API;优化请求方法类型;优化代码逻辑 75 | 2. 实现 selection 多选处理 76 | ``` 77 | - 2021.02.26
78 | ```markdown 79 | 1. 优化 selection 多选处理;添加快递数据导出为 Excel 表格 80 | ``` 81 | - 2021.03.02
82 | ```markdown 83 | 1. 优化 Java Bean 构造模式 84 | 2. 采用 全字段 LIKE 查询实现搜索 85 | TODO:换用 elasticsearch 86 | ``` 87 | - 2021.03.03
88 | ```markdown 89 | 1. 添加日志记录;添加定时任务;优化代码逻辑 90 | ``` 91 | - 2021.03.03
92 | ```markdown 93 | 1. 优化日志 94 | ``` 95 | - 2021.03.07
96 | ```markdown 97 | 1. 优化代码逻辑;container 高度铺满 98 | ``` 99 | - 2021.03.08
100 | ```markdown 101 | 1. 添加 Jasypt 测试类;优化页面布局;优化日志记录 102 | ``` 103 | - 2021.03.09
104 | ```markdown 105 | 1. 优化依赖;优化代码逻辑 106 | ``` 107 | - 2021.03.10
108 | ```markdown 109 | 1. 优化代码逻辑;添加请求 IP 获取;页面走马灯添加图片 110 | 2. 优化日志记录;添加快递单号生成单元测试;优化配置文件 111 | ``` 112 | - 2021.03.11
113 | ```markdown 114 | 1. 优化前台表单验证 115 | ``` 116 | - 2021.03.19
117 | ```markdown 118 | 1. 添加加载 loading;优化代码结构 119 | ``` 120 | - 2021.03.28
121 | ```markdown 122 | 1. 添加取件 Echarts 图使用 123 | 2. 添加 live2d 看板娘 124 | ``` 125 | - 2021.03.29
126 | ```markdown 127 | 1. 优化路由结构 128 | 2. 修复 Echarts 数据更新 BUG 129 | ``` 130 | - 2021.03.31
131 | ```markdown 132 | 1. 更换 ORM 为 MyBatis-plus 133 | 2. 优化 RequestMapping 路径 134 | 3. 优化日志记录 135 | 4. 添加自定义错误页面 136 | ``` 137 | - 2021.04.01
138 | ```markdown 139 | 1. 优化代码结构 140 | 2. 添加 API 文档 141 | 3. 优化错误页面 142 | ``` 143 | - 2021.04.12
144 | ```markdown 145 | 1. 优化定时任务 146 | 2. 优化日志配置 147 | 3. 添加自定义 banner 148 | ``` 149 | 150 | 151 | 152 | ## 相关算法 153 | ### 学生登录退出的状态保存 154 | ### Vue 项目全局变量的存储与管理 155 | ### 取件码给予算法 156 | ```markdown 157 | 1. 如果驿站快递未满且还有取件码未被使用过,则根据未入站前驿站快递总数,使用取件码工具类生成的取件码,给予新入站的快递 158 | 2. 如果驿站快递已满,则当有快递被取出时,未有取件码的快递根据最旧入站时间给予其被取出的快递释放的取件码 159 | 3. 取件码工具类生成取件码,仅在当前驿站快递未满且最大取件码未被使用时才使用 160 | ``` 161 | 162 | 163 | 164 | ## 注意 165 | SQL 语句: 166 | ```mysql 167 | SELECT count(*) 未取快递数 168 | FROM t_pack 169 | WHERE per_tel = 'xxx' 170 | AND status = 1 171 | OR status = -1; 172 | ``` 173 | 实际执行的是: 174 | ```mysql 175 | SELECT count(*) 176 | FROM t_pack 177 | WHERE per_tel = 'xxx' 178 | AND status = 1; 179 | ``` 180 | + 181 | ```mysql 182 | SELECT count(*) 183 | FROM t_pack 184 | WHERE status = -1; 185 | ``` 186 | 导致结果不正确。 187 | 188 | ## 打包上线 189 | 1. 配置环境变量 190 | JASYPT_PASSWORD=[密钥] 191 | export JASYPT_PASSWORD 192 | 2. java -jar [jar包] --server.port=[端口] --jasypt.encryptor.password=${JASYPT_PASSWORD} 193 | 194 | ## 请求响应类型 195 | produces:它的作用是指定返回值类型,不但可以设置返回值类型还可以设定返回值的字符编码; 196 | consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html; 197 | 198 | ## elasticsearch 199 | 1. 前端 搜索 根据 输入内容 请求 服务器 200 | 2. 服务器根据索引请求 elasticsearch 获取数据 返回 前端 -------------------------------------------------------------------------------- /src/main/resources/static/static/js/manifest.3ad1d5771e9b13dbdad2.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///webpack/bootstrap 23d1634359c1bca65662"],"names":["parentJsonpFunction","window","chunkIds","moreModules","executeModules","moduleId","chunkId","result","i","resolves","length","installedChunks","push","Object","prototype","hasOwnProperty","call","modules","shift","__webpack_require__","s","installedModules","2","exports","module","l","m","c","d","name","getter","o","defineProperty","configurable","enumerable","get","n","__esModule","object","property","p","oe","err","console","error"],"mappings":"aACA,IAAAA,EAAAC,OAAA,aACAA,OAAA,sBAAAC,EAAAC,EAAAC,GAIA,IADA,IAAAC,EAAAC,EAAAC,EAAAC,EAAA,EAAAC,KACQD,EAAAN,EAAAQ,OAAoBF,IAC5BF,EAAAJ,EAAAM,GACAG,EAAAL,IACAG,EAAAG,KAAAD,EAAAL,GAAA,IAEAK,EAAAL,GAAA,EAEA,IAAAD,KAAAF,EACAU,OAAAC,UAAAC,eAAAC,KAAAb,EAAAE,KACAY,EAAAZ,GAAAF,EAAAE,IAIA,IADAL,KAAAE,EAAAC,EAAAC,GACAK,EAAAC,QACAD,EAAAS,OAAAT,GAEA,GAAAL,EACA,IAAAI,EAAA,EAAYA,EAAAJ,EAAAM,OAA2BF,IACvCD,EAAAY,IAAAC,EAAAhB,EAAAI,IAGA,OAAAD,GAIA,IAAAc,KAGAV,GACAW,EAAA,GAIA,SAAAH,EAAAd,GAGA,GAAAgB,EAAAhB,GACA,OAAAgB,EAAAhB,GAAAkB,QAGA,IAAAC,EAAAH,EAAAhB,IACAG,EAAAH,EACAoB,GAAA,EACAF,YAUA,OANAN,EAAAZ,GAAAW,KAAAQ,EAAAD,QAAAC,IAAAD,QAAAJ,GAGAK,EAAAC,GAAA,EAGAD,EAAAD,QAKAJ,EAAAO,EAAAT,EAGAE,EAAAQ,EAAAN,EAGAF,EAAAS,EAAA,SAAAL,EAAAM,EAAAC,GACAX,EAAAY,EAAAR,EAAAM,IACAhB,OAAAmB,eAAAT,EAAAM,GACAI,cAAA,EACAC,YAAA,EACAC,IAAAL,KAMAX,EAAAiB,EAAA,SAAAZ,GACA,IAAAM,EAAAN,KAAAa,WACA,WAA2B,OAAAb,EAAA,SAC3B,WAAiC,OAAAA,GAEjC,OADAL,EAAAS,EAAAE,EAAA,IAAAA,GACAA,GAIAX,EAAAY,EAAA,SAAAO,EAAAC,GAAsD,OAAA1B,OAAAC,UAAAC,eAAAC,KAAAsB,EAAAC,IAGtDpB,EAAAqB,EAAA,KAGArB,EAAAsB,GAAA,SAAAC,GAA8D,MAApBC,QAAAC,MAAAF,GAAoBA","file":"static/js/manifest.3ad1d5771e9b13dbdad2.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tvar parentJsonpFunction = window[\"webpackJsonp\"];\n \twindow[\"webpackJsonp\"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [], result;\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n \t\tif(executeModules) {\n \t\t\tfor(i=0; i < executeModules.length; i++) {\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = executeModules[i]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t};\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// objects to store loaded and loading chunks\n \tvar installedChunks = {\n \t\t2: 0\n \t};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"./\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 23d1634359c1bca65662"],"sourceRoot":""} -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import java.net.*; 18 | import java.io.*; 19 | import java.nio.channels.*; 20 | import java.util.Properties; 21 | 22 | public class MavenWrapperDownloader { 23 | 24 | private static final String WRAPPER_VERSION = "0.5.6"; 25 | /** 26 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 27 | */ 28 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 29 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 30 | 31 | /** 32 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 33 | * use instead of the default one. 34 | */ 35 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 36 | ".mvn/wrapper/maven-wrapper.properties"; 37 | 38 | /** 39 | * Path where the maven-wrapper.jar will be saved to. 40 | */ 41 | private static final String MAVEN_WRAPPER_JAR_PATH = 42 | ".mvn/wrapper/maven-wrapper.jar"; 43 | 44 | /** 45 | * Name of the property which should be used to override the default download url for the wrapper. 46 | */ 47 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 48 | 49 | public static void main(String args[]) { 50 | System.out.println("- Downloader started"); 51 | File baseDirectory = new File(args[0]); 52 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 53 | 54 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 55 | // wrapperUrl parameter. 56 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 57 | String url = DEFAULT_DOWNLOAD_URL; 58 | if (mavenWrapperPropertyFile.exists()) { 59 | FileInputStream mavenWrapperPropertyFileInputStream = null; 60 | try { 61 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 62 | Properties mavenWrapperProperties = new Properties(); 63 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 64 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 65 | } catch (IOException e) { 66 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 67 | } finally { 68 | try { 69 | if (mavenWrapperPropertyFileInputStream != null) { 70 | mavenWrapperPropertyFileInputStream.close(); 71 | } 72 | } catch (IOException e) { 73 | // Ignore ... 74 | } 75 | } 76 | } 77 | System.out.println("- Downloading from: " + url); 78 | 79 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 80 | if (!outputFile.getParentFile().exists()) { 81 | if (!outputFile.getParentFile().mkdirs()) { 82 | System.out.println( 83 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 84 | } 85 | } 86 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 87 | try { 88 | downloadFileFromURL(url, outputFile); 89 | System.out.println("Done"); 90 | System.exit(0); 91 | } catch (Throwable e) { 92 | System.out.println("- Error downloading"); 93 | e.printStackTrace(); 94 | System.exit(1); 95 | } 96 | } 97 | 98 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 99 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 100 | String username = System.getenv("MVNW_USERNAME"); 101 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 102 | Authenticator.setDefault(new Authenticator() { 103 | @Override 104 | protected PasswordAuthentication getPasswordAuthentication() { 105 | return new PasswordAuthentication(username, password); 106 | } 107 | }); 108 | } 109 | URL website = new URL(urlString); 110 | ReadableByteChannel rbc; 111 | rbc = Channels.newChannel(website.openStream()); 112 | FileOutputStream fos = new FileOutputStream(destination); 113 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 114 | fos.close(); 115 | rbc.close(); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/service/inter/PackService.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.service.inter; 2 | 3 | import per.kirito.pack.pojo.Pack; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * @author kirito 10 | * @date 2020/12/23 11 | * @time 15:35 12 | * Pack 的 Service 层接口 13 | */ 14 | public interface PackService { 15 | 16 | /** 17 | * 驿站管理员添加快递入站 18 | * 19 | * @param id 快递单号 20 | * @param token 令牌 21 | * @return java.lang.String 22 | */ 23 | String addPack(String id, String token); 24 | 25 | /** 26 | * User 进行取件请求,仅传入快递单号和 token 27 | * 28 | * @param ids 快递单号 29 | * @param token 令牌 30 | * @return java.lang.String 31 | */ 32 | String pickById(String ids, String token); 33 | 34 | /** 35 | * User 进行取件,必须传入驿站地址和取件码 36 | * 37 | * @param addr 驿站地址 38 | * @param code 取件码 39 | * @param token 令牌 40 | * @return java.lang.String 41 | */ 42 | String pickPackByUser(String addr, String code, String token); 43 | 44 | /** 45 | * Admin 进行取件,仅传入快递单号即可 46 | * 47 | * @param ids 快递单号 48 | * @param token 令牌 49 | * @return java.lang.String 50 | */ 51 | String pickPackByAdmin(String ids, String token); 52 | 53 | /** 54 | * 根据快递单号删除此快递 55 | * 56 | * @param ids 快递单号 57 | * @param token 令牌 58 | * @return java.lang.String 59 | */ 60 | String deletePacksById(String ids, String token); 61 | 62 | /** 63 | * ----------------------------------------------------------------------------------------------------------------- 64 | * User相关 65 | */ 66 | 67 | /** 68 | * 分页获取 User 所有的快递,包括已取出和未取出的快递;如果没有 token 令牌,则返回获取信息失败 69 | * 70 | * @param json 参数{currentPage:当前页, pageSize:每页大小, token:令牌, org:快递公司, addr:驿站地址, status:快递状态, search:搜索} 71 | * @return java.util.Map 72 | */ 73 | Map getUserPackByPage(String json); 74 | 75 | /** 76 | * 分页获取 User 已取出的快递;如果没有 token 令牌,则返回获取信息失败 77 | * 78 | * @param json 参数{currentPage:当前页, pageSize:每页大小, token:令牌, org:快递公司, search:搜索} 79 | * @return java.util.Map 80 | */ 81 | Map getUserIsPick(String json); 82 | 83 | /** 84 | * 分页获取 User 所未取出的快递, 无论有无取件码;如果没有 token 令牌,则返回获取信息失败 85 | * 86 | * @param json 参数{currentPage:当前页, pageSize:每页大小, token:令牌, org:快递公司, addr:驿站地址, status:快递状态, search:搜索} 87 | * @return java.util.Map 88 | */ 89 | Map getUserNoPick(String json); 90 | 91 | /** 92 | * 获取 User 所有快递总数、已取快递数量、未取快递数量 93 | * 94 | * @param token 令牌 95 | * @return java.util.Map 96 | */ 97 | Map getUserTotalNum(String token); 98 | 99 | /** 100 | * ----------------------------------------------------------------------------------------------------------------- 101 | * Admin相关 102 | */ 103 | 104 | /** 105 | * 分页获取 Admin 所有的快递,包括已取出和未取出的快递;如果没有 token 令牌,则返回获取信息失败 106 | * 107 | * @param json 参数{currentPage:当前页, pageSize:每页大小, token:令牌, org:快递公司, status:快递状态, search:搜索} 108 | * @return java.util.Map 109 | */ 110 | Map getAdminPackByPage(String json); 111 | 112 | /** 113 | * 分页获取当前驿站的已取出快递;如果没有 token 令牌,则返回获取信息失败 114 | * 115 | * @param json 参数{currentPage:当前页, pageSize:每页大小, token:令牌, org:快递公司, search:搜索} 116 | * @return java.util.Map 117 | */ 118 | Map getAdminIsPick(String json); 119 | 120 | /** 121 | * 分页获取当前驿站的未取出快递,无论有无取件码;如果没有 token 令牌,则返回获取信息失败 122 | * 123 | * @param json 参数{currentPage:当前页, pageSize:每页大小, token:令牌, org:快递公司, status:快递状态, search:搜索} 124 | * @return java.util.Map 125 | */ 126 | Map getAdminNoPick(String json); 127 | 128 | /** 129 | * 获取 Admin 所有快递总数、已取快递数量、未取快递数量 130 | * 131 | * @param token 令牌 132 | * @return java.util.Map 133 | */ 134 | Map getAdminTotalNum(String token); 135 | 136 | /** 137 | * 根据驿站地址和货架获取当前货架的所有快递 138 | * 139 | * @param token 令牌 140 | * @param shelf 货架 141 | * @return java.util.Map 142 | */ 143 | Map getShelfPack(String token, String shelf); 144 | 145 | /** 146 | * 获取不筛选不分页的驿站所有快递集合 147 | * 148 | * @param token 令牌 149 | * @return java.util.List 150 | */ 151 | List getAllPacksByExcelOfAdmin(String token); 152 | 153 | /** 154 | * 获取不筛选不分页的驿站已取快递集合 155 | * 156 | * @param token 令牌 157 | * @return java.util.List 158 | */ 159 | List getIsPacksByExcelOfAdmin(String token); 160 | 161 | /** 162 | * 获取不筛选不分页的驿站未取快递集合 163 | * 164 | * @param token 令牌 165 | * @return java.util.List 166 | */ 167 | List getNoPacksByExcelOfAdmin(String token); 168 | 169 | } 170 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | profiles: 3 | active: redmi 4 | 5 | application: 6 | name: pack 7 | 8 | datasource: 9 | type: com.alibaba.druid.pool.DruidDataSource 10 | # Druid 扩展属性配置 11 | druid: 12 | # driver-class-name: com.mysql.cj.jdbc.Driver 13 | # 初始化大小,最小,最大 14 | initial-size: 5 15 | min-idle: 5 16 | max-active: 20 17 | # 配置获取连接等待超时的时间(毫秒) 18 | max-wait: 60000 19 | # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 20 | time-between-eviction-runs-millis: 60000 21 | # 配置有一个连接在连接池中的最小生存时间,单位是毫秒 22 | min-evictable-idle-time-millis: 300000 23 | validation-query: SELECT 1 FROM DUAL 24 | validation-query-timeout: 60 25 | # 开启 Druid 监控功能 26 | filters: stat 27 | filter: 28 | stat: # 配置 StatFilter ,对应文档 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatFilter 29 | log-slow-sql: true # 开启慢查询记录 30 | slow-sql-millis: 5000 # 慢 SQL 的标准,单位:毫秒 31 | stat-view-servlet: # 配置 StatViewServlet ,对应文档 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE 32 | enabled: true # 是否开启 StatViewServlet 33 | # 允许所有 IP 访问 34 | allow: 35 | login-username: ENC(GuvZlNx3H+Wklc7oN10Gvg==) # 账号 36 | login-password: ENC(GuvZlNx3H+Wklc7oN10Gvg==) # 密码 37 | # web url 请求监控 38 | web-stat-filter: 39 | enabled: true 40 | exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" 41 | 42 | jpa: 43 | open-in-view: false 44 | 45 | server: 46 | servlet: 47 | context-path: /pack 48 | 49 | jasypt: 50 | # jasypt 配置项,对应 JasyptEncryptorConfigurationProperties 配置类 51 | encryptor: 52 | algorithm: PBEWithMD5AndDES # 加密算法 53 | 54 | logging: 55 | config: classpath:logback-spring.xml 56 | 57 | mybatis-plus: 58 | mapper-locations: classpath:/mapper/**.xml # 自定义 SQL XML 位置 59 | type-aliases-package: per.kirito.pack.pojo # MyBaits 别名包扫描路径 60 | typeEnumsPackage: per.kirito.pack.myenum # 扫描通用枚举 61 | configuration: 62 | log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 查看执行的 SQL 语句 63 | cache-enabled: false # 配置缓存,开发时关闭 64 | 65 | # 发送邮件邮箱配置 66 | mail: 67 | sender-mail: ENC(QrLyPQolo1eloonEfX9W4YbUdWDQjWfXA/iYMexdn+I=) # 发件人邮箱 68 | host: ENC(SbfpF4J7vqp29+QQKaneMlh29HtqedMP) # 发件邮箱主机 69 | password: ENC(SMS8MgNusP4+3pwqrWxBd8wECHz9FKOeyiW01VxSOj0=) # 发件邮箱密钥授权码 70 | 71 | desc: 72 | version: V1.0.0 73 | title: 基于Web的校园快递管理系统 74 | author: kirito 75 | mail: chen.yan17@foxmail.com 76 | license: Apache License 77 | 78 | --- 79 | # aorus 开发时配置 80 | spring: 81 | config: 82 | activate: 83 | on-profile: aorus 84 | 85 | datasource: 86 | # Druid 扩展属性配置 87 | druid: 88 | # 本地运行 89 | url: ENC(2Wd2mbkJXAwR5i3DC7j22b7BIeDAkZqJ6cW78hApNmGu9tSeyNQ/Shmh6V5KcK1Wefbu3U4L3FAyApoyaibkS9mcIWqgvXB5+9cWxBo3NoambDjFnyFSWiryKYYxMgIuFv2CcOOC/ZIjDgttfXYvkA==) 90 | username: ENC(yjWek0OXd+CjuX9pZAxLWg==) 91 | password: ENC(dTuy0Gxd4KKsfCzNTUj6vxWf4QRxEEBjv) 92 | driver-class-name: com.p6spy.engine.spy.P6SpyDriver 93 | 94 | # aorus 虚拟机 打包 95 | # url: jdbc:mysql://localhost:3306/pack?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8 96 | # username: demo 97 | # password: demo 98 | 99 | redis: 100 | # aorus 虚拟机 101 | host: 192.168.32.101 102 | port: 6379 103 | database: 0 104 | password: ENC(GuvZlNx3H+Wklc7oN10Gvg==) 105 | 106 | server: 107 | port: 8080 108 | 109 | --- 110 | # redmi 开发时配置 111 | spring: 112 | config: 113 | activate: 114 | on-profile: redmi 115 | 116 | datasource: 117 | # 本地运行 118 | url: ENC(2Wd2mbkJXAwR5i3DC7j22b7BIeDAkZqJ6cW78hApNmGu9tSeyNQ/Shmh6V5KcK1Wefbu3U4L3FAyApoyaibkS9mcIWqgvXB5+9cWxBo3NoambDjFnyFSWiryKYYxMgIuFv2CcOOC/ZIjDgttfXYvkA==) 119 | username: ENC(yjWek0OXd+CjuX9pZAxLWg==) 120 | password: ENC(gIyYN/pv3iTMmBRwsfQEbgYh1PheREBK) 121 | driver-class-name: com.p6spy.engine.spy.P6SpyDriver 122 | 123 | redis: 124 | # redmi 虚拟机 125 | host: 192.168.152.100 126 | port: 6379 127 | database: 0 128 | password: ENC(GuvZlNx3H+Wklc7oN10Gvg==) 129 | 130 | server: 131 | port: 8080 132 | 133 | --- 134 | # 阿里云 上线时配置 135 | spring: 136 | config: 137 | activate: 138 | on-profile: aliyun 139 | 140 | datasource: 141 | # Druid 扩展属性配置 142 | druid: 143 | url: ENC(SifCYFg8bF3bSwmbW+7+hLuwhljNuATKsf5hhB9WGksu1WclfeQkEum9p6Q92SBh4BEIUQUUfHbTjJMpRL20KjWWIOLz0g23XUE/Q/tjYQ4k4a0F9pyZyWeaRM/arV08/XQrlmwkZob1Ow0kZTmklQ==) 144 | username: ENC(1m2rfexUaIH0OobisFPkrQ==) 145 | password: ENC(mo8o7YpS61JKNvbuHO/lOJ7fYa0gO0azLVDpj4EfcUI=) 146 | driver-class-name: com.p6spy.engine.spy.P6SpyDriver 147 | 148 | redis: 149 | host: ENC(4ETL9BRRKsLENtf8X78r7NJFwK9kgpdg) 150 | port: 6379 151 | database: 0 152 | password: ENC(fSj8ksT+W6V31qQo7jQjXtY98PrTnA8PJfZGjnWD6Wc=) 153 | 154 | server: 155 | port: 15000 156 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/util/TypeConversion.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.util; 2 | 3 | import per.kirito.pack.pojo.Echarts; 4 | import per.kirito.pack.pojo.utilpojo.EchartsDO; 5 | import per.kirito.pack.pojo.utilpojo.EchartsVO; 6 | 7 | import java.math.BigInteger; 8 | import java.security.MessageDigest; 9 | import java.security.NoSuchAlgorithmException; 10 | import java.util.ArrayList; 11 | import java.util.Date; 12 | import java.text.ParseException; 13 | import java.text.SimpleDateFormat; 14 | import java.util.List; 15 | 16 | /** 17 | * @author kirito 18 | * @date 2020/11/17 19 | * @time 11:02 20 | * 类型转换 21 | */ 22 | public class TypeConversion { 23 | 24 | /** 25 | * 获取当前时间(yyyy-MM-dd HH:mm:ss) 26 | * 27 | * @return java.lang.String 28 | */ 29 | public static String getTime() { 30 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 31 | Date date = new Date(); 32 | String dateStr = sdf.format(date); 33 | return dateStr; 34 | } 35 | 36 | /** 37 | * MD5 加密字符串,不可逆 38 | * 39 | * @param plainText 加密内容 40 | * @return java.lang.String 41 | */ 42 | public static String stringToMD5(String plainText) { 43 | byte[] secretBytes = null; 44 | int length = 32; 45 | String meta = "0"; 46 | try { 47 | secretBytes = MessageDigest.getInstance("md5").digest(plainText.getBytes()); 48 | } catch (NoSuchAlgorithmException e) { 49 | throw new RuntimeException("没有这个md5算法!"); 50 | } 51 | String md5code = new BigInteger(1, secretBytes).toString(16); 52 | for (int i = 0; i < length - md5code.length(); i++) { 53 | md5code = meta + md5code; 54 | } 55 | return md5code; 56 | } 57 | 58 | /** 59 | * ["中通","申通","圆通"] 转换为 中通,申通,圆通 60 | * 61 | * @param array json 数组 62 | * @return java.lang.String 63 | */ 64 | public static String arrayToString(String array) { 65 | // 正则表达式 66 | String regEx = "[\n`~!@#$%^&*()+=|{}':;'\\[\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。, 、?\"]"; 67 | String org = array.replaceAll(regEx, ""); 68 | return org; 69 | } 70 | 71 | /** 72 | * "-1,0,1" 转换为 [-1,0,1] 73 | * 即字符串转为整型数组 74 | * 75 | * @param statusStr 字符串 76 | * @return java.lang.Integer[] 77 | */ 78 | public static Integer[] stringToIntegerArray(String statusStr) { 79 | if ("".equals(statusStr) || statusStr == null || "null".equals(statusStr)) { 80 | // 筛选框重置情况下,filter 值为 [] 即传入空字符串 81 | return new Integer[]{2}; 82 | } 83 | String[] array = statusStr.split(","); 84 | Integer[] status = new Integer[array.length]; 85 | for (int i = 0; i < array.length; i++) { 86 | status[i] = Integer.valueOf(array[i]); 87 | } 88 | return status; 89 | } 90 | 91 | /** 92 | * Echarts 数据库实体 -> 页面所需数据 93 | * @param echarts Echarts 实体 94 | * @return per.kirito.pack.pojo.utilpojo.EchartsVO 95 | */ 96 | public static EchartsVO getData(Echarts echarts) { 97 | EchartsVO echartsVO; 98 | 99 | EchartsDO echartsDO; 100 | List data = new ArrayList<>(); 101 | if (echarts.getNine() > 0) { 102 | echartsDO = new EchartsDO(echarts.getNine(), "9点"); 103 | data.add(echartsDO); 104 | } 105 | if (echarts.getTen() > 0) { 106 | echartsDO = new EchartsDO(echarts.getTen(), "10点"); 107 | data.add(echartsDO); 108 | } 109 | if (echarts.getEleven() > 0) { 110 | echartsDO = new EchartsDO(echarts.getEleven(), "11点"); 111 | data.add(echartsDO); 112 | } 113 | if (echarts.getTwelve() > 0) { 114 | echartsDO = new EchartsDO(echarts.getTwelve(), "12点"); 115 | data.add(echartsDO); 116 | } 117 | if (echarts.getThirteen() > 0) { 118 | echartsDO = new EchartsDO(echarts.getThirteen(), "13点"); 119 | data.add(echartsDO); 120 | } 121 | if (echarts.getFourteen() > 0) { 122 | echartsDO = new EchartsDO(echarts.getFourteen(), "14点"); 123 | data.add(echartsDO); 124 | } 125 | if (echarts.getFifteen() > 0) { 126 | echartsDO = new EchartsDO(echarts.getFifteen(), "15点"); 127 | data.add(echartsDO); 128 | } 129 | if (echarts.getSixteen() > 0) { 130 | echartsDO = new EchartsDO(echarts.getSixteen(), "16点"); 131 | data.add(echartsDO); 132 | } 133 | if (echarts.getSeventeen() > 0) { 134 | echartsDO = new EchartsDO(echarts.getSeventeen(), "17点"); 135 | data.add(echartsDO); 136 | } 137 | if (echarts.getEighteen() > 0) { 138 | echartsDO = new EchartsDO(echarts.getEighteen(), "18点"); 139 | data.add(echartsDO); 140 | } 141 | if (echarts.getNineteen() > 0) { 142 | echartsDO = new EchartsDO(echarts.getNineteen(), "19点"); 143 | data.add(echartsDO); 144 | } 145 | if (data != null && data.size() == 0) { 146 | echartsDO = new EchartsDO(0, "暂无人员取件"); 147 | data.add(echartsDO); 148 | } 149 | 150 | List count = new ArrayList<>(); 151 | count.add(echarts.getNine()); 152 | count.add(echarts.getTen()); 153 | count.add(echarts.getEleven()); 154 | count.add(echarts.getTwelve()); 155 | count.add(echarts.getThirteen()); 156 | count.add(echarts.getFourteen()); 157 | count.add(echarts.getFifteen()); 158 | count.add(echarts.getSixteen()); 159 | count.add(echarts.getSeventeen()); 160 | count.add(echarts.getEighteen()); 161 | count.add(echarts.getNineteen()); 162 | 163 | echartsVO = new EchartsVO(); 164 | echartsVO.setData(data); 165 | echartsVO.setCount(count); 166 | return echartsVO; 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/mapper/PackMapper.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.baomidou.mybatisplus.core.metadata.IPage; 5 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 6 | import org.apache.ibatis.annotations.Param; 7 | import per.kirito.pack.pojo.Pack; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author kirito 13 | * @date 2020/12/23 14 | * @time 15:24 15 | * Pack 的 Mapper 接口 16 | */ 17 | public interface PackMapper extends BaseMapper { 18 | 19 | /** 20 | * ----------------------------------------------------------------------------------------------------------------- 21 | * User 相关 22 | */ 23 | 24 | /** 25 | * 根据包裹手机号获取对应用户所有快递数 26 | * 27 | * @param phone 快递收件人手机号 28 | * @return int 29 | */ 30 | int getUserAllTotalNum(String phone); 31 | 32 | /** 33 | * 根据包裹手机号获取对应用户已取快递数 34 | * 35 | * @param phone 快递收件人手机号 36 | * @return int 37 | */ 38 | int getUserIsTotalNum(String phone); 39 | 40 | /** 41 | * 根据包裹手机号获取对应用户未取快递数 42 | * 43 | * @param phone 快递收件人手机号 44 | * @return int 45 | */ 46 | int getUserNoTotalNum(String phone); 47 | 48 | /** 49 | * 根据 User 学号查询出该 User 所有快递集合 50 | * 51 | * @param page 分页 52 | * @param card 学号 53 | * @param org 快递公司 54 | * @param addr 驿站地址 55 | * @param status 快递状态 56 | * @param search 搜索 57 | * @return com.baomidou.mybatisplus.extension.plugins.pagination.Page 58 | */ 59 | Page getUserPacks(IPage page, String card, String org, String addr, Integer[] status, String search); 60 | 61 | /** 62 | * 根据 User 学号查询出该 User 已取快递集合 63 | * 64 | * @param page 分页 65 | * @param card 学号 66 | * @param org 快递公司 67 | * @param search 搜索 68 | * @return com.baomidou.mybatisplus.extension.plugins.pagination.Page 69 | */ 70 | Page getUserIsPick(IPage page, String card, String org, String search); 71 | 72 | /** 73 | * 根据 User 学号查询出该 User 未取快递集合 74 | * 75 | * @param page 分页 76 | * @param card 学号 77 | * @param org 快递公司 78 | * @param addr 驿站地址 79 | * @param status 快递状态 80 | * @param search 搜索 81 | * @return com.baomidou.mybatisplus.extension.plugins.pagination.Page 82 | */ 83 | Page getUserNoPick(IPage page, String card, String org, String addr, Integer[] status, String search); 84 | 85 | /** 86 | * ----------------------------------------------------------------------------------------------------------------- 87 | * Admin 相关 88 | */ 89 | 90 | /** 91 | * 根据驿站地址获取该驿站所有快递数 92 | * 93 | * @param addr 驿站地址 94 | * @return int 95 | */ 96 | int getAdminAllTotalNum(String addr); 97 | 98 | /** 99 | * 根据驿站地址获取该驿站已取快递数 100 | * 101 | * @param addr 驿站地址 102 | * @return int 103 | */ 104 | int getAdminIsTotalNum(String addr); 105 | 106 | /** 107 | * 根据驿站地址获取该驿站未取快递数 108 | * 109 | * @param addr 驿站地址 110 | * @return int 111 | */ 112 | int getAdminNoTotalNum(String addr); 113 | 114 | /** 115 | * 根据 Admin 编号查询出所在驿站的所有快递集合 116 | * 117 | * @param page 分页 118 | * @param card Admin 编号 119 | * @param org 快递公司 120 | * @param status 快递状态 121 | * @param search 搜索 122 | * @return com.baomidou.mybatisplus.extension.plugins.pagination.Page 123 | */ 124 | Page getAdminPacks(IPage page, String card, String org, Integer[] status, String search); 125 | 126 | /** 127 | * 根据 Admin 编号查询出所在驿站已取快递集合 128 | * 129 | * @param page 分页 130 | * @param card Admin 编号 131 | * @param org 快递公司 132 | * @param search 搜索 133 | * @return com.baomidou.mybatisplus.extension.plugins.pagination.Page 134 | */ 135 | Page getAdminIsPick(IPage page, String card, String org, String search); 136 | 137 | /** 138 | * 根据 Admin 编号查询出所在驿站未取快递集合 139 | * 140 | * @param page 分页 141 | * @param card Admin 编号 142 | * @param org 快递公司 143 | * @param status 快递状态 144 | * @param search 搜索 145 | * @return com.baomidou.mybatisplus.extension.plugins.pagination.Page 146 | */ 147 | Page getAdminNoPick(IPage page, String card, String org, Integer[] status, String search); 148 | 149 | /** 150 | * 根据驿站地址取出当前驿站未有取件码的快递中入站时间最早的快递 151 | * 152 | * @param addr 驿站地址 153 | * @return per.kirito.pack.pojo.Pack 154 | */ 155 | Pack getPackByStartMin(String addr); 156 | 157 | /** 158 | * 根据驿站地址和货架获取当前货架的所有快递 159 | * 160 | * @param card 编号 161 | * @param shelf 货架 162 | * @return java.util.List 163 | */ 164 | List getShelfPack(@Param("card") String card, @Param("shelf") String shelf); 165 | 166 | /** 167 | * 获取不筛选不分页的驿站所有快递集合 168 | * 169 | * @param card 编号 170 | * @return java.util.List 171 | */ 172 | List getAllPacksByExcelOfAdmin(@Param("card") String card); 173 | 174 | /** 175 | * 获取不筛选不分页的驿站已取快递集合 176 | * 177 | * @param card 编号 178 | * @return java.util.List 179 | */ 180 | List getIsPacksByExcelOfAdmin(@Param("card") String card); 181 | 182 | /** 183 | * 获取不筛选不分页的驿站未取快递集合 184 | * 185 | * @param card 编号 186 | * @return java.util.List 187 | */ 188 | List getNoPacksByExcelOfAdmin(@Param("card") String card); 189 | 190 | } 191 | -------------------------------------------------------------------------------- /src/main/resources/static/live2d-widget/waifu.css: -------------------------------------------------------------------------------- 1 | #waifu-toggle { 2 | background-color: #fa0; 3 | border-radius: 5px; 4 | bottom: 66px; 5 | color: #fff; 6 | cursor: pointer; 7 | font-size: 12px; 8 | left: 0; 9 | margin-left: -100px; 10 | padding: 5px 2px 5px 5px; 11 | position: fixed; 12 | transition: margin-left 1s; 13 | width: 60px; 14 | writing-mode: vertical-rl; 15 | } 16 | 17 | #waifu-toggle.waifu-toggle-active { 18 | margin-left: -50px; 19 | } 20 | 21 | #waifu-toggle.waifu-toggle-active:hover { 22 | margin-left: -30px; 23 | } 24 | 25 | #waifu { 26 | bottom: -1000px; 27 | left: 0; 28 | line-height: 0; 29 | margin-bottom: -10px; 30 | position: fixed; 31 | transform: translateY(3px); 32 | transition: transform .3s ease-in-out, bottom 3s ease-in-out; 33 | z-index: 1; 34 | } 35 | 36 | #waifu:hover { 37 | transform: translateY(0); 38 | } 39 | 40 | #waifu-tips { 41 | animation: shake 50s ease-in-out 5s infinite; 42 | background-color: rgba(236, 217, 188, .5); 43 | border: 1px solid rgba(224, 186, 140, .62); 44 | border-radius: 12px; 45 | box-shadow: 0 3px 15px 2px rgba(191, 158, 118, .2); 46 | font-size: 14px; 47 | line-height: 24px; 48 | margin: -30px 20px; 49 | min-height: 70px; 50 | opacity: 0; 51 | overflow: hidden; 52 | padding: 5px 10px; 53 | position: absolute; 54 | text-overflow: ellipsis; 55 | transition: opacity 1s; 56 | width: 250px; 57 | word-break: break-all; 58 | } 59 | 60 | #waifu-tips.waifu-tips-active { 61 | opacity: 1; 62 | transition: opacity .2s; 63 | } 64 | 65 | #waifu-tips span { 66 | color: #0099cc; 67 | } 68 | 69 | #waifu #live2d { 70 | cursor: grab; 71 | height: 300px; 72 | position: relative; 73 | width: 300px; 74 | } 75 | 76 | #waifu #live2d:active { 77 | cursor: grabbing; 78 | } 79 | 80 | #waifu-tool { 81 | color: #aaa; 82 | opacity: 0; 83 | position: absolute; 84 | right: -10px; 85 | top: 70px; 86 | transition: opacity 1s; 87 | } 88 | 89 | #waifu:hover #waifu-tool { 90 | opacity: 1; 91 | } 92 | 93 | #waifu-tool span { 94 | color: #7b8c9d; 95 | cursor: pointer; 96 | display: block; 97 | line-height: 30px; 98 | text-align: center; 99 | transition: color .3s; 100 | } 101 | 102 | #waifu-tool span:hover { 103 | color: #0684bd; /* #34495e */ 104 | } 105 | 106 | @keyframes shake { 107 | 2% { 108 | transform: translate(.5px, -1.5px) rotate(-.5deg); 109 | } 110 | 111 | 4% { 112 | transform: translate(.5px, 1.5px) rotate(1.5deg); 113 | } 114 | 115 | 6% { 116 | transform: translate(1.5px, 1.5px) rotate(1.5deg); 117 | } 118 | 119 | 8% { 120 | transform: translate(2.5px, 1.5px) rotate(.5deg); 121 | } 122 | 123 | 10% { 124 | transform: translate(.5px, 2.5px) rotate(.5deg); 125 | } 126 | 127 | 12% { 128 | transform: translate(1.5px, 1.5px) rotate(.5deg); 129 | } 130 | 131 | 14% { 132 | transform: translate(.5px, .5px) rotate(.5deg); 133 | } 134 | 135 | 16% { 136 | transform: translate(-1.5px, -.5px) rotate(1.5deg); 137 | } 138 | 139 | 18% { 140 | transform: translate(.5px, .5px) rotate(1.5deg); 141 | } 142 | 143 | 20% { 144 | transform: translate(2.5px, 2.5px) rotate(1.5deg); 145 | } 146 | 147 | 22% { 148 | transform: translate(.5px, -1.5px) rotate(1.5deg); 149 | } 150 | 151 | 24% { 152 | transform: translate(-1.5px, 1.5px) rotate(-.5deg); 153 | } 154 | 155 | 26% { 156 | transform: translate(1.5px, .5px) rotate(1.5deg); 157 | } 158 | 159 | 28% { 160 | transform: translate(-.5px, -.5px) rotate(-.5deg); 161 | } 162 | 163 | 30% { 164 | transform: translate(1.5px, -.5px) rotate(-.5deg); 165 | } 166 | 167 | 32% { 168 | transform: translate(2.5px, -1.5px) rotate(1.5deg); 169 | } 170 | 171 | 34% { 172 | transform: translate(2.5px, 2.5px) rotate(-.5deg); 173 | } 174 | 175 | 36% { 176 | transform: translate(.5px, -1.5px) rotate(.5deg); 177 | } 178 | 179 | 38% { 180 | transform: translate(2.5px, -.5px) rotate(-.5deg); 181 | } 182 | 183 | 40% { 184 | transform: translate(-.5px, 2.5px) rotate(.5deg); 185 | } 186 | 187 | 42% { 188 | transform: translate(-1.5px, 2.5px) rotate(.5deg); 189 | } 190 | 191 | 44% { 192 | transform: translate(-1.5px, 1.5px) rotate(.5deg); 193 | } 194 | 195 | 46% { 196 | transform: translate(1.5px, -.5px) rotate(-.5deg); 197 | } 198 | 199 | 48% { 200 | transform: translate(2.5px, -.5px) rotate(.5deg); 201 | } 202 | 203 | 50% { 204 | transform: translate(-1.5px, 1.5px) rotate(.5deg); 205 | } 206 | 207 | 52% { 208 | transform: translate(-.5px, 1.5px) rotate(.5deg); 209 | } 210 | 211 | 54% { 212 | transform: translate(-1.5px, 1.5px) rotate(.5deg); 213 | } 214 | 215 | 56% { 216 | transform: translate(.5px, 2.5px) rotate(1.5deg); 217 | } 218 | 219 | 58% { 220 | transform: translate(2.5px, 2.5px) rotate(.5deg); 221 | } 222 | 223 | 60% { 224 | transform: translate(2.5px, -1.5px) rotate(1.5deg); 225 | } 226 | 227 | 62% { 228 | transform: translate(-1.5px, .5px) rotate(1.5deg); 229 | } 230 | 231 | 64% { 232 | transform: translate(-1.5px, 1.5px) rotate(1.5deg); 233 | } 234 | 235 | 66% { 236 | transform: translate(.5px, 2.5px) rotate(1.5deg); 237 | } 238 | 239 | 68% { 240 | transform: translate(2.5px, -1.5px) rotate(1.5deg); 241 | } 242 | 243 | 70% { 244 | transform: translate(2.5px, 2.5px) rotate(.5deg); 245 | } 246 | 247 | 72% { 248 | transform: translate(-.5px, -1.5px) rotate(1.5deg); 249 | } 250 | 251 | 74% { 252 | transform: translate(-1.5px, 2.5px) rotate(1.5deg); 253 | } 254 | 255 | 76% { 256 | transform: translate(-1.5px, 2.5px) rotate(1.5deg); 257 | } 258 | 259 | 78% { 260 | transform: translate(-1.5px, 2.5px) rotate(.5deg); 261 | } 262 | 263 | 80% { 264 | transform: translate(-1.5px, .5px) rotate(-.5deg); 265 | } 266 | 267 | 82% { 268 | transform: translate(-1.5px, .5px) rotate(-.5deg); 269 | } 270 | 271 | 84% { 272 | transform: translate(-.5px, .5px) rotate(1.5deg); 273 | } 274 | 275 | 86% { 276 | transform: translate(2.5px, 1.5px) rotate(.5deg); 277 | } 278 | 279 | 88% { 280 | transform: translate(-1.5px, .5px) rotate(1.5deg); 281 | } 282 | 283 | 90% { 284 | transform: translate(-1.5px, -.5px) rotate(-.5deg); 285 | } 286 | 287 | 92% { 288 | transform: translate(-1.5px, -1.5px) rotate(1.5deg); 289 | } 290 | 291 | 94% { 292 | transform: translate(.5px, .5px) rotate(-.5deg); 293 | } 294 | 295 | 96% { 296 | transform: translate(2.5px, -.5px) rotate(-.5deg); 297 | } 298 | 299 | 98% { 300 | transform: translate(-1.5px, -1.5px) rotate(-.5deg); 301 | } 302 | 303 | 0%, 100% { 304 | transform: translate(0, 0) rotate(0); 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /src/main/resources/static/live2d-widget/README.md: -------------------------------------------------------------------------------- 1 | # Live2D Widget 2 | 3 | ![](https://forthebadge.com/images/badges/built-with-love.svg) 4 | ![](https://forthebadge.com/images/badges/uses-html.svg) 5 | ![](https://forthebadge.com/images/badges/made-with-javascript.svg) 6 | ![](https://forthebadge.com/images/badges/contains-cat-gifs.svg) 7 | ![](https://forthebadge.com/images/badges/powered-by-electricity.svg) 8 | ![](https://forthebadge.com/images/badges/makes-people-smile.svg) 9 | 10 | ## 特性 Feature 11 | 12 | 在网页中添加 Live2D 看板娘。兼容 PJAX,支持无刷新加载。 13 | Add Live2D widget to web page. Compatible with PJAX. 14 | 15 | **警告:本项目使用了大量 ES6 语法,不支持 IE 11 等老旧浏览器。** 16 | **WARNING: This project does not support legacy browsers such as IE 11.** 17 | 18 | ## 示例 Demo 19 | 20 | 在[米米的博客](https://zhangshuqiao.org)的左下角可查看效果。(注:以下人物模型仅供展示之用,本仓库并不包含任何模型。) 21 | 22 | 23 | 24 | 你也可以在允许的范围内进行二次开发,这里有一些示例 25 | 26 | - [demo.html](https://mi.js.org/live2d-widget/demo/demo.html) ,展现基础效果 27 | - [login.html](https://mi.js.org/live2d-widget/demo/login.html) ,仿 NPM 的登陆界面 28 | 29 | ## 依赖 Dependencies 30 | 31 | 本插件需要 Font Awesome (v4 或 v5) 图标支持,请确保相关样式表已在页面中加载。以 Font Awesome v4 为例,请在 `` 中加入: 32 | Font Awesome (v4 or v5) is required for this plugin. Take Font Awesome v4 as an example, please add the following in ``: 33 | ```xml 34 | 35 | ``` 36 | 否则图标将无法正常显示。(如果网页中已经加载了任何版本的 Font Awesome,就不要重复加载了) 37 | 38 | ## 使用 Usage 39 | 40 | 将这一行代码加入 `` 或 ``,即可展现出效果: 41 | ```xml 42 | 43 | ``` 44 | 如果网站启用了 PJAX,由于看板娘不必每页刷新,因此要注意将相关脚本放到 PJAX 刷新区域之外。 45 | 46 | 换句话说,如果你是小白,或者只需要最基础的功能,就只用把这一行代码,连同前面加载 Font Awesome 的一行代码,一起放到 html 的 `` 中即可。 47 | 对于用各种模版引擎(例如 Nunjucks,Jinja 或者 PHP)生成的页面,也要自行修改,方法类似,只是可能略为麻烦。以 [Hexo](https://hexo.io) 为例,需要在主题相关的 ejs 或 njk 模版中正确配置路径,才可以加载。 48 | 49 | **但是!我们强烈推荐自己进行配置,否则很多功能是不完整的,并且可能产生问题!** 50 | 如果你有兴趣自己折腾的话,请看下面的详细说明。 51 | 52 | ### Using CDN 53 | 54 | 要自定义有关内容,可以把这个仓库 Fork 一份,然后进行修改。这时,使用方法对应地变为 55 | ```xml 56 | 57 | ``` 58 | 将此处的 `username` 替换为你的 GitHub 用户名。为了使 CDN 的内容正常刷新,需要创建新的 git tag 并推送至 GitHub 仓库中,否则此处的 `@latest` 仍然指向更新前的文件。此外 CDN 本身存在缓存,因此改动可能需要一定的时间生效。相关文档: 59 | - [Git Basics - Tagging](https://git-scm.com/book/en/v2/Git-Basics-Tagging) 60 | - [Managing releases in a repository](https://help.github.com/en/github/administering-a-repository/managing-releases-in-a-repository) 61 | 62 | ### Self-host 63 | 64 | 你也可以直接把这些文件放到服务器上,而不是通过 CDN 加载。 65 | 66 | - 如果你能够通过 `ssh` 访问你的主机,请把整个仓库克隆到服务器上。执行: 67 | ```bash 68 | cd /path/to/your/webroot 69 | # Clone this repository 70 | git clone https://github.com/stevenjoezhang/live2d-widget.git 71 | ``` 72 | - 如果你的主机无法用 `ssh` 连接(例如一般的虚拟主机),请选择 `Download ZIP`,然后通过 `ftp` 等方式上传到主机上,再解压到网站的目录下。 73 | - 如果你是通过 Hexo 等工具部署的静态博客,请在博客源文件(即 `source`)目录下,执行前述的 `git clone` 命令。重新部署博客时,相关文件就会自动上传到对应的路径下。为了避免这些文件被 Hexo 插件错误地修改,可能需要设置 `skip_render`。 74 | 75 | 这样,整个项目就可以通过你的服务器 IP 或者域名从公网访问了。不妨试试能否正常地通过浏览器打开 `autoload.js` 和 `live2d.min.js` 等文件,并确认这些文件的内容是完整和正确的。 76 | 一切正常的话,接下来修改一些配置就行了。(需要通过服务器上的文本编辑器修改;你也可以先在本地完成这一步骤,再上传到服务器上) 77 | 修改 `autoload.js` 中的常量 `live2d_path` 为 `live2d-widget` 这一目录的 URL。比如说,如果你能够通过 78 | ``` 79 | https://example.com/path/to/live2d-widget/live2d.min.js 80 | ``` 81 | 访问到 `live2d.min.js`,那么就把 `live2d_path` 的值修改为 82 | ``` 83 | https://example.com/path/to/live2d-widget/ 84 | ``` 85 | 路径末尾的 `/` 一定要加上。具体可以参考 `autoload.js` 内的注释。 86 | 完成后,在你要添加看板娘的界面加入 87 | ```xml 88 | 89 | ``` 90 | 就可以加载了。 91 | 92 | ## 后端 API 93 | 94 | `initWidget` 方法接受名为 `apiPath` 和 `cdnPath` 的参数,两者设置其中一项即可。其中 `apiPath` 为后端 API 的 URL,可以自行搭建,并增加模型(需要修改的内容比较多,此处不再赘述)。而 `cdnPath` 则是通过 jsDelivr 这样的 CDN 服务加载资源,更加稳定。 95 | 96 | ## 目录结构 Files 97 | 98 | - `waifu-tips.js` 包含了按钮和对话框的逻辑; 99 | - `waifu-tips.json` 中定义了触发条件(`selector`,CSS 选择器)和触发时显示的文字(`text`); 100 | - `waifu.css` 是看板娘的样式表。 101 | 102 | 源文件是对 Hexo 的 [NexT 主题](http://github.com/next-theme/hexo-theme-next)有效的,为了适用于你自己的网页,可能需要自行修改,或增加新内容。 103 | **警告:作者不对包括但不限于 `waifu-tips.js` 和 `waifu-tips.json` 文件中的内容负责,请自行确保它们是合适的。** 104 | 105 | 如果有任何疑问,欢迎提 Issue。如果有任何修改建议,欢迎提 Pull Request。 106 | 107 | ## 鸣谢 Thanks 108 | 109 | BrowserStack Logo 110 | 111 | > 感谢 BrowserStack 容许我们在真实的浏览器中测试此项目。 112 | > Thanks to [BrowserStack](https://www.browserstack.com/) for providing the infrastructure that allows us to test in real browsers! 113 | 114 | 115 | 116 | > 感谢 jsDelivr 提供的 CDN 服务。 117 | > Thanks jsDelivr for providing public CDN service. 118 | 119 | 代码自这篇博文魔改而来: 120 | https://www.fghrsh.net/post/123.html 121 | 122 | 点击看板娘的纸飞机按钮时,会出现一个彩蛋,这来自于 [WebsiteAsteroids](http://www.websiteasteroids.com)。 123 | 124 | ## 更多 More 125 | 126 | 更多内容可以参考: 127 | https://imjad.cn/archives/lab/add-dynamic-poster-girl-with-live2d-to-your-blog-02 128 | https://github.com/xiazeyu/live2d-widget.js 129 | https://github.com/summerscar/live2dDemo 130 | 131 | 关于后端 API 模型: 132 | https://github.com/fghrsh/live2d_api 133 | https://github.com/xiazeyu/live2d-widget-models 134 | https://github.com/xiaoski/live2d_models_collection 135 | 136 | 除此之外,还有桌面版本: 137 | https://github.com/amorist/platelet 138 | https://github.com/akiroz/Live2D-Widget 139 | https://github.com/zenghongtu/PPet 140 | https://github.com/LikeNeko/L2dPetForMac 141 | 142 | 以及 Wallpaper Engine: 143 | https://github.com/guansss/nep-live2d 144 | 145 | ## 许可证 License 146 | 147 | Released under the GNU General Public License v3 148 | http://www.gnu.org/licenses/gpl-3.0.html 149 | 150 | 本仓库并不包含任何模型,用作展示的所有 Live2D 模型、图片、动作数据等版权均属于其原作者,仅供研究学习,不得用于商业用途。 151 | 152 | Live2D 官方网站: 153 | https://www.live2d.com/en/ 154 | https://live2d.github.io 155 | 156 | Live2D Cubism Core は Live2D Proprietary Software License で提供しています。 157 | https://www.live2d.com/eula/live2d-proprietary-software-license-agreement_en.html 158 | Live2D Cubism Components は Live2D Open Software License で提供しています。 159 | http://www.live2d.com/eula/live2d-open-software-license-agreement_en.html 160 | 161 | > The terms and conditions do prohibit modification, but obfuscating in `live2d.min.js` would not be considered illegal modification. 162 | 163 | https://community.live2d.com/discussion/140/webgl-developer-licence-and-javascript-question 164 | 165 | ## 更新 Update 166 | 167 | 2018年10月31日,由 fghrsh 提供的原 API 停用,请更新至新地址。参考文章: 168 | https://www.fghrsh.net/post/170.html 169 | 170 | 2020年1月1日起,本项目不再依赖于 jQuery。 171 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/util/CheckCodeUtil.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.util; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import sun.misc.BASE64Decoder; 5 | import sun.misc.BASE64Encoder; 6 | 7 | import javax.imageio.ImageIO; 8 | import java.awt.*; 9 | import java.awt.geom.AffineTransform; 10 | import java.awt.image.BufferedImage; 11 | import java.io.*; 12 | import java.util.Arrays; 13 | import java.util.Random; 14 | 15 | /** 16 | * @author kirito 17 | * @date 2021/1/8 18 | * @time 10:51 19 | * 验证码工具类 20 | */ 21 | @Slf4j 22 | public class CheckCodeUtil { 23 | 24 | private static Random random = new Random(); 25 | 26 | /** 27 | * 获取随机颜色 28 | * 29 | * @param fc 最小值 30 | * @param bc 最大色 31 | * @return java.awt.Color 32 | */ 33 | private static Color getRandColor(int fc, int bc) { 34 | if (fc > 255) { 35 | fc = 255; 36 | } 37 | if (bc > 255) { 38 | bc = 255; 39 | } 40 | int r = fc + random.nextInt(bc - fc); 41 | int g = fc + random.nextInt(bc - fc); 42 | int b = fc + random.nextInt(bc - fc); 43 | return new Color(r, g, b); 44 | } 45 | 46 | /** 47 | * 获取随机颜色的整型值 48 | * 49 | * @return int 50 | */ 51 | private static int getRandomIntColor() { 52 | int[] rgb = getRandomRgb(); 53 | int color = 0; 54 | for (int c : rgb) { 55 | color = color << 8; 56 | color = color | c; 57 | } 58 | return color; 59 | } 60 | 61 | /** 62 | * 获取随机颜色的 RGB 值 63 | * 64 | * @return int[] 65 | */ 66 | private static int[] getRandomRgb() { 67 | int[] rgb = new int[3]; 68 | for (int i = 0; i < 3; i++) { 69 | rgb[i] = random.nextInt(255); 70 | } 71 | return rgb; 72 | } 73 | 74 | /** 75 | * 图片扭曲 76 | * 77 | * @param g 图形 78 | * @param w1 横轴扭曲像素 79 | * @param h1 纵轴扭曲像素 80 | * @param color 颜色 81 | */ 82 | private static void shear(Graphics g, int w1, int h1, Color color) { 83 | shearX(g, w1, h1, color); 84 | shearY(g, w1, h1, color); 85 | } 86 | 87 | /** 88 | * x 轴方向扭曲 89 | * 90 | * @param g 图形 91 | * @param w1 横轴扭曲像素 92 | * @param h1 纵轴扭曲像素 93 | * @param color 颜色 94 | */ 95 | private static void shearX(Graphics g, int w1, int h1, Color color) { 96 | int period = random.nextInt(2); 97 | boolean borderGap = true; 98 | int frames = 1; 99 | int phase = random.nextInt(2); 100 | for (int i = 0; i < h1; i++) { 101 | double d = (double) (period >> 1) 102 | * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames); 103 | g.copyArea(0, i, w1, 1, (int) d, 0); 104 | if (borderGap) { 105 | g.setColor(color); 106 | g.drawLine((int) d, i, 0, i); 107 | g.drawLine((int) d + w1, i, w1, i); 108 | } 109 | } 110 | } 111 | 112 | /** 113 | * y 轴方向扭曲 114 | * 115 | * @param g 图形 116 | * @param w1 横轴扭曲像素 117 | * @param h1 纵轴扭曲像素 118 | * @param color 颜色 119 | */ 120 | private static void shearY(Graphics g, int w1, int h1, Color color) { 121 | int period = random.nextInt(40) + 10; // 50; 122 | boolean borderGap = true; 123 | int frames = 20; 124 | int phase = 7; 125 | for (int i = 0; i < w1; i++) { 126 | double d = (double) (period >> 1) 127 | * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames); 128 | g.copyArea(i, 0, 1, h1, 0, (int) d); 129 | if (borderGap) { 130 | g.setColor(color); 131 | g.drawLine(i, (int) d, i, 0); 132 | g.drawLine(i, (int) d + h1, i, h1); 133 | } 134 | } 135 | } 136 | 137 | /** 138 | * 生成指定长度的随机数字和字母 139 | * 140 | * @param length 验证码长度 141 | * @return java.lang.String 142 | */ 143 | public static String getStringRandom(int length) { 144 | String val = ""; 145 | Random random = new Random(); 146 | for (int i = 0; i < length; i++) { 147 | String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num"; 148 | switch (charOrNum) { 149 | case "char": 150 | int temp = random.nextInt(2) % 2 == 0 ? 65 : 97; 151 | val += (char) (random.nextInt(26) + temp); 152 | break; 153 | case "num": 154 | val += String.valueOf(random.nextInt(10)); 155 | break; 156 | default: 157 | break; 158 | } 159 | } 160 | return val; 161 | } 162 | 163 | /** 164 | * 获取 Base64 编码的验证码图片 165 | * 166 | * @param w 图片宽度 167 | * @param h 图片高度 168 | * @param code 验证码 169 | * @return java.lang.String 170 | */ 171 | public static String imageToBase64(int w, int h, String code) throws Exception { 172 | int verifySize = code.length(); 173 | BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); 174 | Random rand = new Random(); 175 | Graphics2D g2 = image.createGraphics(); 176 | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 177 | Color[] colors = new Color[5]; 178 | Color[] colorSpaces = new Color[]{Color.WHITE, Color.CYAN, Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA, 179 | Color.ORANGE, Color.PINK, Color.YELLOW}; 180 | float[] fractions = new float[colors.length]; 181 | for (int i = 0; i < colors.length; i++) { 182 | colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)]; 183 | fractions[i] = rand.nextFloat(); 184 | } 185 | Arrays.sort(fractions); 186 | 187 | g2.setColor(Color.GRAY); // 设置边框色 188 | g2.fillRect(0, 0, w, h); 189 | 190 | Color c = getRandColor(200, 250); 191 | g2.setColor(c); // 设置背景色 192 | g2.fillRect(0, 2, w, h - 4); 193 | 194 | // 绘制干扰线 195 | Random random = new Random(); 196 | g2.setColor(getRandColor(160, 200)); // 设置线条的颜色 197 | for (int i = 0; i < 20; i++) { 198 | int x = random.nextInt(w - 1); 199 | int y = random.nextInt(h - 1); 200 | int xl = random.nextInt(6) + 1; 201 | int yl = random.nextInt(12) + 1; 202 | g2.drawLine(x, y, x + xl + 40, y + yl + 20); 203 | } 204 | 205 | // 添加噪点 206 | float yawpRate = 0.05f; // 噪声率 207 | int area = (int) (yawpRate * w * h); 208 | for (int i = 0; i < area; i++) { 209 | int x = random.nextInt(w); 210 | int y = random.nextInt(h); 211 | int rgb = getRandomIntColor(); 212 | image.setRGB(x, y, rgb); 213 | } 214 | 215 | shear(g2, w, h, c); // 使图片扭曲 216 | 217 | g2.setColor(getRandColor(100, 160)); 218 | int fontSize = h - 4; 219 | Font font = new Font("Arial", Font.ITALIC, fontSize); 220 | g2.setFont(font); 221 | char[] chars = code.toCharArray(); 222 | for (int i = 0; i < verifySize; i++) { 223 | AffineTransform affine = new AffineTransform(); 224 | affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1), 225 | (w / verifySize) * i + fontSize / 2, h / 2); 226 | g2.setTransform(affine); 227 | g2.drawChars(chars, i, 1, ((w - 10) / verifySize) * i + 5, h / 2 + fontSize / 2 - 10); 228 | } 229 | g2.dispose(); 230 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 231 | ImageIO.write(image, "png", baos); 232 | return new BASE64Encoder().encode(baos.toByteArray()); 233 | } 234 | 235 | /** 236 | * 将 Base64 位编码的图片进行解码,并保存到指定目录 237 | * 238 | * @param base64 Base64 编码的验证码图片 239 | * @param fileName 指定保存目录 240 | */ 241 | public static void base64ToImage(String base64, String fileName) { 242 | BASE64Decoder decoder = new BASE64Decoder(); 243 | try { 244 | File file = new File(fileName); 245 | FileOutputStream write = new FileOutputStream(file); 246 | byte[] decoderBytes = decoder.decodeBuffer(base64); 247 | write.write(decoderBytes); 248 | write.close(); 249 | } catch (IOException e) { 250 | log.error("error: {}", e.getMessage(), e); 251 | log.info("生成验证码图片失败,因为发生了异常!"); 252 | } 253 | } 254 | 255 | } 256 | -------------------------------------------------------------------------------- /src/main/resources/static/live2d-widget/waifu-tips.json: -------------------------------------------------------------------------------- 1 | { 2 | "mouseover": [{ 3 | "selector": "#waifu #live2d", 4 | "text": ["干嘛呢你,快把手拿开~~", "鼠…鼠标放错地方了!", "你要干嘛呀?", "喵喵喵?", "怕怕(ノ≧∇≦)ノ", "非礼呀!救命!", "这样的话,只能使用武力了!", "我要生气了哦", "不要动手动脚的!", "真…真的是不知羞耻!", "Hentai!"] 5 | }, { 6 | "selector": "#waifu-tool .fa-comment", 7 | "text": ["猜猜我要说些什么?", "我从青蛙王子那里听到了不少人生经验。"] 8 | }, { 9 | "selector": "#waifu-tool .fa-paper-plane", 10 | "text": ["要不要来玩飞机大战?", "这个按钮上写着「不要点击」。", "怎么,你想来和我玩个游戏?", "听说这样可以蹦迪!"] 11 | }, { 12 | "selector": "#waifu-tool .fa-user-circle", 13 | "text": ["你是不是不爱人家了呀,呜呜呜~", "要见见我的姐姐嘛?", "想要看我妹妹嘛?", "要切换看板娘吗?"] 14 | }, { 15 | "selector": "#waifu-tool .fa-street-view", 16 | "text": ["喜欢换装 PLAY 吗?", "这次要扮演什么呢?", "变装!", "让我们看看接下来会发生什么!"] 17 | }, { 18 | "selector": "#waifu-tool .fa-camera-retro", 19 | "text": ["你要给我拍照呀?一二三~茄子~", "要不,我们来合影吧!", "保持微笑就好了~"] 20 | }, { 21 | "selector": "#waifu-tool .fa-info-circle", 22 | "text": ["想要知道更多关于我的事么?", "这里记录着我搬家的历史呢。", "你想深入了解我什么呢?"] 23 | }, { 24 | "selector": "#waifu-tool .fa-times", 25 | "text": ["到了要说再见的时候了吗?", "呜呜 QAQ 后会有期……", "不要抛弃我呀……", "我们,还能再见面吗……", "哼,你会后悔的!"] 26 | }, { 27 | "selector": ".menu-item-home a", 28 | "text": ["点击前往首页,想回到上一页可以使用浏览器的后退功能哦。", "点它就可以回到首页啦!", "回首页看看吧。"] 29 | }, { 30 | "selector": ".menu-item-about a", 31 | "text": ["你想知道我家主人是谁吗?", "这里有一些关于我家主人的秘密哦,要不要看看呢?", "发现主人出没地点!"] 32 | }, { 33 | "selector": ".menu-item-tags a", 34 | "text": ["点击就可以看文章的标签啦!", "点击来查看所有标签哦。"] 35 | }, { 36 | "selector": ".menu-item-categories a", 37 | "text": ["文章都分类好啦~", "点击来查看文章分类哦。"] 38 | }, { 39 | "selector": ".menu-item-archives a", 40 | "text": ["翻页比较麻烦吗,那就来看看文章归档吧。", "文章目录都整理在这里啦!"] 41 | }, { 42 | "selector": ".menu-item-friends a", 43 | "text": ["这是我的朋友们哦ヾ(◍°∇°◍)ノ゙", "要去大佬们的家看看吗?", "要去拜访一下我的朋友们吗?"] 44 | }, { 45 | "selector": ".menu-item-search a", 46 | "text": ["找不到想看的内容?搜索看看吧!", "在找什么东西呢,需要帮忙吗?"] 47 | }, { 48 | "selector": ".menu-item a", 49 | "text": ["快看看这里都有什么呢?"] 50 | }, { 51 | "selector": ".site-author", 52 | "text": ["我家主人好看吗?", "这是我家主人(*´∇`*)"] 53 | }, { 54 | "selector": ".site-state", 55 | "text": ["这是文章的统计信息~", "要不要点进去看看?"] 56 | }, { 57 | "selector": ".feed-link a", 58 | "text": ["这里可以使用 RSS 订阅呢!", "利用 feed 订阅器,就能快速知道博客有没有更新了呢。"] 59 | }, { 60 | "selector": ".cc-opacity, .post-copyright-author", 61 | "text": ["要记得规范转载哦。", "所有文章均采用 CC BY-NC-SA 4.0 许可协议~", "转载前要先注意下文章的版权协议呢。"] 62 | }, { 63 | "selector": ".links-of-author", 64 | "text": ["这里是主人的常驻地址哦。", "这里有主人的联系方式!"] 65 | }, { 66 | "selector": "#qrcode", 67 | "text": ["手机扫一下就能继续看,很方便呢~", "扫一扫,打开新世界的大门!"] 68 | }, { 69 | "selector": ".fancybox img, img.medium-zoom-image", 70 | "text": ["点击图片可以放大呢!"] 71 | }, { 72 | "selector": ".copy-btn", 73 | "text": ["代码可以直接点击复制哟。"] 74 | }, { 75 | "selector": ".highlight .table-container, .gist", 76 | "text": ["GitHub!我是新手!", "PHP 是最好的语言!"] 77 | }, { 78 | "selector": "a[href^='mailto']", 79 | "text": ["邮件我会及时回复的!", "点击就可以发送邮件啦~"] 80 | }, { 81 | "selector": "a[href^='/tags/']", 82 | "text": ["要去看看 {text} 标签么?", "点它可以查看此标签下的所有文章哟!"] 83 | }, { 84 | "selector": "a[href^='/categories/']", 85 | "text": ["要去看看 {text} 分类么?", "点它可以查看此分类下的所有文章哟!"] 86 | }, { 87 | "selector": ".post-title-link", 88 | "text": ["要看看 {text} 这篇文章吗?"] 89 | }, { 90 | "selector": "a[rel='contents']", 91 | "text": ["点击来阅读全文哦。"] 92 | }, { 93 | "selector": "a[itemprop='discussionUrl']", 94 | "text": ["要去看看评论吗?"] 95 | }, { 96 | "selector": ".beian a", 97 | "text": ["我也是有户口的人哦。", "我的主人可是遵纪守法的好主人。"] 98 | }, { 99 | "selector": ".container a[href^='http'], .nav-link .nav-text", 100 | "text": ["要去看看 {text} 么?", "去 {text} 逛逛吧。", "到 {text} 看看吧。"] 101 | }, { 102 | "selector": ".back-to-top", 103 | "text": ["点它就可以回到顶部啦!", "又回到最初的起点~", "要回到开始的地方么?"] 104 | }, { 105 | "selector": ".reward-container", 106 | "text": ["我是不是棒棒哒~快给我点赞吧!", "要打赏我嘛?好期待啊~", "主人最近在吃土呢,很辛苦的样子,给他一些钱钱吧~"] 107 | }, { 108 | "selector": "#wechat", 109 | "text": ["这是我的微信二维码~"] 110 | }, { 111 | "selector": "#alipay", 112 | "text": ["这是我的支付宝哦!"] 113 | }, { 114 | "selector": "#bitcoin", 115 | "text": ["这是我的比特币账号!"] 116 | }, { 117 | "selector": "#needsharebutton-postbottom .btn", 118 | "text": ["好东西要让更多人知道才行哦。", "觉得文章有帮助的话,可以分享给更多需要的朋友呢。"] 119 | }, { 120 | "selector": ".need-share-button_weibo", 121 | "text": ["微博?来分享一波喵!"] 122 | }, { 123 | "selector": ".need-share-button_wechat", 124 | "text": ["分享到微信吧!"] 125 | }, { 126 | "selector": ".need-share-button_douban", 127 | "text": ["分享到豆瓣好像也不错!"] 128 | }, { 129 | "selector": ".need-share-button_qqzone", 130 | "text": ["QQ 空间,一键转发,耶~"] 131 | }, { 132 | "selector": ".need-share-button_twitter", 133 | "text": ["Twitter?好像是不存在的东西?"] 134 | }, { 135 | "selector": ".need-share-button_facebook", 136 | "text": ["emmm…FB 好像也是不存在的东西?"] 137 | }, { 138 | "selector": ".post-nav-item a[rel='next']", 139 | "text": ["来看看下一篇文章吧。", "点它可以看下一篇文章哦!", "要翻到下一篇文章吗?"] 140 | }, { 141 | "selector": ".post-nav-item a[rel='prev']", 142 | "text": ["来看看上一篇文章吧。", "点它可以看上一篇文章哦!", "要翻到上一篇文章吗?"] 143 | }, { 144 | "selector": ".extend.next", 145 | "text": ["去下一页看看吧。", "点它可以前进哦!", "要翻到下一页吗?"] 146 | }, { 147 | "selector": ".extend.prev", 148 | "text": ["去上一页看看吧。", "点它可以后退哦!", "要翻到上一页吗?"] 149 | }, { 150 | "selector": "input.vnick", 151 | "text": ["该怎么称呼你呢?", "留下你的尊姓大名!"] 152 | }, { 153 | "selector": ".vmail", 154 | "text": ["留下你的邮箱,不然就是无头像人士了!", "记得设置好 Gravatar 头像哦!", "为了方便通知你最新消息,一定要留下邮箱!"] 155 | }, { 156 | "selector": ".vlink", 157 | "text": ["快快告诉我你的家在哪里,好让我去参观参观!"] 158 | }, { 159 | "selector": ".veditor", 160 | "text": ["想要去评论些什么吗?", "要说点什么吗?", "觉得博客不错?快来留言和主人交流吧!"] 161 | }, { 162 | "selector": ".vcontrol a", 163 | "text": ["你会不会熟练使用 Markdown 呀?", "使用 Markdown 让评论更美观吧~"] 164 | }, { 165 | "selector": ".vemoji-btn", 166 | "text": ["要插入一个萌萌哒的表情吗?", "要来一发表情吗?"] 167 | }, { 168 | "selector": ".vpreview-btn", 169 | "text": ["要预览一下你的发言吗?", "快看看你的评论有多少负熵!"] 170 | }, { 171 | "selector": ".vsubmit", 172 | "text": ["评论没有审核,要对自己的发言负责哦~", "要提交了吗,请耐心等待回复哦~"] 173 | }, { 174 | "selector": ".vcontent", 175 | "text": ["哇,快看看这个精彩评论!", "如果有疑问,请尽快留言哦~"] 176 | }], 177 | "click": [{ 178 | "selector": "#waifu #live2d", 179 | "text": ["是…是不小心碰到了吧…", "萝莉控是什么呀?", "你看到我的小熊了吗?", "再摸的话我可要报警了!⌇●﹏●⌇", "110 吗,这里有个变态一直在摸我(ó﹏ò。)", "不要摸我了,我会告诉老婆来打你的!", "干嘛动我呀!小心我咬你!", "别摸我,有什么好摸的!"] 180 | }, { 181 | "selector": ".veditor", 182 | "text": ["要吐槽些什么呢?", "一定要认真填写喵~", "有什么想说的吗?"] 183 | }, { 184 | "selector": ".vsubmit", 185 | "text": ["输入验证码就可以提交评论啦~"] 186 | }], 187 | "seasons": [{ 188 | "date": "01/01", 189 | "text": "元旦了呢,新的一年又开始了,今年是{year}年~" 190 | }, { 191 | "date": "02/14", 192 | "text": "又是一年情人节,{year}年找到对象了嘛~" 193 | }, { 194 | "date": "03/08", 195 | "text": "今天是国际妇女节!" 196 | }, { 197 | "date": "03/12", 198 | "text": "今天是植树节,要保护环境呀!" 199 | }, { 200 | "date": "04/01", 201 | "text": "悄悄告诉你一个秘密~今天是愚人节,不要被骗了哦~" 202 | }, { 203 | "date": "05/01", 204 | "text": "今天是五一劳动节,计划好假期去哪里了吗~" 205 | }, { 206 | "date": "06/01", 207 | "text": "儿童节了呢,快活的时光总是短暂,要是永远长不大该多好啊…" 208 | }, { 209 | "date": "09/03", 210 | "text": "中国人民抗日战争胜利纪念日,铭记历史、缅怀先烈、珍爱和平、开创未来。" 211 | }, { 212 | "date": "09/10", 213 | "text": "教师节,在学校要给老师问声好呀~" 214 | }, { 215 | "date": "10/01", 216 | "text": "国庆节到了,为祖国母亲庆生!" 217 | }, { 218 | "date": "11/05-11/12", 219 | "text": "今年的双十一是和谁一起过的呢~" 220 | }, { 221 | "date": "12/20-12/31", 222 | "text": "这几天是圣诞节,主人肯定又去剁手买买买了~" 223 | }] 224 | } 225 | -------------------------------------------------------------------------------- /src/main/resources/static/live2d-widget/demo/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 看板娘登陆平台 7 | 8 | 9 | 10 | 123 | 124 | 125 | 153 | 268 | 269 | 270 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/controller/AdminController.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.controller; 2 | 3 | import io.swagger.annotations.*; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Qualifier; 7 | import org.springframework.web.bind.annotation.*; 8 | import per.kirito.pack.pojo.Admin; 9 | import per.kirito.pack.service.inter.AccountService; 10 | import per.kirito.pack.util.IpAddressUtil; 11 | import per.kirito.pack.util.PackIdUtil; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | import java.util.Map; 15 | 16 | /** 17 | * @author kirito 18 | * @date 2020/12/11 19 | * @time 20:11 20 | * Admin 的 Controller 层 21 | */ 22 | @Slf4j 23 | @Api(tags = {"管理员管理"}, produces = "application/json", consumes = "application/json") 24 | // @CrossOrigin 25 | @RestController 26 | @RequestMapping(value = "/admin") 27 | public class AdminController { 28 | 29 | @Qualifier("adminServiceImpl") 30 | @Autowired 31 | private AccountService accountService; 32 | 33 | /** 34 | * Admin 登录 35 | * 36 | * @param card 编号 37 | * @param password 密码 38 | * @param request http 请求 39 | * @return java.util.Map 40 | */ 41 | @ApiOperation(value = "登录", notes = "登录请求,返回登录成功与否", httpMethod = "PUT") 42 | @ApiResponses(value = { 43 | @ApiResponse(code = 200, message = "响应成功"), 44 | @ApiResponse(code = 201, message = "响应创建"), 45 | @ApiResponse(code = 401, message = "没有权限"), 46 | @ApiResponse(code = 403, message = "请求被拒绝"), 47 | @ApiResponse(code = 404, message = "资源不存在") 48 | }) 49 | @PostMapping(value = "/login") 50 | public Map adminLogin( 51 | @ApiParam(required = true, name = "card", value = "驿站编号") @RequestParam(value = "card") String card, 52 | @ApiParam(required = true, name = "password", value = "密码") @RequestParam(value = "password") String password, 53 | HttpServletRequest request) { 54 | log.info("请求 URL[/admin/login];参数[card={}, password={}]", card, password); 55 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 56 | return accountService.login(card, password); 57 | } 58 | 59 | /** 60 | * Admin 退出登录 61 | * 62 | * @param token 令牌 63 | * @param request http 请求 64 | * @return java.lang.String 65 | */ 66 | @ApiOperation(value = "退出登录", notes = "退出登录请求,返回退出成功与否", httpMethod = "POST") 67 | @ApiResponses(value = { 68 | @ApiResponse(code = 200, message = "响应成功"), 69 | @ApiResponse(code = 201, message = "响应创建"), 70 | @ApiResponse(code = 401, message = "没有权限"), 71 | @ApiResponse(code = 403, message = "请求被拒绝"), 72 | @ApiResponse(code = 404, message = "资源不存在") 73 | }) 74 | @PostMapping(value = "/logout") 75 | public String adminLogout( 76 | @ApiParam(required = true, name = "token", value = "token 令牌") @RequestParam(value = "token") String token, 77 | HttpServletRequest request) { 78 | log.info("请求 URL[/admin/logout];参数[token={}]", token); 79 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 80 | return accountService.logout(token); 81 | } 82 | 83 | /** 84 | * 获取 Admin 信息 85 | * 86 | * @param token 令牌 87 | * @param request http 请求 88 | * @return java.util.Map 89 | */ 90 | @ApiOperation(value = "获取驿站信息", notes = "获取信息,如果获取成功返回账号信息,如果失败返回失败原因", httpMethod = "POST") 91 | @ApiResponses(value = { 92 | @ApiResponse(code = 200, message = "响应成功"), 93 | @ApiResponse(code = 201, message = "响应创建"), 94 | @ApiResponse(code = 401, message = "没有权限"), 95 | @ApiResponse(code = 403, message = "请求被拒绝"), 96 | @ApiResponse(code = 404, message = "资源不存在") 97 | }) 98 | @PostMapping(value = "/get-info") 99 | public Map getAdminInfo( 100 | @ApiParam(required = true, name = "token", value = "token 令牌") @RequestParam(value = "token") String token, 101 | HttpServletRequest request) { 102 | log.info("请求 URL[/admin/get-info];参数[token={}]", token); 103 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 104 | return accountService.getInfo(token); 105 | } 106 | 107 | /** 108 | * 修改密码 109 | * 110 | * @param card 编号 111 | * @param oldPwd 原密码 112 | * @param newPwd 新密码 113 | * @param checkCode 验证码 114 | * @param token 令牌 115 | * @param request http 请求 116 | * @return java.util.Map 117 | */ 118 | @ApiOperation(value = "修改密码", notes = "修改密码请求,返回修改密码成功与否", httpMethod = "PUT") 119 | @ApiResponses(value = { 120 | @ApiResponse(code = 200, message = "响应成功"), 121 | @ApiResponse(code = 201, message = "响应创建"), 122 | @ApiResponse(code = 401, message = "没有权限"), 123 | @ApiResponse(code = 403, message = "请求被拒绝"), 124 | @ApiResponse(code = 404, message = "资源不存在") 125 | }) 126 | @PutMapping(value = "/reset-pwd") 127 | public Map resetPwd( 128 | @ApiParam(required = true, name = "card", value = "驿站编号") @RequestParam(value = "card") String card, 129 | @ApiParam(required = true, name = "oldPwd", value = "原密码") @RequestParam(value = "oldPwd") String oldPwd, 130 | @ApiParam(required = true, name = "newPwd", value = "新密码") @RequestParam(value = "newPwd") String newPwd, 131 | @ApiParam(required = true, name = "checkCode", value = "验证码") @RequestParam(value = "checkCode") String checkCode, 132 | @ApiParam(required = true, name = "token", value = "token 令牌") @RequestParam(value = "token") String token, 133 | HttpServletRequest request) { 134 | log.info("请求 URL[/admin/reset-pwd];参数[card={}, oldPwd={}, newPwd={}, checkCode={}, token={}]", card, oldPwd, newPwd, checkCode, token); 135 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 136 | return accountService.resetPwd(card, oldPwd, newPwd, checkCode, token); 137 | } 138 | 139 | /** 140 | * 更新 Admin 信息 141 | * 142 | * @param name 姓名 143 | * @param phone 手机号 144 | * @param mail 邮箱 145 | * @param token 令牌 146 | * @param request http 请求 147 | * @return java.util.Map 148 | */ 149 | @ApiOperation(value = "更新驿站信息", notes = "更新信息请求,返回更新成功与否", httpMethod = "PUT") 150 | @ApiResponses(value = { 151 | @ApiResponse(code = 200, message = "响应成功"), 152 | @ApiResponse(code = 201, message = "响应创建"), 153 | @ApiResponse(code = 401, message = "没有权限"), 154 | @ApiResponse(code = 403, message = "请求被拒绝"), 155 | @ApiResponse(code = 404, message = "资源不存在") 156 | }) 157 | @PutMapping(value = "/update-info") 158 | public Map updateInfo( 159 | @ApiParam(required = true, name = "name", value = "驿站编号") @RequestParam(value = "name") String name, 160 | @ApiParam(required = true, name = "phone", value = "手机号") @RequestParam(value = "phone") String phone, 161 | @ApiParam(required = false, name = "mail", value = "邮箱") String mail, 162 | @ApiParam(required = true, name = "token", value = "token 令牌") @RequestParam(value = "token") String token, 163 | HttpServletRequest request) { 164 | log.info("请求 URL[/admin/update-info];参数[name={}, phone={}, token={}]", name, phone, token); 165 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 166 | return accountService.updateInfo(name, phone, mail, token); 167 | } 168 | 169 | /** 170 | * 生成快递单号 171 | * 172 | * @param type 快递所属公司 173 | * @param request http 请求 174 | * @return java.lang.String 175 | */ 176 | @ApiOperation(value = "生成快递单号", notes = "生成指定快递的单号请求,返回快递单号", httpMethod = "POST") 177 | @ApiResponses(value = { 178 | @ApiResponse(code = 200, message = "响应成功"), 179 | @ApiResponse(code = 201, message = "响应创建"), 180 | @ApiResponse(code = 401, message = "没有权限"), 181 | @ApiResponse(code = 403, message = "请求被拒绝"), 182 | @ApiResponse(code = 404, message = "资源不存在") 183 | }) 184 | @PostMapping(value = "/pack-id") 185 | public String getPackId( 186 | @ApiParam(required = true, name = "type", value = "快递公司") @RequestParam(value = "type") String type, 187 | HttpServletRequest request) { 188 | log.info("请求 URL[/admin/pack-id];参数[type={}]", type); 189 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 190 | return PackIdUtil.generate(type); 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.4.0 9 | 10 | 11 | per.kirito 12 | pack 13 | 1.0.0 14 | 15 | jar 16 | pack 17 | Package manage project for Spring Boot 18 | 19 | 20 | 1.8 21 | 8.0.21 22 | 2.1.5 23 | 1.2.4 24 | 1.1.17 25 | 2.4.1 26 | 1.6.2 27 | 1.1.1 28 | 1.6.2 29 | 2.1.0 30 | 5.5.7 31 | 4.1.2 32 | 4.12 33 | 3.4.2 34 | 3.8.7 35 | 36 | 37 | 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter 42 | 43 | 44 | 45 | 46 | com.github.ulisesbocchio 47 | jasypt-spring-boot-starter 48 | 49 | ${jasypt.version} 50 | 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-data-jdbc 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-starter-data-jpa 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-starter-jdbc 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-starter-web-services 67 | 68 | 69 | 70 | 71 | org.springframework.boot 72 | spring-boot-configuration-processor 73 | true 74 | 75 | 76 | 77 | 78 | mysql 79 | mysql-connector-java 80 | ${mysql-connector-java.version} 81 | runtime 82 | 83 | 84 | 85 | 86 | org.projectlombok 87 | lombok 88 | true 89 | 90 | 91 | 92 | 93 | org.springframework.boot 94 | spring-boot-starter-tomcat 95 | provided 96 | 97 | 98 | 99 | 100 | org.springframework.boot 101 | spring-boot-starter-web 102 | 103 | 104 | 105 | 106 | com.baomidou 107 | mybatis-plus-boot-starter 108 | ${mybatis-plus-boot-starter.version} 109 | 110 | 111 | 112 | 113 | p6spy 114 | p6spy 115 | ${p6spy.version} 116 | 117 | 118 | 119 | 120 | org.springframework.boot 121 | spring-boot-starter-data-redis 122 | 123 | 124 | 125 | 126 | com.alibaba 127 | druid 128 | ${druid.version} 129 | 130 | 131 | com.alibaba 132 | druid-spring-boot-starter 133 | ${druid-spring-boot-starter.version} 134 | 135 | 136 | 137 | 138 | com.sun.mail 139 | javax.mail 140 | ${javax-mail.version} 141 | 142 | 143 | 144 | 145 | cn.hutool 146 | hutool-all 147 | ${hutool.version} 148 | 149 | 150 | 151 | 152 | org.apache.poi 153 | poi-ooxml 154 | ${poi-ooxml.version} 155 | 156 | 157 | 158 | 159 | com.alibaba 160 | fastjson 161 | 1.2.69 162 | 163 | 164 | 165 | 166 | org.springframework.boot 167 | spring-boot-starter-test 168 | test 169 | 170 | 171 | org.junit.vintage 172 | junit-vintage-engine 173 | 174 | 175 | 176 | 177 | junit 178 | junit 179 | ${junit.version} 180 | test 181 | 182 | 183 | 184 | 185 | io.springfox 186 | springfox-swagger2 187 | 2.9.2 188 | 189 | 190 | com.github.xiaoymin 191 | swagger-bootstrap-ui 192 | 1.9.6 193 | 194 | 195 | 196 | 197 | 198 | 199 | org.springframework.boot 200 | spring-boot-maven-plugin 201 | ${spring-boot-maven-plugin.version} 202 | 203 | 204 | maven-compiler-plugin 205 | 206 | ${java.version} 207 | ${java.version} 208 | 209 | 210 | 211 | org.apache.maven.plugins 212 | maven-dependency-plugin 213 | 2.10 214 | 215 | 216 | analyze-only-in-package 217 | package 218 | 219 | analyze-only 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | alimaven spring plugin 232 | alimaven spring plugin 233 | https://maven.aliyun.com/repository/spring-plugin 234 | 235 | 236 | 237 | 238 | 239 | aliyun 240 | aliyun repository 241 | http://maven.aliyun.com/nexus/content/groups/public/ 242 | 243 | true 244 | 245 | 246 | true 247 | 248 | 249 | 250 | 251 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/service/impl/AdminServiceImpl.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.service.impl; 2 | 3 | import cn.hutool.core.date.DateUtil; 4 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.data.redis.core.StringRedisTemplate; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Transactional; 10 | import per.kirito.pack.mapper.AdminMapper; 11 | import per.kirito.pack.mapper.EchartsMapper; 12 | import per.kirito.pack.myenum.Status; 13 | import per.kirito.pack.pojo.Admin; 14 | import per.kirito.pack.pojo.Echarts; 15 | import per.kirito.pack.service.inter.AccountService; 16 | import per.kirito.pack.util.Constant; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | import java.util.UUID; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | /** 24 | * @author kirito 25 | * @date 2020/12/28 26 | * @time 16:02 27 | * Admin 的 Service 层,是 AccountService 的泛型接口实现 28 | */ 29 | @Slf4j 30 | @Service 31 | public class AdminServiceImpl implements AccountService { 32 | 33 | @Autowired 34 | private AdminMapper adminMapper; 35 | 36 | @Autowired 37 | private EchartsMapper echartsMapper; 38 | 39 | @Autowired 40 | private StringRedisTemplate stringRedisTemplate; 41 | 42 | private static final int LOGIN_CODE = Status.LOGIN_SUCCESS.getCode(); 43 | 44 | private static final String LOGIN_SUCCESS = Status.LOGIN_SUCCESS.getEnMsg(); 45 | private static final String LOGIN_FAIL = Status.LOGIN_FAIL.getEnMsg(); 46 | private static final String EXIT_SUCCESS = Status.EXIT_SUCCESS.getEnMsg(); 47 | private static final String EXIT_FAIL = Status.EXIT_FAIL.getEnMsg(); 48 | private static final String INFO_SUCCESS = Status.INFO_SUCCESS.getEnMsg(); 49 | private static final String INFO_FAIL = Status.INFO_FAIL.getEnMsg(); 50 | private static final String PWD_SUCCESS = Status.PWD_SUCCESS.getEnMsg(); 51 | private static final String LOGIN_TO_DO = Status.LOGIN_TO_DO.getEnMsg(); 52 | private static final String DO_SUCCESS = Status.DO_SUCCESS.getEnMsg(); 53 | private static final String DO_FAIL = Status.DO_FAIL.getEnMsg(); 54 | private static final String CODE_ERR = Status.CODE_ERR.getEnMsg(); 55 | private static final String CODE_INVALID = Status.CODE_INVALID.getEnMsg(); 56 | private static final String PWD_ERR = Status.PWD_ERR.getEnMsg(); 57 | 58 | /** 59 | * Admin 登录 60 | * 61 | * @param card 编号 62 | * @param password 密码 63 | * @return java.util.Map 64 | */ 65 | @Override 66 | public Map login(String card, String password) { 67 | Map map = new HashMap<>(); 68 | // 根据 card 和 password 查询出该 Admin 是否存在 69 | QueryWrapper queryWrapper = new QueryWrapper<>(); 70 | queryWrapper.eq("card", card).eq("password", password); 71 | int flag = adminMapper.selectCount(queryWrapper); 72 | if (flag == LOGIN_CODE) { 73 | // 生成唯一令牌 token 74 | String token = UUID.randomUUID().toString(); 75 | // 如果 Redis 中已存储,则先删除此键 76 | if (stringRedisTemplate.hasKey(token)) { 77 | stringRedisTemplate.delete(token); 78 | } 79 | stringRedisTemplate.opsForValue().set(token, card, Constant.LOGIN_VALID_MINUTE, TimeUnit.MINUTES); 80 | map.put("token", token); 81 | map.put("result", LOGIN_SUCCESS); 82 | 83 | // Echarts 统计 84 | String today = DateUtil.today(); 85 | QueryWrapper echartsQueryWrapper = new QueryWrapper<>(); 86 | echartsQueryWrapper.eq("datee", today).eq("card", card); 87 | Echarts echarts = echartsMapper.selectOne(echartsQueryWrapper); 88 | if (echarts == null) { 89 | echarts = new Echarts(); 90 | echarts.setDatee(today); 91 | echarts.setCard(card); 92 | echarts.setNine(0); 93 | echarts.setTen(0); 94 | echarts.setEleven(0); 95 | echarts.setTwelve(0); 96 | echarts.setThirteen(0); 97 | echarts.setFourteen(0); 98 | echarts.setFifteen(0); 99 | echarts.setSixteen(0); 100 | echarts.setSeventeen(0); 101 | echarts.setEighteen(0); 102 | echarts.setNineteen(0); 103 | echartsMapper.insert(echarts); 104 | } 105 | } else { 106 | log.info("card: {} 登录失败,因为该驿站管理员不存在!", card); 107 | map.put("result", LOGIN_FAIL); 108 | } 109 | return map; 110 | } 111 | 112 | /** 113 | * 退出登录 114 | * 115 | * @param token 令牌 116 | * @return java.lang.String 117 | */ 118 | @Override 119 | public String logout(String token) { 120 | // 退出登录时,删除 Redis 中存储的相关键值 121 | stringRedisTemplate.delete(token); 122 | return stringRedisTemplate.hasKey(token) ? EXIT_FAIL : EXIT_SUCCESS; 123 | } 124 | 125 | /** 126 | * 获取 Admin 信息 127 | * 128 | * @param token 令牌 129 | * @return java.util.Map 130 | */ 131 | @Override 132 | public Map getInfo(String token) { 133 | Map map = new HashMap<>(); 134 | String result = ""; 135 | boolean isLogin = stringRedisTemplate.hasKey(token); 136 | if (isLogin) { 137 | String card = stringRedisTemplate.opsForValue().get(token); 138 | Admin admin = adminMapper.selectById(card); 139 | map.put("admin", admin); 140 | result = INFO_SUCCESS; 141 | } else { 142 | log.info("token: {} 获取该驿站管理员信息失败,因为登录状态失效!", token); 143 | result = INFO_FAIL; 144 | } 145 | map.put("result", result); 146 | return map; 147 | } 148 | 149 | /** 150 | * 注册(不实现该功能) 151 | * 152 | * @param entity 账户实体信息 153 | * @return java.util.Map 154 | */ 155 | @Override 156 | public Map register(E entity) { 157 | // 不实现该功能 158 | return null; 159 | } 160 | 161 | /** 162 | * 忘记密码(不实现该功能) 163 | * 164 | * @param card 编号 165 | * @param phone 手机号 166 | * @param password 新密码 167 | * @return java.util.Map 168 | */ 169 | @Override 170 | public Map forgetPwd(String card, String phone, String password) { 171 | // 不实现该功能 172 | return null; 173 | } 174 | 175 | /** 176 | * 修改密码 177 | * 178 | * @param card 编号 179 | * @param oldPwd 原密码 180 | * @param newPwd 新密码 181 | * @param checkCode 验证码 182 | * @param token 令牌 183 | * @return java.util.Map 184 | */ 185 | @Transactional(rollbackFor = Exception.class) 186 | @Override 187 | public Map resetPwd(String card, String oldPwd, String newPwd, String checkCode, String token) { 188 | Map map = new HashMap<>(); 189 | try { 190 | if (stringRedisTemplate.hasKey(token)) { 191 | String tokenCode = token + "-code"; 192 | if (stringRedisTemplate.hasKey(tokenCode)) { 193 | // Redis 中存有验证码,且未过期 194 | String code = stringRedisTemplate.opsForValue().get(token + "-code"); 195 | if (code.equals(checkCode)) { 196 | QueryWrapper queryWrapper = new QueryWrapper<>(); 197 | queryWrapper.eq("card", card).eq("password", oldPwd); 198 | int flag = adminMapper.selectCount(queryWrapper); 199 | if (flag == 1) { 200 | Admin admin = adminMapper.selectById(card); 201 | admin.setPassword(newPwd); 202 | adminMapper.updateById(admin); 203 | map.put("result", PWD_SUCCESS); 204 | } else { 205 | // 原密码错误,导致成功执行条数不为1 206 | log.info("card: {} 修改密码失败,因为原密码输入错误!", card); 207 | map.put("result", PWD_ERR); 208 | } 209 | } else { 210 | // 验证码不正确 211 | log.info("card: {} 修改密码失败,因为验证码输入错误!", card); 212 | map.put("result", CODE_ERR); 213 | } 214 | } else { 215 | // 验证码已过期 216 | log.info("card: {} 修改密码失败,因为验证码已过期!", card); 217 | map.put("result", CODE_INVALID); 218 | } 219 | } else { 220 | // 登录状态失效 221 | log.info("card: {} 修改密码失败,因为登录状态失效!", card); 222 | map.put("result", LOGIN_TO_DO); 223 | } 224 | return map; 225 | } catch (Exception e) { 226 | log.error("error: {}", e.getMessage(), e); 227 | log.info("card: {} 修改密码失败,因为发生了异常!", card); 228 | map.put("result", DO_FAIL); 229 | return map; 230 | } 231 | } 232 | 233 | /** 234 | * 更新 Admin 信息 235 | * 236 | * @param name 姓名 237 | * @param phone 手机号 238 | * @param mail 邮箱 239 | * @param token 令牌 240 | * @return java.util.Map 241 | */ 242 | @Transactional(rollbackFor = Exception.class) 243 | @Override 244 | public Map updateInfo(String name, String phone, String mail, String token) { 245 | Map map = new HashMap<>(); 246 | try { 247 | if (stringRedisTemplate.hasKey(token)) { 248 | String card = stringRedisTemplate.opsForValue().get(token); 249 | Admin admin = adminMapper.selectById(card); 250 | admin.setName(name); 251 | admin.setPhone(phone); 252 | adminMapper.updateById(admin); 253 | map.put("result", DO_SUCCESS); 254 | } else { 255 | // 登录状态失效 256 | log.info("token: " + token + " 更新信息失败,因为登录状态失效!"); 257 | map.put("result", LOGIN_TO_DO); 258 | } 259 | return map; 260 | } catch (Exception e) { 261 | log.error("error: {}", e.getMessage(), e); 262 | log.info("token: {} 更新信息失败,因为发生了异常!", token); 263 | map.put("result", DO_FAIL); 264 | return map; 265 | } 266 | } 267 | 268 | } 269 | -------------------------------------------------------------------------------- /src/main/java/per/kirito/pack/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package per.kirito.pack.controller; 2 | 3 | import io.swagger.annotations.*; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Qualifier; 7 | import org.springframework.web.bind.annotation.*; 8 | import per.kirito.pack.pojo.User; 9 | import per.kirito.pack.service.inter.AccountService; 10 | import per.kirito.pack.util.IpAddressUtil; 11 | 12 | import javax.servlet.http.HttpServletRequest; 13 | import java.util.Map; 14 | 15 | /** 16 | * @author kirito 17 | * @date 2020/12/4 18 | * @time 21:04 19 | * User 的 Controller 层 20 | */ 21 | @Slf4j 22 | @Api(tags = {"学生管理"}, produces = "application/json", consumes = "application/json") 23 | // @CrossOrigin 24 | @RestController 25 | @RequestMapping(value = "/user") 26 | public class UserController { 27 | 28 | @Qualifier("userServiceImpl") 29 | @Autowired 30 | private AccountService accountService; 31 | 32 | /** 33 | * User 登录 34 | * 35 | * @param card 学号 36 | * @param password 密码 37 | * @param request http 请求 38 | * @return java.util.Map 39 | */ 40 | @ApiOperation(value = "登录", notes = "登录请求,返回登录成功与否", httpMethod = "POST") 41 | @ApiResponses(value = { 42 | @ApiResponse(code = 200, message = "响应成功"), 43 | @ApiResponse(code = 201, message = "响应创建"), 44 | @ApiResponse(code = 401, message = "没有权限"), 45 | @ApiResponse(code = 403, message = "请求被拒绝"), 46 | @ApiResponse(code = 404, message = "资源不存在") 47 | }) 48 | @PostMapping(value = "/login") 49 | public Map login( 50 | @ApiParam(required = true, name = "card", value = "学号") @RequestParam(value = "card") String card, 51 | @ApiParam(required = true, name = "password", value = "密码") @RequestParam(value = "password") String password, 52 | HttpServletRequest request) { 53 | log.info("请求 URL[/user/login];参数[card={}, password={}]", card, password); 54 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 55 | return accountService.login(card, password); 56 | } 57 | 58 | /** 59 | * User 退出登录 60 | * 61 | * @param token 令牌 62 | * @param request http 请求 63 | * @return java.lang.String 64 | */ 65 | @ApiOperation(value = "退出登录", notes = "退出登录请求,返回退出成功与否", httpMethod = "POST") 66 | @ApiResponses(value = { 67 | @ApiResponse(code = 200, message = "响应成功"), 68 | @ApiResponse(code = 201, message = "响应创建"), 69 | @ApiResponse(code = 401, message = "没有权限"), 70 | @ApiResponse(code = 403, message = "请求被拒绝"), 71 | @ApiResponse(code = 404, message = "资源不存在") 72 | }) 73 | @PostMapping(value = "/logout") 74 | public String userLogout( 75 | @ApiParam(required = true, name = "token", value = "token 令牌") @RequestParam(value = "token") String token, 76 | HttpServletRequest request) { 77 | log.info("请求 URL[/user/logout];参数[token={}]", token); 78 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 79 | return accountService.logout(token); 80 | } 81 | 82 | /** 83 | * 获取 User 信息 84 | * 85 | * @param token 令牌 86 | * @param request http 请求 87 | * @return java.util.Map 88 | */ 89 | @ApiOperation(value = "获取学生信息", notes = "获取信息,如果获取成功返回账号信息,如果失败返回失败原因", httpMethod = "POST") 90 | @ApiResponses(value = { 91 | @ApiResponse(code = 200, message = "响应成功"), 92 | @ApiResponse(code = 201, message = "响应创建"), 93 | @ApiResponse(code = 401, message = "没有权限"), 94 | @ApiResponse(code = 403, message = "请求被拒绝"), 95 | @ApiResponse(code = 404, message = "资源不存在") 96 | }) 97 | @PostMapping(value = "/get-info") 98 | public Map getUserInfo( 99 | @ApiParam(required = true, name = "token", value = "token 令牌") @RequestParam(value = "token") String token, 100 | HttpServletRequest request) { 101 | log.info("请求 URL[/user/get-info];参数[token={}]", token); 102 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 103 | return accountService.getInfo(token); 104 | } 105 | 106 | /** 107 | * User 注册 108 | * 109 | * @param user 用户信息 110 | * @param request http 请求 111 | * @return java.util.Map 112 | */ 113 | @ApiOperation(value = "注册", notes = "注册请求,返回注册成功与否", httpMethod = "POST") 114 | @ApiResponses(value = { 115 | @ApiResponse(code = 200, message = "响应成功"), 116 | @ApiResponse(code = 201, message = "响应创建"), 117 | @ApiResponse(code = 401, message = "没有权限"), 118 | @ApiResponse(code = 403, message = "请求被拒绝"), 119 | @ApiResponse(code = 404, message = "资源不存在") 120 | }) 121 | @PostMapping(value = "/register") 122 | public Map register( 123 | @ApiParam(required = true, name = "user", value = "学生实体") @RequestBody User user, 124 | HttpServletRequest request) { 125 | log.info("请求 URL[/user/register];参数[user={}]", user.toString()); 126 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 127 | return accountService.register(user); 128 | } 129 | 130 | /** 131 | * User 忘记密码 132 | * 133 | * @param card 学号 134 | * @param phone 手机号 135 | * @param password 新密码 136 | * @param request http 请求 137 | * @return java.util.Map 138 | */ 139 | @ApiOperation(value = "忘记密码", notes = "忘记密码请求,如果学生存在返回操作成功与否,如果不存在返回学生不存在信息", httpMethod = "PUT") 140 | @ApiResponses(value = { 141 | @ApiResponse(code = 200, message = "响应成功"), 142 | @ApiResponse(code = 201, message = "响应创建"), 143 | @ApiResponse(code = 401, message = "没有权限"), 144 | @ApiResponse(code = 403, message = "请求被拒绝"), 145 | @ApiResponse(code = 404, message = "资源不存在") 146 | }) 147 | @PutMapping(value = "/forget-pwd") 148 | public Map forgetPwd( 149 | @ApiParam(required = true, name = "card", value = "学号") @RequestParam(value = "card") String card, 150 | @ApiParam(required = true, name = "phone", value = "手机号") @RequestParam(value = "phone") String phone, 151 | @ApiParam(required = true, name = "password", value = "密码") @RequestParam(value = "password") String password, 152 | HttpServletRequest request) { 153 | log.info("请求 URL[/user/forget-pwd];参数[card={}, phone{}, password={}]", card, phone, password); 154 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 155 | return accountService.forgetPwd(card, phone, password); 156 | } 157 | 158 | /** 159 | * 修改密码 160 | * 161 | * @param card 学号 162 | * @param oldPwd 原密码 163 | * @param newPwd 新密码 164 | * @param checkCode 验证码 165 | * @param token 令牌 166 | * @param request http 请求 167 | * @return java.util.Map 168 | */ 169 | @ApiOperation(value = "修改密码", notes = "修改密码请求,返回修改密码成功与否", httpMethod = "PUT") 170 | @ApiResponses(value = { 171 | @ApiResponse(code = 200, message = "响应成功"), 172 | @ApiResponse(code = 201, message = "响应创建"), 173 | @ApiResponse(code = 401, message = "没有权限"), 174 | @ApiResponse(code = 403, message = "请求被拒绝"), 175 | @ApiResponse(code = 404, message = "资源不存在") 176 | }) 177 | @PutMapping(value = "/reset-pwd") 178 | public Map resetPwd( 179 | @ApiParam(required = true, name = "card", value = "学号") @RequestParam(value = "card") String card, 180 | @ApiParam(required = true, name = "oldPwd", value = "原密码") @RequestParam(value = "oldPwd") String oldPwd, 181 | @ApiParam(required = true, name = "newPwd", value = "新密码") @RequestParam(value = "newPwd") String newPwd, 182 | @ApiParam(required = true, name = "checkCode", value = "验证码") @RequestParam(value = "checkCode") String checkCode, 183 | @ApiParam(required = true, name = "token", value = "token 令牌") @RequestParam(value = "token") String token, 184 | HttpServletRequest request) { 185 | log.info("请求 URL[/user/reset-pwd];参数[card={}, oldPwd={}, newPwd={}, checkCode={}, token={}]", card, oldPwd, newPwd, checkCode, token); 186 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 187 | return accountService.resetPwd(card, oldPwd, newPwd, checkCode, token); 188 | } 189 | 190 | /** 191 | * 更新用户信息 192 | * 193 | * @param name 姓名 194 | * @param addr 地址 195 | * @param mail 邮箱 196 | * @param token 令牌 197 | * @param request http 请求 198 | * @return java.util.Map 199 | */ 200 | @ApiOperation(value = "更新学生信息", notes = "更新信息请求,返回更新成功与否", httpMethod = "PUT") 201 | @ApiResponses(value = { 202 | @ApiResponse(code = 200, message = "响应成功"), 203 | @ApiResponse(code = 201, message = "响应创建"), 204 | @ApiResponse(code = 401, message = "没有权限"), 205 | @ApiResponse(code = 403, message = "请求被拒绝"), 206 | @ApiResponse(code = 404, message = "资源不存在") 207 | }) 208 | @PutMapping(value = "/update-info") 209 | public Map updateInfo( 210 | @ApiParam(required = true, name = "name", value = "姓名") @RequestParam(value = "name") String name, 211 | @ApiParam(required = true, name = "addr", value = "地址") @RequestParam(value = "addr") String addr, 212 | @ApiParam(required = true, name = "mail", value = "邮箱") @RequestParam(value = "mail") String mail, 213 | @ApiParam(required = true, name = "token", value = "token 令牌") @RequestParam(value = "token") String token, 214 | HttpServletRequest request) { 215 | log.info("请求 URL[/user/update-info];参数[name={}, addr={}, mail={}, token={}]", name, addr, mail, token); 216 | log.info("请求来源: {}", IpAddressUtil.getIpAddress(request)); 217 | return accountService.updateInfo(name, addr, mail, token); 218 | } 219 | 220 | } 221 | --------------------------------------------------------------------------------