From fd64c134b0b56f9cec4e308229918ea6e82ac688 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?LI-CCONG=5C=E6=9D=8E=E8=81=AA=E8=81=AA?= <1441652193@qq.com>
Date: Wed, 28 Feb 2024 18:15:28 +0800
Subject: [PATCH] =?UTF-8?q?jwt=E7=99=BB=E5=BD=95=E9=AA=8C=E8=AF=81?=
=?UTF-8?q?=E3=80=81=E5=BE=AE=E4=BF=A1=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=99=BB?=
=?UTF-8?q?=E5=BD=95=E3=80=81=E4=BB=A3=E7=A0=81=E8=87=AA=E5=8A=A8=E7=94=9F?=
=?UTF-8?q?=E6=88=90=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
nxhs-common/pom.xml | 46 +++++
.../cc/yunxi/common/domain/PageQueryPlus.java | 45 +++++
.../yunxi/common/domain/SortingFieldDTO.java | 34 ++++
.../java/cc/yunxi/common/genCodeMain.java | 82 +++++++++
nxhs-service/pom.xml | 9 +
.../java/cc/yunxi/config/AuthProperties.java | 13 ++
.../java/cc/yunxi/config/JwtProperties.java | 16 ++
.../java/cc/yunxi/config/SecurityConfig.java | 33 ++++
.../java/cc/yunxi/config/WebMvcConfig.java | 76 ++++++++
.../java/cc/yunxi/config/WxHsyProperties.java | 17 ++
.../cc/yunxi/controller/ClientController.java | 20 ++
.../cc/yunxi/controller/CommonController.java | 96 ++++++++++
.../yunxi/controller/RecyclerController.java | 32 ++++
.../java/cc/yunxi/domain/dto/RecyclerDTO.java | 50 +++++
.../java/cc/yunxi/domain/dto/UserDTO.java | 21 +++
.../cc/yunxi/domain/dto/form/WxLoginDTO.java | 11 ++
.../main/java/cc/yunxi/domain/po/Client.java | 115 ++++++++++++
.../java/cc/yunxi/domain/po/Recycler.java | 48 +++++
.../cc/yunxi/domain/query/RecyclerQuery.java | 22 +++
.../yunxi/interceptor/LoginInterceptor.java | 40 ++++
.../yunxi/interceptor/StatsInterceptor.java | 36 ++++
.../java/cc/yunxi/mapper/ClientMapper.java | 18 ++
.../java/cc/yunxi/mapper/RecyclerMapper.java | 14 ++
.../java/cc/yunxi/service/ClientService.java | 16 ++
.../cc/yunxi/service/IRecyclerService.java | 23 +++
.../yunxi/service/impl/ClientServiceImpl.java | 20 ++
.../service/impl/RecyclerServiceImpl.java | 46 +++++
.../src/main/java/cc/yunxi/utils/JwtTool.java | 100 ++++++++++
.../java/cc/yunxi}/utils/UserContext.java | 17 +-
.../main/java/cc/yunxi/utils/WeChatUtil.java | 173 ++++++++++++++++++
.../src/main/resources/application.yml | 30 +--
.../src/main/resources/logback-spring.xml | 22 ---
.../main/resources/mapper/ClientMapper.xml | 5 +
.../main/resources/mapper/RecyclerMapper.xml | 5 +
nxhs-service/src/main/resources/nxhs.jks | Bin 0 -> 2217 bytes
.../java/cc/yunxi/NxhsApplicationTest.java | 18 ++
pom.xml | 72 ++++----
37 files changed, 1359 insertions(+), 82 deletions(-)
create mode 100644 nxhs-common/src/main/java/cc/yunxi/common/domain/PageQueryPlus.java
create mode 100644 nxhs-common/src/main/java/cc/yunxi/common/domain/SortingFieldDTO.java
create mode 100644 nxhs-common/src/main/java/cc/yunxi/common/genCodeMain.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/config/AuthProperties.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/config/JwtProperties.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/config/SecurityConfig.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/config/WebMvcConfig.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/config/WxHsyProperties.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/controller/ClientController.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/controller/CommonController.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/controller/RecyclerController.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/domain/dto/RecyclerDTO.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/domain/dto/UserDTO.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/domain/dto/form/WxLoginDTO.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/domain/po/Client.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/domain/po/Recycler.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/domain/query/RecyclerQuery.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/interceptor/LoginInterceptor.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/interceptor/StatsInterceptor.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/mapper/ClientMapper.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/mapper/RecyclerMapper.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/service/ClientService.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/service/IRecyclerService.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/service/impl/ClientServiceImpl.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/service/impl/RecyclerServiceImpl.java
create mode 100644 nxhs-service/src/main/java/cc/yunxi/utils/JwtTool.java
rename {nxhs-common/src/main/java/cc/yunxi/common => nxhs-service/src/main/java/cc/yunxi}/utils/UserContext.java (51%)
create mode 100644 nxhs-service/src/main/java/cc/yunxi/utils/WeChatUtil.java
create mode 100644 nxhs-service/src/main/resources/mapper/ClientMapper.xml
create mode 100644 nxhs-service/src/main/resources/mapper/RecyclerMapper.xml
create mode 100644 nxhs-service/src/main/resources/nxhs.jks
diff --git a/nxhs-common/pom.xml b/nxhs-common/pom.xml
index c177837..1b307d2 100644
--- a/nxhs-common/pom.xml
+++ b/nxhs-common/pom.xml
@@ -30,6 +30,52 @@
tomcat-embed-core
provided
+
+
+ com.mysql
+ mysql-connector-j
+ runtime
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+
+
+
+ com.baomidou
+ mybatis-plus-generator
+ 3.5.1
+
+
+ org.freemarker
+ freemarker
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-xml
+
+
+
+ cn.hutool
+ hutool-all
+
+
+
+ org.hibernate.validator
+ hibernate-validator
+
+
+
+ com.github.xiaoymin
+ knife4j-openapi2-spring-boot-starter
+
\ No newline at end of file
diff --git a/nxhs-common/src/main/java/cc/yunxi/common/domain/PageQueryPlus.java b/nxhs-common/src/main/java/cc/yunxi/common/domain/PageQueryPlus.java
new file mode 100644
index 0000000..d1347df
--- /dev/null
+++ b/nxhs-common/src/main/java/cc/yunxi/common/domain/PageQueryPlus.java
@@ -0,0 +1,45 @@
+package cc.yunxi.common.domain;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.metadata.OrderItem;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.Min;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+@Data
+@ApiModel(description = "分页查询条件")
+@Accessors(chain = true)
+public class PageQueryPlus {
+ public static final Integer DEFAULT_PAGE_SIZE = 20;
+ public static final Integer DEFAULT_PAGE_NUM = 1;
+ @ApiModelProperty("页码")
+ @Min(value = 1, message = "页码不能小于1")
+ private Integer pageNo = DEFAULT_PAGE_NUM;
+
+ @ApiModelProperty("每页个数")
+ @Min(value = 1, message = "每页查询数量不能小于1")
+ private Integer pageSize = DEFAULT_PAGE_SIZE;
+
+ @ApiModelProperty("排序规则")
+ private Collection sortingFields;
+
+// public Page buildPage() {
+// Page page = new Page<>(pageNo, pageSize);
+// if (!CollectionUtil.isEmpty(sortingFields)) {
+// page.addOrder(sortingFields.stream().map(sortingField -> SortingFieldDTO.ORDER_ASC.equals(sortingField.getOrder()) ?
+// OrderItem.asc(sortingField.getField()) : OrderItem.desc(sortingField.getField()))
+// .collect(Collectors.toList()));
+// }
+// }
+
+}
diff --git a/nxhs-common/src/main/java/cc/yunxi/common/domain/SortingFieldDTO.java b/nxhs-common/src/main/java/cc/yunxi/common/domain/SortingFieldDTO.java
new file mode 100644
index 0000000..83483f9
--- /dev/null
+++ b/nxhs-common/src/main/java/cc/yunxi/common/domain/SortingFieldDTO.java
@@ -0,0 +1,34 @@
+package cc.yunxi.common.domain;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class SortingFieldDTO implements Serializable {
+
+ /**
+ * 顺序 - 升序
+ */
+ public static final String ORDER_ASC = "asc";
+ /**
+ * 顺序 - 降序
+ */
+ public static final String ORDER_DESC = "desc";
+
+ /**
+ * 字段
+ */
+ @ApiModelProperty("字段")
+ private String field;
+ /**
+ * 顺序
+ */
+ @ApiModelProperty("是否升序")
+ private String order;
+}
diff --git a/nxhs-common/src/main/java/cc/yunxi/common/genCodeMain.java b/nxhs-common/src/main/java/cc/yunxi/common/genCodeMain.java
new file mode 100644
index 0000000..9694eb5
--- /dev/null
+++ b/nxhs-common/src/main/java/cc/yunxi/common/genCodeMain.java
@@ -0,0 +1,82 @@
+package cc.yunxi.common;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.generator.FastAutoGenerator;
+import com.baomidou.mybatisplus.generator.config.OutputFile;
+import com.baomidou.mybatisplus.generator.config.rules.DateType;
+import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
+import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
+import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
+import com.baomidou.mybatisplus.generator.fill.Column;
+
+import java.util.Collections;
+
+public class genCodeMain {
+ public static void main(String[] args) {
+ //1、配置数据源
+ FastAutoGenerator.create("jdbc:mysql://222.71.165.188:3309/jnpf_ningxia", "root", "linus,.123")
+ //2、全局配置
+ .globalConfig(builder -> {
+ builder.author("ccongli") // 设置作者名
+ .outputDir(System.getProperty("user.dir") + "/src/main/java") //设置输出路径:项目的 java 目录下
+ .commentDate("yyyy-MM-dd hh:mm:ss") //注释日期
+ .dateType(DateType.ONLY_DATE) //定义生成的实体类中日期的类型 TIME_PACK=LocalDateTime;ONLY_DATE=Date;
+ .fileOverride() // 覆盖之前的文件
+ .enableSwagger() // 开启 swagger 模式
+ .disableOpenDir(); // 禁止打开输出目录,默认打开
+ })
+ //3、包配置
+ .packageConfig(builder -> {
+ builder.parent("cc.yunxi") // 设置父包名
+ .moduleName("") //设置模块包名
+ .entity("domain.po") //pojo 实体类包名
+ .service("service") //Service 包名
+ .serviceImpl("service.impl") // ***ServiceImpl 包名
+ .mapper("mapper") //Mapper 包名
+ .xml("mapper") //Mapper XML 包名
+ .controller("controller") //Controller 包名
+ .other("utils") //自定义文件包名
+ .pathInfo(Collections.singletonMap(OutputFile.mapperXml, System.getProperty("user.dir")+"/src/main/resources/mapper")); //配置 mapper.xml 路径信息:项目的 resources 目录下
+ })
+ //4、策略配置
+ .strategyConfig(builder -> {
+ builder.addInclude("nx_client") // 设置需要生成的数据表名
+ .addTablePrefix("nx") // 设置过滤表前缀
+ //4.1、Mapper策略配置
+ .mapperBuilder()
+ .superClass(BaseMapper.class) //设置父类
+ .formatMapperFileName("%sMapper") //格式化 mapper 文件名称
+ .enableMapperAnnotation() //开启 @Mapper 注解
+ .formatXmlFileName("%sMapper") //格式化 Xml 文件名称
+
+ //4.2、service 策略配置
+ .serviceBuilder()
+ .formatServiceFileName("%sService") //格式化 service 接口文件名称,%s进行匹配表名,如 UserService
+ .formatServiceImplFileName("%sServiceImpl") //格式化 service 实现类文件名称,%s进行匹配表名,如 UserServiceImpl
+
+ //4.3、实体类策略配置
+ .entityBuilder()
+ .enableLombok() //开启 Lombok
+ .disableSerialVersionUID() //不实现 Serializable 接口,不生产 SerialVersionUID
+// .logicDeleteColumnName("deleted") //逻辑删除字段名
+ .naming(NamingStrategy.underline_to_camel) // 数据库表映射到实体的命名策略:下划线转驼峰命
+ .columnNaming(NamingStrategy.underline_to_camel) // 数据库表字段映射到实体的命名策略:下划线转驼峰命
+ .addTableFills(
+ new Column("create_time", FieldFill.INSERT),
+ new Column("modify_time", FieldFill.INSERT_UPDATE)
+ ) //添加表字段填充,"create_time"字段自动填充为插入时间,"modify_time"字段自动填充为插入修改时间
+ .enableTableFieldAnnotation() // 开启生成实体时生成字段注解
+
+ //4.4、Controller策略配置
+ .controllerBuilder()
+ .formatFileName("%sController") //格式化 Controller 类文件名称,%s进行匹配表名,如 UserController
+ .enableRestStyle(); //开启生成 @RestController 控制器
+ })
+ //5、模板
+ .templateEngine(new FreemarkerTemplateEngine())
+// .templateEngine(new VelocityTemplateEngine())
+ //6、执行
+ .execute();
+ }
+}
diff --git a/nxhs-service/pom.xml b/nxhs-service/pom.xml
index 959e9da..1eb6992 100644
--- a/nxhs-service/pom.xml
+++ b/nxhs-service/pom.xml
@@ -29,6 +29,15 @@
org.springframework.boot
spring-boot-starter-web
+
+
+ org.springframework.security
+ spring-security-crypto
+
+
+ org.springframework.security
+ spring-security-rsa
+
\ No newline at end of file
diff --git a/nxhs-service/src/main/java/cc/yunxi/config/AuthProperties.java b/nxhs-service/src/main/java/cc/yunxi/config/AuthProperties.java
new file mode 100644
index 0000000..5fbf3f5
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/config/AuthProperties.java
@@ -0,0 +1,13 @@
+package cc.yunxi.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.List;
+
+@Data
+@ConfigurationProperties(prefix = "nxhs.auth")
+public class AuthProperties {
+ private List includePaths;
+ private List excludePaths;
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/config/JwtProperties.java b/nxhs-service/src/main/java/cc/yunxi/config/JwtProperties.java
new file mode 100644
index 0000000..838edab
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/config/JwtProperties.java
@@ -0,0 +1,16 @@
+package cc.yunxi.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.core.io.Resource;
+
+import java.time.Duration;
+
+@Data
+@ConfigurationProperties(prefix = "nxhs.jwt")
+public class JwtProperties {
+ private Resource location;
+ private String password;
+ private String alias;
+ private Duration tokenTTL = Duration.ofMinutes(10);
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/config/SecurityConfig.java b/nxhs-service/src/main/java/cc/yunxi/config/SecurityConfig.java
new file mode 100644
index 0000000..c664184
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/config/SecurityConfig.java
@@ -0,0 +1,33 @@
+package cc.yunxi.config;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
+
+import java.security.KeyPair;
+
+@Configuration
+@EnableConfigurationProperties(JwtProperties.class)
+public class SecurityConfig {
+
+ @Bean
+ public PasswordEncoder passwordEncoder(){
+ return new BCryptPasswordEncoder();
+ }
+
+ @Bean
+ public KeyPair keyPair(JwtProperties properties){
+ // 获取秘钥工厂
+ KeyStoreKeyFactory keyStoreKeyFactory =
+ new KeyStoreKeyFactory(
+ properties.getLocation(),
+ properties.getPassword().toCharArray());
+ //读取钥匙对
+ return keyStoreKeyFactory.getKeyPair(
+ properties.getAlias(),
+ properties.getPassword().toCharArray());
+ }
+}
\ No newline at end of file
diff --git a/nxhs-service/src/main/java/cc/yunxi/config/WebMvcConfig.java b/nxhs-service/src/main/java/cc/yunxi/config/WebMvcConfig.java
new file mode 100644
index 0000000..156688d
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/config/WebMvcConfig.java
@@ -0,0 +1,76 @@
+package cc.yunxi.config;
+
+import cc.yunxi.interceptor.LoginInterceptor;
+import cc.yunxi.interceptor.StatsInterceptor;
+import cn.hutool.core.collection.CollUtil;
+import lombok.RequiredArgsConstructor;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.Arrays;
+import java.util.List;
+
+@Configuration
+@RequiredArgsConstructor
+@EnableConfigurationProperties(AuthProperties.class)
+public class WebMvcConfig implements WebMvcConfigurer {
+ private final LoginInterceptor loginInterceptor;
+ private final StatsInterceptor statsInterceptor;
+ private final AuthProperties authProperties;
+
+ // 全局忽略路径
+ private static List globalExcludePaths = Arrays.asList(
+ "/error",
+ "/favicon.ico",
+ "/v2/**",
+ "/v3/**",
+ "/swagger-resources/**",
+ "/webjars/**",
+ "/doc.html"
+ );
+
+ @Override
+ public void addCorsMappings(CorsRegistry registry) {
+ registry.addMapping("/**") // 所有接口
+ .allowCredentials(true) // 是否发送 Cookie
+ .allowedOriginPatterns("*") // 支持域
+ .allowedMethods("GET", "POST", "PUT", "DELETE") // 支持方法
+ .allowedHeaders("*")
+ .exposedHeaders("*");
+ }
+
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ // 按顺序执行拦截器
+ this.addStatsInterceptor(registry);
+ this.addLoginInterceptor(registry);
+ }
+
+ // 添加登录拦截器
+ private void addLoginInterceptor(InterceptorRegistry registry) {
+ InterceptorRegistration registration = registry.addInterceptor(loginInterceptor);
+ // 2.配置拦截路径
+ List includePaths = authProperties.getIncludePaths();
+ if (CollUtil.isNotEmpty(includePaths)) {
+ registration.addPathPatterns(includePaths);
+ }
+ // 3.配置放行路径
+ List excludePaths = authProperties.getExcludePaths();
+ if (CollUtil.isNotEmpty(excludePaths)) {
+ registration.excludePathPatterns(excludePaths);
+ }
+ registration.excludePathPatterns(globalExcludePaths);
+ }
+
+ // 添加请求日志拦截器
+ private void addStatsInterceptor(InterceptorRegistry registry) {
+ registry.addInterceptor(statsInterceptor)
+ .addPathPatterns("/**")
+ .excludePathPatterns(globalExcludePaths);
+ }
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/config/WxHsyProperties.java b/nxhs-service/src/main/java/cc/yunxi/config/WxHsyProperties.java
new file mode 100644
index 0000000..3efbd1e
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/config/WxHsyProperties.java
@@ -0,0 +1,17 @@
+package cc.yunxi.config;
+
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 回收员微信配置
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "nxhs.wx.recycler")
+public class WxHsyProperties {
+ private String appId;
+ private String appSecret;
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/controller/ClientController.java b/nxhs-service/src/main/java/cc/yunxi/controller/ClientController.java
new file mode 100644
index 0000000..90d2c6e
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/controller/ClientController.java
@@ -0,0 +1,20 @@
+package cc.yunxi.controller;
+
+
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ *
+ * 散户信息 前端控制器
+ *
+ *
+ * @author ccongli
+ * @since 2024-02-28 06:08:09
+ */
+@RestController
+@RequestMapping("/client")
+public class ClientController {
+
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/controller/CommonController.java b/nxhs-service/src/main/java/cc/yunxi/controller/CommonController.java
new file mode 100644
index 0000000..22919fd
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/controller/CommonController.java
@@ -0,0 +1,96 @@
+package cc.yunxi.controller;
+
+import cc.yunxi.common.domain.CommonResult;
+import cc.yunxi.common.exception.BizIllegalException;
+import cc.yunxi.config.WxHsyProperties;
+import cc.yunxi.domain.dto.UserDTO;
+import cc.yunxi.domain.dto.form.WxLoginDTO;
+import cc.yunxi.domain.po.Recycler;
+import cc.yunxi.service.IRecyclerService;
+import cc.yunxi.utils.JwtTool;
+import cc.yunxi.utils.WeChatUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+
+@Api(tags = "统一接口")
+@RestController
+@RequestMapping("/common")
+@RequiredArgsConstructor
+@Slf4j
+public class CommonController {
+
+ private final String code2SessionUrl = "https://api.weixin.qq.com/sns/jscode2session";
+
+ private final IRecyclerService recyclerService;
+
+ @Resource
+ private WxHsyProperties wxHsyProperties;
+
+ @Resource
+ private JwtTool jwtTool;
+
+ @ApiOperation("登录")
+ @PostMapping("/login")
+ public CommonResult login(@RequestBody WxLoginDTO wxLoginDTO) {
+ log.info("login request body:{}", wxLoginDTO);
+ String url = code2SessionUrl +"?appid=" + wxHsyProperties.getAppId() +
+ "&secret="+ wxHsyProperties.getAppSecret() +
+ "&js_code=" + wxLoginDTO.getCode() +
+ "&grant_type=authorization_code";
+ String jsonStr = HttpUtil.get(url, 30000);
+ log.info("WXserver code2Session return {}", jsonStr);
+ JSONObject jsonObject = JSONUtil.parseObj(jsonStr);
+ if (StrUtil.contains(jsonStr, "errcode")) {
+ throw new BizIllegalException("微信登录认证失败");
+ }
+ String openId = jsonObject.getStr("openid");
+ String sessionKey = jsonObject.getStr("session_key");
+// String unionId = jsonObject.getStr("unionid");
+ // 解密得到用户手机号
+ String userInfo = WeChatUtil.decryptData(wxLoginDTO.getEncryptedData(), sessionKey, wxLoginDTO.getIv());
+ // 根据openId查找回收员 userType todo
+ Recycler recycler = recyclerService.getRecyclerByOpenid(openId);
+ if (recycler == null) {
+ // 回收员不存在,则注册
+ if (StringUtils.isEmpty(userInfo)) {
+ throw new BizIllegalException("解密用户信息错误");
+ }
+ JSONObject userObject = JSONUtil.parseObj(userInfo);
+ String phoneNumber = userObject.getStr("phoneNumber");
+ recycler = recyclerService.registerRecycler(phoneNumber, openId);
+ }
+ UserDTO userDTO = new UserDTO();
+ userDTO.setId(recycler.getId());
+ userDTO.setUserType(1); // todo
+ userDTO.setUsername(recycler.getStaffsName());
+ userDTO.setPhone(recycler.getMobilePhone());
+ String token = jwtTool.createToken(userDTO);
+ userDTO.setToken(token);
+ return CommonResult.success(userDTO);
+ }
+
+
+ @ApiOperation("退出")
+ @GetMapping("/logout")
+ public CommonResult logout() {
+ return CommonResult.success("login success");
+ }
+
+
+ @ApiOperation("用户查询接口")
+ @GetMapping("/search")
+ public CommonResult search() {
+ return CommonResult.success("users...");
+ }
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/controller/RecyclerController.java b/nxhs-service/src/main/java/cc/yunxi/controller/RecyclerController.java
new file mode 100644
index 0000000..bd56fd9
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/controller/RecyclerController.java
@@ -0,0 +1,32 @@
+package cc.yunxi.controller;
+
+import cc.yunxi.common.domain.PageDTO;
+import cc.yunxi.common.domain.PageQuery;
+import cc.yunxi.domain.dto.RecyclerDTO;
+import cc.yunxi.domain.po.Recycler;
+import cc.yunxi.service.IRecyclerService;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@Api(tags = "回收员接口")
+@RestController
+@RequestMapping("/user")
+@RequiredArgsConstructor
+public class RecyclerController {
+
+ private final IRecyclerService recyclerService;
+
+ @ApiOperation("分页查询商品")
+ @GetMapping("/page")
+ public PageDTO queryItemByPage(PageQuery query) {
+ // 1.分页查询
+ Page result = recyclerService.page(query.toMpPage("good_total", false));
+ // 2.封装并返回
+ return PageDTO.of(result, RecyclerDTO.class);
+ }
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/dto/RecyclerDTO.java b/nxhs-service/src/main/java/cc/yunxi/domain/dto/RecyclerDTO.java
new file mode 100644
index 0000000..0a6328f
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/domain/dto/RecyclerDTO.java
@@ -0,0 +1,50 @@
+package cc.yunxi.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@ApiModel(description = "回收员实体")
+public class RecyclerDTO {
+ @ApiModelProperty("回收员id")
+ private Integer id;
+
+ @ApiModelProperty("所属回收站id")
+ private String stationId; // 所属回收站
+
+ @ApiModelProperty("回收员姓名")
+ private String staffsName; // 回收员姓名
+
+ @ApiModelProperty("回收员电话")
+ private String mobilePhone; // 回收员电话
+
+ @ApiModelProperty("头像")
+ private String headIcon; // 头像
+
+ @ApiModelProperty("性别")
+ private Integer gender; // 性别
+
+ @ApiModelProperty("总里程数")
+ private Double recycleMiles; // 总里程数
+
+ @ApiModelProperty("总订单数")
+ private Integer orderTotal; // 总订单数
+
+ @ApiModelProperty("好评数")
+ private Integer goodTotal; // 好评数
+
+ @ApiModelProperty("总回收金额")
+ private BigDecimal orderAmount; // 总回收金额
+
+ @ApiModelProperty("自动接单(0-否,1-是)")
+ private Integer autoEnabled; // 自动接单(0-否,1-是)
+
+ @ApiModelProperty("状态(0-禁用,1-启用)")
+ private Integer status; // 状态(0-禁用,1-启用)
+
+ @ApiModelProperty("openid")
+ private String openid; // wx openid
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/dto/UserDTO.java b/nxhs-service/src/main/java/cc/yunxi/domain/dto/UserDTO.java
new file mode 100644
index 0000000..edd0b40
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/domain/dto/UserDTO.java
@@ -0,0 +1,21 @@
+package cc.yunxi.domain.dto;
+
+import lombok.Data;
+
+/**
+ * 用户信息 DTO
+ */
+@Data
+public class UserDTO {
+
+ private Integer userType; // 1 回收员 2 散户
+
+ private String id;
+
+ private String username;
+
+ private String phone;
+
+ private String token; // 访问token
+
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/dto/form/WxLoginDTO.java b/nxhs-service/src/main/java/cc/yunxi/domain/dto/form/WxLoginDTO.java
new file mode 100644
index 0000000..4be1064
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/domain/dto/form/WxLoginDTO.java
@@ -0,0 +1,11 @@
+package cc.yunxi.domain.dto.form;
+
+import lombok.Data;
+
+@Data
+public class WxLoginDTO {
+ private String code;
+ private String encryptedData;
+ private String iv;
+ private Integer userType; // 1 回收员 2 散户
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/po/Client.java b/nxhs-service/src/main/java/cc/yunxi/domain/po/Client.java
new file mode 100644
index 0000000..5e674e7
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/domain/po/Client.java
@@ -0,0 +1,115 @@
+package cc.yunxi.domain.po;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.math.BigDecimal;
+import java.util.Date;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ *
+ * 散户信息
+ *
+ *
+ * @author ccongli
+ * @since 2024-02-28 06:08:09
+ */
+@Getter
+@Setter
+@TableName("nx_client")
+@ApiModel(value = "Client对象", description = "散户信息")
+public class Client {
+
+ @ApiModelProperty("自然主键")
+ @TableId("id")
+ private String id;
+
+ @ApiModelProperty("微信openid")
+ @TableField("wx_openid")
+ private String wxOpenid;
+
+ @ApiModelProperty("微信名")
+ @TableField("wx_user_name")
+ private String wxUserName;
+
+ @ApiModelProperty("昵称")
+ @TableField("nick_name")
+ private String nickName;
+
+ @ApiModelProperty("手机")
+ @TableField("mobile_phone")
+ private String mobilePhone;
+
+ @ApiModelProperty("头像")
+ @TableField("head_icon")
+ private String headIcon;
+
+ @ApiModelProperty("性别")
+ @TableField("gender")
+ private Integer gender;
+
+ @ApiModelProperty("生日")
+ @TableField("birthday")
+ private Date birthday;
+
+ @ApiModelProperty("账户余额")
+ @TableField("banlance")
+ private BigDecimal banlance;
+
+ @ApiModelProperty("会员码")
+ @TableField("membership_number")
+ private String membershipNumber;
+
+ @ApiModelProperty("会员积分")
+ @TableField("membership_point")
+ private Integer membershipPoint;
+
+ @ApiModelProperty("会员等级")
+ @TableField("membership_level")
+ private Integer membershipLevel;
+
+ @ApiModelProperty("消费喜好")
+ @TableField("consume_preference")
+ private String consumePreference;
+
+ @ApiModelProperty("发票抬头")
+ @TableField("tax_title")
+ private String taxTitle;
+
+ @ApiModelProperty("企业税号")
+ @TableField("tax_id")
+ private String taxId;
+
+ @ApiModelProperty("当前位置-经度")
+ @TableField("longitude")
+ private Double longitude;
+
+ @ApiModelProperty("当前位置-纬度")
+ @TableField("latitude")
+ private Double latitude;
+
+ @ApiModelProperty("最后登录时间")
+ @TableField("last_log_time")
+ private Date lastLogTime;
+
+ @ApiModelProperty("最后登录IP")
+ @TableField("last_log_ip")
+ private String lastLogIp;
+
+ @ApiModelProperty("有效标志(0-禁用,1-启用)")
+ @TableField("enabled_mark")
+ private Integer enabledMark;
+
+ @ApiModelProperty("创建人")
+ @TableField("creator_user_id")
+ private String creatorUserId;
+
+ @ApiModelProperty("创建时间")
+ @TableField("creator_time")
+ private Date creatorTime;
+
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/po/Recycler.java b/nxhs-service/src/main/java/cc/yunxi/domain/po/Recycler.java
new file mode 100644
index 0000000..7664ce6
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/domain/po/Recycler.java
@@ -0,0 +1,48 @@
+package cc.yunxi.domain.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.math.BigDecimal;
+
+/**
+ * 回收员表
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("nx_recycle_station_staff")
+public class Recycler {
+
+ @TableId(value = "id", type = IdType.ASSIGN_UUID)
+ private String id;
+
+ private String stationId; // 所属回收站
+
+ private String staffsName; // 回收员姓名
+
+ private String mobilePhone; // 回收员电话
+
+ private String headIcon; // 头像
+
+ private Integer gender; // 性别
+
+ private Double recycleMiles; // 总里程数
+
+ private Integer orderTotal; // 总订单数
+
+ private Integer goodTotal; // 好评数
+
+ private BigDecimal orderAmount; // 总回收金额
+
+ private Integer autoEnabled; // 自动接单(0-否,1-是)
+
+ @TableField("enabled_mark")
+ private Integer status; // 状态(0-禁用,1-启用)
+
+ private String openid; // wx openid
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/query/RecyclerQuery.java b/nxhs-service/src/main/java/cc/yunxi/domain/query/RecyclerQuery.java
new file mode 100644
index 0000000..6d61cab
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/domain/query/RecyclerQuery.java
@@ -0,0 +1,22 @@
+package cc.yunxi.domain.query;
+
+import cc.yunxi.common.domain.PageQuery;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@ApiModel(description = "回收员查询条件")
+public class RecyclerQuery extends PageQuery {
+
+ @ApiModelProperty("所属回收站")
+ private String stationId;
+
+ @ApiModelProperty("联系电话")
+ private String mobilePhone;
+
+ @ApiModelProperty("回收员姓名")
+ private String staffsName;
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/interceptor/LoginInterceptor.java b/nxhs-service/src/main/java/cc/yunxi/interceptor/LoginInterceptor.java
new file mode 100644
index 0000000..df93d6e
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/interceptor/LoginInterceptor.java
@@ -0,0 +1,40 @@
+package cc.yunxi.interceptor;
+
+import cc.yunxi.domain.dto.UserDTO;
+import cc.yunxi.utils.JwtTool;
+import cc.yunxi.utils.UserContext;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.Ordered;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@RequiredArgsConstructor
+@Slf4j
+@Component
+public class LoginInterceptor implements HandlerInterceptor {
+
+ private final JwtTool jwtTool;
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+ // 1.获取请求头中的 token
+ String token = request.getHeader("authorization");
+ // 2.校验token
+ UserDTO userInfo = jwtTool.parseToken(token);
+ log.info("当前用户信息: 【{}】", userInfo);
+ // 3.存入上下文
+ UserContext.setUser(userInfo);
+ // 4.放行
+ return true;
+ }
+
+ @Override
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+ // 清理用户
+ UserContext.removeUser();
+ }
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/interceptor/StatsInterceptor.java b/nxhs-service/src/main/java/cc/yunxi/interceptor/StatsInterceptor.java
new file mode 100644
index 0000000..0e860b7
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/interceptor/StatsInterceptor.java
@@ -0,0 +1,36 @@
+package cc.yunxi.interceptor;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.Ordered;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Component
+@RequiredArgsConstructor
+@Slf4j
+public class StatsInterceptor implements HandlerInterceptor {
+
+ public static final String METHOD_REQUEST_ELAPSED_TIME = "method_request_elapsed_time";
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+ request.setAttribute(METHOD_REQUEST_ELAPSED_TIME, System.currentTimeMillis());
+ String requestURI = request.getRequestURI();
+ String method = request.getMethod();
+ String contentType = request.getContentType();
+ log.info("请求路径:【{}】, 请求方法:【{}】, 协议:【{}】", requestURI, method, contentType);
+ return true;
+ }
+
+
+ @Override
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+ long startMillis = (long) request.getAttribute(METHOD_REQUEST_ELAPSED_TIME);
+ long endMillis = System.currentTimeMillis();
+ log.info("请求耗时:【{}】ms", endMillis - startMillis);
+ }
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/mapper/ClientMapper.java b/nxhs-service/src/main/java/cc/yunxi/mapper/ClientMapper.java
new file mode 100644
index 0000000..9e9e026
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/mapper/ClientMapper.java
@@ -0,0 +1,18 @@
+package cc.yunxi.mapper;
+
+import cc.yunxi.domain.po.Client;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ *
+ * 散户信息 Mapper 接口
+ *
+ *
+ * @author ccongli
+ * @since 2024-02-28 06:08:09
+ */
+@Mapper
+public interface ClientMapper extends BaseMapper {
+
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/mapper/RecyclerMapper.java b/nxhs-service/src/main/java/cc/yunxi/mapper/RecyclerMapper.java
new file mode 100644
index 0000000..e8438c9
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/mapper/RecyclerMapper.java
@@ -0,0 +1,14 @@
+package cc.yunxi.mapper;
+
+import cc.yunxi.domain.po.Recycler;
+import cc.yunxi.domain.po.Test;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 回收员表 mapper接口
+ */
+@Mapper
+public interface RecyclerMapper extends BaseMapper {
+
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/service/ClientService.java b/nxhs-service/src/main/java/cc/yunxi/service/ClientService.java
new file mode 100644
index 0000000..8c6c5d5
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/service/ClientService.java
@@ -0,0 +1,16 @@
+package cc.yunxi.service;
+
+import cc.yunxi.domain.po.Client;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ *
+ * 散户信息 服务类
+ *
+ *
+ * @author ccongli
+ * @since 2024-02-28 06:08:09
+ */
+public interface ClientService extends IService {
+
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/service/IRecyclerService.java b/nxhs-service/src/main/java/cc/yunxi/service/IRecyclerService.java
new file mode 100644
index 0000000..4d2928f
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/service/IRecyclerService.java
@@ -0,0 +1,23 @@
+package cc.yunxi.service;
+
+import cc.yunxi.domain.po.Recycler;
+import cc.yunxi.domain.po.Test;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * 测试表 业务接口
+ */
+public interface IRecyclerService extends IService {
+
+ /**
+ * 根据openid获取回收员信息
+ * @param openid
+ * @return Recycler
+ */
+ Recycler getRecyclerByOpenid(String openid);
+
+ /**
+ * 手机号注册回收员
+ */
+ Recycler registerRecycler(String phoneNumber, String openId);
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/service/impl/ClientServiceImpl.java b/nxhs-service/src/main/java/cc/yunxi/service/impl/ClientServiceImpl.java
new file mode 100644
index 0000000..98008c1
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/service/impl/ClientServiceImpl.java
@@ -0,0 +1,20 @@
+package cc.yunxi.service.impl;
+
+import cc.yunxi.domain.po.Client;
+import cc.yunxi.mapper.ClientMapper;
+import cc.yunxi.service.ClientService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ *
+ * 散户信息 服务实现类
+ *
+ *
+ * @author ccongli
+ * @since 2024-02-28 06:08:09
+ */
+@Service
+public class ClientServiceImpl extends ServiceImpl implements ClientService {
+
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/service/impl/RecyclerServiceImpl.java b/nxhs-service/src/main/java/cc/yunxi/service/impl/RecyclerServiceImpl.java
new file mode 100644
index 0000000..643b70e
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/service/impl/RecyclerServiceImpl.java
@@ -0,0 +1,46 @@
+package cc.yunxi.service.impl;
+
+import cc.yunxi.common.exception.BizIllegalException;
+import cc.yunxi.domain.po.Recycler;
+import cc.yunxi.mapper.RecyclerMapper;
+import cc.yunxi.service.IRecyclerService;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+
+/**
+ * 测试表 服务实现类
+ */
+@Service
+public class RecyclerServiceImpl extends ServiceImpl implements IRecyclerService {
+
+ @Resource
+ RecyclerMapper recyclerMapper;
+
+ @Override
+ public Recycler getRecyclerByOpenid(String openid) {
+ Recycler recycler = lambdaQuery().eq(Recycler::getOpenid, openid).one();
+ return recycler;
+ }
+
+ @Override
+ @Transactional
+ public Recycler registerRecycler(String phoneNumber, String openId) {
+ // 判断手机号是否被注册
+ Recycler recycler = lambdaQuery().eq(Recycler::getMobilePhone, phoneNumber).one();
+ if (ObjectUtil.isNotEmpty(recycler)) {
+ throw new BizIllegalException("该微信号已被注册");
+ }
+ recycler = new Recycler();
+ recycler.setMobilePhone(phoneNumber);
+ recycler.setStaffsName(phoneNumber);
+ recycler.setOpenid(openId);
+ recyclerMapper.insert(recycler);
+ return recycler;
+ }
+}
diff --git a/nxhs-service/src/main/java/cc/yunxi/utils/JwtTool.java b/nxhs-service/src/main/java/cc/yunxi/utils/JwtTool.java
new file mode 100644
index 0000000..fceca06
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/utils/JwtTool.java
@@ -0,0 +1,100 @@
+package cc.yunxi.utils;
+
+import cc.yunxi.common.exception.UnauthorizedException;
+import cc.yunxi.config.JwtProperties;
+import cc.yunxi.domain.dto.UserDTO;
+import cn.hutool.core.exceptions.ValidateException;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import cn.hutool.jwt.JWT;
+import cn.hutool.jwt.JWTValidator;
+import cn.hutool.jwt.signers.JWTSigner;
+import cn.hutool.jwt.signers.JWTSignerUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+import java.security.KeyPair;
+import java.time.Duration;
+import java.util.Date;
+
+@Component
+public class JwtTool {
+ private final JWTSigner jwtSigner;
+
+ @Autowired
+ @Lazy
+ private JwtProperties jwtProperties;
+
+ public JwtTool(KeyPair keyPair) {
+ this.jwtSigner = JWTSignerUtil.createSigner("rs256", keyPair);
+ }
+
+ /**
+ * 创建 access-token
+ *
+ * @param userDTO 用户信息
+ * @return access-token
+ */
+ public String createToken(UserDTO userDTO) {
+ // 1.生成jws
+ return JWT.create()
+ .setPayload("user", userDTO)
+ .setExpiresAt(new Date(System.currentTimeMillis() + jwtProperties.getTokenTTL().toMillis()))
+ .setSigner(jwtSigner)
+ .sign();
+ }
+
+
+ /**
+ * 验证 token
+ */
+ public JWT verifyToken(String token) {
+ // 1.校验token是否为空
+ if (token == null) {
+ throw new UnauthorizedException("请先登录");
+ }
+ // 2.校验jwt
+ JWT jwt;
+ try {
+ jwt = JWT.of(token).setSigner(jwtSigner);
+ } catch (Exception e) {
+ throw new UnauthorizedException("无效的token", e);
+ }
+ // 2.校验jwt是否有效
+ if (!jwt.verify()) {
+ // 验证失败
+ throw new UnauthorizedException("无效的token");
+ }
+ // 3.校验是否过期
+ try {
+ JWTValidator.of(jwt).validateDate();
+ } catch (ValidateException e) {
+ throw new UnauthorizedException("token已过期");
+ }
+ return jwt;
+ }
+
+ /**
+ * 解析token
+ *
+ * @param token token
+ * @return 解析刷新token得到的用户信息
+ */
+ public UserDTO parseToken(String token) {
+ JWT jwt = verifyToken(token);
+ // 2.数据格式校验
+ JSONObject userPayload = (JSONObject) jwt.getPayload("user");
+ if (userPayload == null) {
+ // 数据为空
+ throw new UnauthorizedException("无效的token");
+ }
+ // 3.数据解析
+ try {
+ return JSONUtil.toBean(userPayload, UserDTO.class);
+ } catch (RuntimeException e) {
+ // 数据格式有误
+ throw new UnauthorizedException("无效的token", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/nxhs-common/src/main/java/cc/yunxi/common/utils/UserContext.java b/nxhs-service/src/main/java/cc/yunxi/utils/UserContext.java
similarity index 51%
rename from nxhs-common/src/main/java/cc/yunxi/common/utils/UserContext.java
rename to nxhs-service/src/main/java/cc/yunxi/utils/UserContext.java
index bdd0ec7..55c6ac1 100644
--- a/nxhs-common/src/main/java/cc/yunxi/common/utils/UserContext.java
+++ b/nxhs-service/src/main/java/cc/yunxi/utils/UserContext.java
@@ -1,21 +1,26 @@
-package cc.yunxi.common.utils;
+package cc.yunxi.utils;
+import cc.yunxi.domain.dto.UserDTO;
+
+/**
+ * 用户信息上下文
+ */
public class UserContext {
- private static final ThreadLocal tl = new ThreadLocal<>();
+ private static final ThreadLocal tl = new ThreadLocal<>();
/**
* 保存当前登录用户信息到ThreadLocal
- * @param userId 用户id
+ * @param userInfo 用户id
*/
- public static void setUser(Long userId) {
- tl.set(userId);
+ public static void setUser(UserDTO userInfo) {
+ tl.set(userInfo);
}
/**
* 获取当前登录用户信息
* @return 用户id
*/
- public static Long getUser() {
+ public static UserDTO getUser() {
return tl.get();
}
diff --git a/nxhs-service/src/main/java/cc/yunxi/utils/WeChatUtil.java b/nxhs-service/src/main/java/cc/yunxi/utils/WeChatUtil.java
new file mode 100644
index 0000000..3965457
--- /dev/null
+++ b/nxhs-service/src/main/java/cc/yunxi/utils/WeChatUtil.java
@@ -0,0 +1,173 @@
+package cc.yunxi.utils;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.json.JSONObject;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ssl.HttpsURLConnection;
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.security.Key;
+import java.security.Security;
+import java.util.Arrays;
+
+/**
+ * 微信小程序工具类
+ *
+ * @author zhuhuix
+ * @date 2019-12-25
+ */
+public class WeChatUtil {
+ private static final String KEY_ALGORITHM = "AES";
+ private static final String ALGORITHM_STR = "AES/CBC/PKCS7Padding";
+ private static Key key;
+ private static Cipher cipher;
+
+ private static void init(byte[] keyBytes) {
+ // 如果密钥不足16位,那么就补足. 这个if 中的内容很重要
+ int base = 16;
+ if (keyBytes.length % base != 0) {
+ int groups = keyBytes.length / base + 1;
+ byte[] temp = new byte[groups * base];
+ Arrays.fill(temp, (byte) 0);
+ System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
+ keyBytes = temp;
+ }
+ // 初始化
+ Security.addProvider(new BouncyCastleProvider());
+ // 转化成JAVA的密钥格式
+ key = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
+ try {
+ // 初始化cipher
+ cipher = Cipher.getInstance(ALGORITHM_STR, "BC");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static String decryptData(String encryptDataB64, String sessionKeyB64, String ivB64) {
+ return new String(
+ decryptOfDiyIv(
+ Base64.decode(encryptDataB64),
+ Base64.decode(sessionKeyB64),
+ Base64.decode(ivB64)
+ )
+ );
+ }
+
+ /**
+ * 解密方法
+ *
+ * @param encryptedData 要解密的字符串
+ * @param keyBytes 解密密钥
+ * @param ivs 自定义对称解密算法初始向量 iv
+ * @return 解密后的字节数组
+ */
+ private static byte[] decryptOfDiyIv(byte[] encryptedData, byte[] keyBytes, byte[] ivs) {
+ byte[] encryptedText = null;
+ init(keyBytes);
+ try {
+ cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivs));
+ encryptedText = cipher.doFinal(encryptedData);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return encryptedText;
+ }
+
+ public static String httpRequest(String requestUrl, String requestMethod, String output) {
+ try {
+ URL url = new URL(requestUrl);
+ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
+ connection.setDoOutput(true);
+ connection.setDoInput(true);
+ connection.setUseCaches(false);
+ connection.setRequestMethod(requestMethod);
+ if (null != output) {
+ OutputStream outputStream = connection.getOutputStream();
+ outputStream.write(output.getBytes(StandardCharsets.UTF_8));
+ outputStream.close();
+ }
+ // 从输入流读取返回内容
+ InputStream inputStream = connection.getInputStream();
+ InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
+ BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
+ String str;
+ StringBuilder buffer = new StringBuilder();
+ while ((str = bufferedReader.readLine()) != null) {
+ buffer.append(str);
+ }
+ bufferedReader.close();
+ inputStreamReader.close();
+ inputStream.close();
+ connection.disconnect();
+ return buffer.toString();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+
+ /**
+ * 向指定 URL 发送POST方法的请求
+ *
+ * @param url 发送请求的 URL
+ * @param json 请求参数,请求参数应该是 json 的形式。
+ * @return 所代表远程资源的响应结果
+ */
+ public static String httpPost(String url, JSONObject json) {
+ PrintWriter out = null;
+ BufferedReader in = null;
+ String result = "";
+ try {
+ URL realUrl = new URL(url);
+ // 打开和URL之间的连接
+ URLConnection conn = realUrl.openConnection();
+ // 设置通用的请求属性
+ conn.setRequestProperty("accept", "*/*");
+ conn.setRequestProperty("connection", "Keep-Alive");
+ conn.setRequestProperty("user-agent",
+ "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+ // 发送POST请求必须设置如下两行
+ conn.setDoOutput(true);
+ conn.setDoInput(true);
+ // 获取URLConnection对象对应的输出流
+ out = new PrintWriter(conn.getOutputStream());
+ // 发送请求参数
+ out.print(json);
+ // flush输出流的缓冲
+ out.flush();
+ // 定义BufferedReader输入流来读取URL的响应
+ in = new BufferedReader(
+ new InputStreamReader(conn.getInputStream()));
+ String line;
+ while ((line = in.readLine()) != null) {
+ result=result.concat(line);
+ }
+ } catch (Exception e) {
+ System.out.println("发送 POST 请求出现异常!" + e);
+ e.printStackTrace();
+ }
+ //使用finally块来关闭输出流、输入流
+ finally {
+ try {
+ if (out != null) {
+ out.close();
+ }
+ if (in != null) {
+ in.close();
+ }
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/nxhs-service/src/main/resources/application.yml b/nxhs-service/src/main/resources/application.yml
index 561895f..41dbd32 100644
--- a/nxhs-service/src/main/resources/application.yml
+++ b/nxhs-service/src/main/resources/application.yml
@@ -1,5 +1,7 @@
server:
port: 8080
+ servlet:
+ context-path: /api
spring:
application:
@@ -56,16 +58,18 @@ knife4j:
api-rule: package
api-rule-resources:
- cc.yunxi.controller
-#hm:
-# jwt:
-# location: classpath:hmall.jks
-# alias: hmall
-# password: hmall123
-# tokenTTL: 30m
-# auth:
-# excludePaths:
-# - /search/**
-# - /users/login
-# - /items/**
-# - /hi
-# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123
\ No newline at end of file
+nxhs:
+ jwt:
+ location: classpath:nxhs.jks
+ alias: nxhs
+ password: nxhs2024
+ tokenTTL: 1h
+ auth:
+ excludePaths:
+ - /common/login
+ - /test/**
+ wx:
+ recycler: # 回收员端
+ appid: wxf82bcc798891a29d
+ appsecret: f37fb0ab2b5f691d8507acced60a58fb
+# keytool -genkeypair -alias nxhs -keyalg RSA -keypass nxhs2024 -keystore nxhs.jks -storepass nxhs2024
\ No newline at end of file
diff --git a/nxhs-service/src/main/resources/logback-spring.xml b/nxhs-service/src/main/resources/logback-spring.xml
index 50ea71c..ce1c7a6 100644
--- a/nxhs-service/src/main/resources/logback-spring.xml
+++ b/nxhs-service/src/main/resources/logback-spring.xml
@@ -177,28 +177,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/nxhs-service/src/main/resources/mapper/ClientMapper.xml b/nxhs-service/src/main/resources/mapper/ClientMapper.xml
new file mode 100644
index 0000000..807c4a4
--- /dev/null
+++ b/nxhs-service/src/main/resources/mapper/ClientMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/nxhs-service/src/main/resources/mapper/RecyclerMapper.xml b/nxhs-service/src/main/resources/mapper/RecyclerMapper.xml
new file mode 100644
index 0000000..807c4a4
--- /dev/null
+++ b/nxhs-service/src/main/resources/mapper/RecyclerMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/nxhs-service/src/main/resources/nxhs.jks b/nxhs-service/src/main/resources/nxhs.jks
new file mode 100644
index 0000000000000000000000000000000000000000..b2107ce68bc9de07c6e43e83f55b55e246a4f9d0
GIT binary patch
literal 2217
zcmchY`8(7L7r^H`Gp0eN##+Q=>t-3g2qVUt>d~=?iJ$de+-E<
z8-ZgB69AInM<5EpB7p$lfH)Kl089`#3BJ+^I3%pJ&oMI3ye(Hy=0p|9j;jUOrB@s!
zIDBw!QjZ+6ScN*vq@Mk1nvB_q^3+$u2l+qBE*pLA!MAZP=AEDy>W^j>i5mKVwMS4g
zbh`v%Vy=j@)oM#*O_XlG%$yjd=dMs^sj
z!S-67QtP5#7q|UBU?+%~E|NJ?&y}JfU>P^byI#e9^Es+cd16!Kr1f0(Dv
ze!geZz29{|&3#%hFZk|b=Sjpf5v&iMl0>QHZ*F>r4*yo_?60swy1CA;C^>5y9HY_g
zV!h;j8UT
z8_*uX<2pQbuJ6>Uu^l>jOkJa&U!za3RJ4YQ3iI@WQQ?(heAOT8avzJ3o+ds_Fk7fx
ztKdf+V%U`n69|*})ti#5&Rd5mXLy%po@Td7sA_(>LEdY%*jHREQg4{fX*OtSFj&*t
zu@~D?b$&@L_p4@J2yAy7dy}%bnr_VUC`po<)YYrwxpjBq4Iz=KSW9k=xvWI<^cmPn
zcUJY11xaM{e5kMI9jTh|`>evmGhD{M51XQ8ecJ*kgv}%FC12Akj>!a@{!6EdJ?$i$
z@~~AAk}Ua2lHH}RimdxWyK!AA5Q@-aDb@F+!CD~W!2lO_5Z{SZLtGpZya=?DHhb`@
z1Ze=~-}*iK<#%Izg787h{wEvH)~?jrPsLnRG^P^yIfonTw0Ko`+NCFQGq7W9z>P86
z6cEPYPttrf!*~cWD0}XEWqUH>F=m6*AS8&c1(SwvRWD-{
zL+TBw2R;vmhcryNR)yKy2AHw+p)eC!Vn7?+LL2Vw>WVkL(3P+9s6ue7g!rD`Fk?$!
z)rOBnI)172H&yf$U+&=EkHu+DTkE-)m+|KYr|E}$qG5$d3b*`{jCGg0OaijWg~psu
zDYH_+K9?jY6;h)_%bjGxmnM7_Grw+$pIIGc)Qq#esWoisF8((scho}2_?i|mOENoq
z+!9FJ9Uq*-k?a`x8u|7kUq1+FD9qfzk1sDW9f)ki*1U=5aMJlRu)5LRM=_=4tj=jN
zafUE6a=6w?(+VNRc}Jv16`m-DfQz{ksEC+oG2C~nNCi28iJ_i|L8Z}y8kz6+!M?y*$cl3_*x8+aYE0gw{9|0TL?4X$6*_DgdO@4OOU@rq>K>7dS
z{~-esAoO$D$4>!90!~3d62Jl_0RZF;)lGl7q?Ltzk0m($xnbz)t$+l~p!xNnT2#Y>
zOH0MME$x{N*n-AkH9_wAgsYo*nZ)}Av6h(}vC?S{uyV2@Evu(9m-ND}KqTMDXNBg=
z9bx&Z*SW_SaFWUh`pqtiRiwuI$>@v8S4m_ZhP|tF~
z7NM$sf%Tras4f8Oex~xM_7eu5cjE#2e#~{5kBj~@R;)N=luG~5
zw0d;aYGDTu61yFmSDQA6irc*~IZqH*u_)kdZ4<1#@Y*
zZReA2U$rL#OLqSlkio}S-=SBgFJ!j(a-n=5w2^Ms;~xLK!I)O6k9+E?(-$?R1wU_u
gZEj5LgTAk_+o=qUJ{UAjD-Q1({CJ{eGqTCxFR|9*PXGV_
literal 0
HcmV?d00001
diff --git a/nxhs-service/src/test/java/cc/yunxi/NxhsApplicationTest.java b/nxhs-service/src/test/java/cc/yunxi/NxhsApplicationTest.java
index c706e6c..ccbefc1 100644
--- a/nxhs-service/src/test/java/cc/yunxi/NxhsApplicationTest.java
+++ b/nxhs-service/src/test/java/cc/yunxi/NxhsApplicationTest.java
@@ -1,10 +1,13 @@
package cc.yunxi;
+import cc.yunxi.domain.dto.UserDTO;
import cc.yunxi.service.ITestService;
+import cc.yunxi.utils.JwtTool;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
+import java.time.Duration;
import java.util.List;
@SpringBootTest
@@ -13,9 +16,24 @@ public class NxhsApplicationTest {
@Resource
ITestService testService;
+ @Resource
+ JwtTool jwtTool;
+
@Test
void test01() {
List list = testService.list();
System.out.println(list);
}
+
+
+ @Test
+ void test02() {
+ UserDTO userDTO = new UserDTO();
+ userDTO.setId("10086");
+ userDTO.setPhone("15137603460");
+ userDTO.setUsername("小冲");
+ String token = jwtTool.createToken(userDTO);
+ UserDTO userInfo = jwtTool.parseToken(token);
+ System.out.println(userInfo);
+ }
}
diff --git a/pom.xml b/pom.xml
index ad93d1a..40edc15 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,9 +23,10 @@
2.7.6
3.4.3
5.8.11
+ 4.1.0
+ 1.0.9.RELEASE
-
@@ -37,59 +38,49 @@
pom
import
+
+ cn.hutool
+ hutool-all
+ ${hutool.version}
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+ ${mybatis-plus.version}
+
+
+
+ com.github.xiaoymin
+ knife4j-openapi2-spring-boot-starter
+ ${knife4j-openapi2.version}
+
+
+
+ org.springframework.security
+ spring-security-rsa
+ ${security-rsa.version}
+
-
-
-
- com.mysql
- mysql-connector-j
- runtime
-
-
- com.baomidou
- mybatis-plus-boot-starter
- ${mybatis-plus.version}
-
-
+
org.springframework.boot
- spring-boot-starter-data-redis
-
-
-
- com.fasterxml.jackson.dataformat
- jackson-dataformat-xml
+ spring-boot-starter-test
+ test
-
+
org.projectlombok
lombok
true
-
- cn.hutool
- hutool-all
- ${hutool.version}
-
-
-
- org.hibernate.validator
- hibernate-validator
-
-
-
- com.github.xiaoymin
- knife4j-openapi2-spring-boot-starter
- 4.1.0
-
-
org.springframework.boot
- spring-boot-starter-test
- test
+ spring-boot-configuration-processor
+ true
@@ -125,7 +116,6 @@
-
alimaven