Commit c0f8cb8e by giaogiao

im

parent 0ba3859c
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**
!**/src/test/**
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
### VS Code ###
.vscode/
/src/main/resources/rebel.xml
# Getting Started
### Reference Documentation
For further reference, please consider the following sections:
* [Spring Security](https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#boot-features-security)
* [Spring Boot Actuator](https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#production-ready)
* [Spring Boot Admin (Client)](https://codecentric.github.io/spring-boot-admin/current/#getting-started)
* [Spring Boot Admin (Server)](https://codecentric.github.io/spring-boot-admin/current/#getting-started)
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.geekidea.springbootplus</groupId>
<artifactId>parent</artifactId>
<version>2.0</version>
</parent>
<artifactId>admin</artifactId>
<name>admin</name>
<description>Spring Boot Admin Server</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>${spring-boot-admin.version}</version>
<exclusions>
<exclusion>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-cloud</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
/*
* Copyright 2019-2029 geekidea(https://github.com/geekidea)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.geekidea.springbootplus.admin;
import de.codecentric.boot.admin.server.config.EnableAdminServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* Spring Boot Admin 启动类
*
* @author geekidea
* @date 2020/3/20
**/
@Slf4j
@Configuration
@EnableAutoConfiguration
@EnableAdminServer
@SpringBootApplication
public class SpringBootPlusAdminApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringBootPlusAdminApplication.class, args);
ConfigurableEnvironment environment = context.getEnvironment();
String serverPort = environment.getProperty("server.port");
log.info("SpringBootAdmin: http://localhost:" + serverPort);
}
}
/*
* Copyright 2019-2029 geekidea(https://github.com/geekidea)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.geekidea.springbootplus.admin.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* WebMvc配置
*
* @author geekidea
* @date 2020/3/19
*/
@Configuration
public class AdminWebMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 设置项目静态资源访问
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
}
/*
* Copyright 2019-2029 geekidea(https://github.com/geekidea)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.geekidea.springbootplus.admin.config;
import de.codecentric.boot.admin.server.config.AdminServerProperties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import java.util.UUID;
/**
* Spring Boot Admin Security配置
* https://codecentric.github.io/spring-boot-admin/current/#_securing_spring_boot_admin_server
*
* @author geekidea
* @date 2020/3/20
*/
@Configuration(proxyBeanMethods = false)
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
private final AdminServerProperties adminServer;
@Value("${spring-boot-plus.admin.username}")
private String username;
@Value("${spring-boot-plus.admin.password}")
private String password;
public SecuritySecureConfig(AdminServerProperties adminServer) {
this.adminServer = adminServer;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
successHandler.setDefaultTargetUrl(this.adminServer.path("/"));
http.authorizeRequests(
(authorizeRequests) -> authorizeRequests
.antMatchers(this.adminServer.path("/assets/**")).permitAll()
.antMatchers(this.adminServer.path("/static/**")).permitAll()
.antMatchers(this.adminServer.path("/login")).permitAll()
.anyRequest().authenticated()
).formLogin(
(formLogin) -> formLogin.loginPage(this.adminServer.path("/login")).successHandler(successHandler).and()
).logout((logout) -> logout.logoutUrl(this.adminServer.path("/logout"))).httpBasic(Customizer.withDefaults())
.csrf((csrf) -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringRequestMatchers(
new AntPathRequestMatcher(this.adminServer.path("/instances"),
HttpMethod.POST.toString()),
new AntPathRequestMatcher(this.adminServer.path("/instances/*"),
HttpMethod.DELETE.toString()),
new AntPathRequestMatcher(this.adminServer.path("/actuator/**"))
))
.rememberMe((rememberMe) -> rememberMe.key(UUID.randomUUID().toString()).tokenValiditySeconds(1209600));
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser(username).password("{noop}" + password).roles("USER");
}
}
\ No newline at end of file
server:
port: 8000
# Spring Boot Admin Server配置
spring:
boot:
admin:
monitor:
period: 100000
status-lifetime: 100000
connect-timeout: 100000
read-timeout: 100000
ui:
external-views:
- label: "🚀"
url: https://springboot.plus
order: 2000
# Spring Boot Admin 登录账号密码
spring-boot-plus:
admin:
username: admin
password: admin
/target/
/classes
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/build/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
.DS_Store
*.log
logs
*.rdb
# system 项目系统模块
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.geekidea.springbootplus</groupId>
<artifactId>parent</artifactId>
<version>2.0</version>
</parent>
<artifactId>api-app</artifactId>
<name>api-app</name>
<description>app的api服务模块</description>
<dependencies>
<dependency>
<groupId>io.geekidea.springbootplus</groupId>
<artifactId>framework</artifactId>
</dependency>
<dependency>
<groupId>io.geekidea.springbootplus</groupId>
<artifactId>common</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!--
This is the JRebel configuration file. It maps the running application to your IDE workspace, enabling JRebel reloading for this project.
Refer to https://manuals.jrebel.com/jrebel/standalone/config.html for more information.
-->
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" generated-by="intellij"
xmlns="http://www.zeroturnaround.com"
xsi:schemaLocation="http://www.zeroturnaround.com http://update.zeroturnaround.com/jrebel/rebel-2_1.xsd">
<classpath>
<dir name="/Users/giaogiao/Documents/hewei/code/gitPull/spring-boot-plus-master/service/target/classes">
</dir>
</classpath>
</application>
......@@ -15,31 +15,14 @@
<description>项目启动模块</description>
<dependencies>
<!-- <dependency>-->
<!-- <groupId>de.codecentric</groupId>-->
<!-- <artifactId>spring-boot-admin-starter-client</artifactId>-->
<!-- <version>${spring-boot-admin.version}</version>-->
<!-- </dependency>-->
<dependency>
<groupId>io.geekidea.springbootplus</groupId>
<artifactId>framework</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>io.geekidea.springbootplus</groupId>-->
<!-- <artifactId>api-merchant</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>io.geekidea.springbootplus</groupId>
<artifactId>api-app</artifactId>
<artifactId>common</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>io.geekidea.springbootplus</groupId>-->
<!-- <artifactId>api-system</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>io.geekidea.springbootplus</groupId>-->
<!-- <artifactId>scheduled</artifactId>-->
<!-- </dependency>-->
</dependencies>
<build>
......
......@@ -18,6 +18,8 @@ package io.geekidea.springbootplus;
import io.geekidea.springbootplus.framework.util.PrintApplicationInfo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
......@@ -28,6 +30,8 @@ import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.util.concurrent.CountDownLatch;
/**
* spring-boot-plus 项目启动入口
*
......@@ -42,15 +46,30 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
@ConfigurationPropertiesScan("com.sien.common.config")
@MapperScan({"io.geekidea.springbootplus.**.mapper", "com.sien.**.mapper"})
@SpringBootApplication(scanBasePackages = {"io.geekidea.springbootplus", "com.sien", "com.sien.common", "com.sien.common.config"})
public class SpringBootPlusApplication {
public class SpringBootPlusApplication implements CommandLineRunner {
// @Autowired
// private NettyStart nettyServer;
@Value("${netty.port}")
private int port;
public static void main(String[] args) {
private static CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) throws InterruptedException {
// 启动spring-boot-plus
ConfigurableApplicationContext context = SpringApplication.run(SpringBootPlusApplication.class, args);
// 打印项目信息
PrintApplicationInfo.print(context);
// 打印项目提示
PrintApplicationInfo.printTip(context);
// countDownLatch.await();
}
@Override
public void run(String... args) throws Exception {
// nettyServer.run(port);
}
}
package io.geekidea.springbootplus.test;
import com.sien.common.factory.PushFactory;
import com.sien.common.service.DonationRecordService;
import com.sien.common.vo.DonationRankAndTotal;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.mp.api.WxMpService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -19,11 +14,7 @@ public class SpiringPlusTest {
@Autowired
private PushFactory pushFactory;
@Autowired
private WxMpService wxMpService;
@Autowired
private DonationRecordService donationRecordService;
/**
......@@ -43,27 +34,6 @@ public class SpiringPlusTest {
}
@Test
public void wxGetOpenId() throws Exception {
String code = "071iQYGa1jwhCA06vtFa1OHuHE2iQYGq";
WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(code);
// 获取用户头像
WxOAuth2UserInfo userInfo = wxMpService.getOAuth2Service().getUserInfo(accessToken, null);
int ii = 1;
}
@Test
public void donationTest() throws Exception {
DonationRankAndTotal donationRankAndTotal = donationRecordService.getDonationRankAndTotal(1L);
int ii = 1;
};
......
package io.geekidea.springbootplus.test;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* 微信支付测试
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class WxPayTest {
@Autowired
private WxPayService wxService;
/**
* 调用统一下单接口,并组装生成支付所需参数对象.
*
* @param request 统一下单请求参数
* @param <T> 请使用{@link com.github.binarywang.wxpay.bean.order}包下的类
* @return 返回 {@link com.github.binarywang.wxpay.bean.order}包下的类对象
*/
// @ApiOperation(value = "统一下单,并组装所需支付参数")
// @PostMapping("/createOrder")
public <T> T createOrder(WxPayUnifiedOrderRequest request) throws WxPayException {
return this.wxService.createOrder(request);
}
@Test
public void order() {
WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = new WxPayUnifiedOrderRequest();
wxPayUnifiedOrderRequest.setBody("会员充值");
// 商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
wxPayUnifiedOrderRequest.setOutTradeNo("00001");
//订单总金额,单位为分,详见支付金额
wxPayUnifiedOrderRequest.setTotalFee(1);
// APP和网页支付提交用户端ip
wxPayUnifiedOrderRequest.setSpbillCreateIp("111.111.222.33");
// 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
wxPayUnifiedOrderRequest.setNotifyUrl("http");
wxPayUnifiedOrderRequest.setOpenid("ogXcu56o2ZYi-MgLRkSklKbe-PdU");
wxPayUnifiedOrderRequest.setTradeType("JSAPI");
Object order=null;
try {
order = createOrder(wxPayUnifiedOrderRequest);
} catch (WxPayException e) {
e.printStackTrace();
}
int i =1;
}
}
......@@ -21,27 +21,66 @@
<artifactId>framework</artifactId>
</dependency>
<!-- 公众号(包括订阅号和服务号):weixin-java-mp -->
<!-- netty-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>4.0.0</version>
<groupId>io.netty</groupId>
<artifactId>netty-codec</artifactId>
<version>4.1.25.Final</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>4.0.0</version>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
<version>4.1.25.Final</version>
</dependency>
<!-- fastbootWeixin的核心依赖 -->
<dependency>
<groupId>com.mxixm</groupId>
<artifactId>fastboot-weixin</artifactId>
<version>0.6.2</version>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<version>4.1.25.Final</version>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http</artifactId>
<version>4.1.25.Final</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
<version>4.1.25.Final</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-udt</artifactId>
<version>4.1.25.Final</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-sctp</artifactId>
<version>4.1.25.Final</version>
</dependency>
<!-- netty-->
<!-- &lt;!&ndash; 公众号(包括订阅号和服务号):weixin-java-mp &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.github.binarywang</groupId>-->
<!-- <artifactId>weixin-java-mp</artifactId>-->
<!-- <version>4.0.0</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>com.github.binarywang</groupId>-->
<!-- <artifactId>weixin-java-pay</artifactId>-->
<!-- <version>4.0.0</version>-->
<!-- </dependency>-->
<!-- &lt;!&ndash; fastbootWeixin的核心依赖 &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.mxixm</groupId>-->
<!-- <artifactId>fastboot-weixin</artifactId>-->
<!-- <version>0.6.2</version>-->
<!-- </dependency>-->
<!-- SpringBoot的web项目,必须 -->
<dependency>
......
package com.sien.common.config;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import tillo.app_ws.model.Constants;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 异步注解的线程池数量
*/
@Configuration
@ComponentScan("com")
@EnableAsync //开启异步任务支持如果Application已经开启,则这个可以省略
public class TaskExecutorConfig implements AsyncConfigurer {
// 此配置可以解决Could not find default TaskExecutor bean的debug异常
// 实现AsyncConfigurer接口并重写getAsyncExecutor方法,并返回一个ThreadPoolTask​​Executor,
// 这样我们就获得了一个基于线程池TaskExecutor
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//当前线程数
taskExecutor.setCorePoolSize(Constants.CPU_PROCESSORS * 10);
// 最大线程数
taskExecutor.setMaxPoolSize(Constants.CPU_PROCESSORS * 30);
//线程池所使用的缓冲队列
taskExecutor.setQueueCapacity(2048);
// 空闲线程存活时间
taskExecutor.setKeepAliveSeconds(0);
// 线程名称前缀
taskExecutor.setThreadNamePrefix("MyAsync-");
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 执行初始化
taskExecutor.initialize();
return taskExecutor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
\ No newline at end of file
package com.sien.common.config;
import io.geekidea.springbootplus.config.properties.WxMpProperties;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.stream.Collectors;
/**
* wechat mp configuration
*
* @author Binary Wang(https://github.com/binarywang)
*/
@Configuration
//@EnableConfigurationProperties(WxMpProperties.class)
public class WxMpConfiguration {
// private final LogHandler logHandler;
// private final NullHandler nullHandler;
// private final KfSessionHandler kfSessionHandler;
// private final StoreCheckNotifyHandler storeCheckNotifyHandler;
// private final LocationHandler locationHandler;
// private final MenuHandler menuHandler;
// private final MsgHandler msgHandler;
// private final UnsubscribeHandler unsubscribeHandler;
// private final SubscribeHandler subscribeHandler;
// private final ScanHandler scanHandler;
// private final WxMpProperties properties;
@Autowired
private WxMpProperties properties;
@Bean
public WxMpService wxMpService() {
// 代码里 getConfigs()处报错的同学,请注意仔细阅读项目说明,你的IDE需要引入lombok插件!!!!
final List<WxMpProperties.MpConfig> configs = this.properties.getConfigs();
if (configs == null) {
throw new RuntimeException("添加下相关配置,注意别配错了!");
}
WxMpService service = new WxMpServiceImpl();
service.setMultiConfigStorages(configs
.stream().map(a -> {
WxMpDefaultConfigImpl configStorage;
// if (this.properties.isUseRedis()) {
// final WxMpProperties.RedisConfig redisConfig = this.properties.getRedisConfig();
// JedisPool jedisPool = new JedisPool(redisConfig.getHost(), redisConfig.getPort());
// configStorage = new WxMpRedisConfigImpl(new JedisWxRedisOps(jedisPool), a.getAppId());
// } else {
configStorage = new WxMpDefaultConfigImpl();
// }
configStorage.setAppId(a.getAppId());
configStorage.setSecret(a.getSecret());
configStorage.setToken(a.getToken());
configStorage.setAesKey(a.getAesKey());
return configStorage;
}).collect(Collectors.toMap(WxMpDefaultConfigImpl::getAppId, a -> a, (o, n) -> o)));
return service;
}
// @Bean
// public WxMpMessageRouter messageRouter(WxMpService wxMpService) {
// final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);
//
// // 记录所有事件的日志 (异步执行)
// newRouter.rule().handler(this.logHandler).next();
//
// // 接收客服会话管理事件
// newRouter.rule().async(false).msgType(EVENT).event(KF_CREATE_SESSION)
// .handler(this.kfSessionHandler).end();
// newRouter.rule().async(false).msgType(EVENT).event(KF_CLOSE_SESSION)
// .handler(this.kfSessionHandler).end();
// newRouter.rule().async(false).msgType(EVENT).event(KF_SWITCH_SESSION)
// .handler(this.kfSessionHandler).end();
//
// // 门店审核事件
// newRouter.rule().async(false).msgType(EVENT).event(POI_CHECK_NOTIFY).handler(this.storeCheckNotifyHandler).end();
//
// // 自定义菜单事件
// newRouter.rule().async(false).msgType(EVENT).event(EventType.CLICK).handler(this.menuHandler).end();
//
// // 点击菜单连接事件
// newRouter.rule().async(false).msgType(EVENT).event(EventType.VIEW).handler(this.nullHandler).end();
//
// // 关注事件
// newRouter.rule().async(false).msgType(EVENT).event(SUBSCRIBE).handler(this.subscribeHandler).end();
//
// // 取消关注事件
// newRouter.rule().async(false).msgType(EVENT).event(UNSUBSCRIBE).handler(this.unsubscribeHandler).end();
//
// // 上报地理位置事件
// newRouter.rule().async(false).msgType(EVENT).event(EventType.LOCATION).handler(this.locationHandler).end();
//
// // 接收地理位置消息
// newRouter.rule().async(false).msgType(XmlMsgType.LOCATION).handler(this.locationHandler).end();
//
// // 扫码事件
// newRouter.rule().async(false).msgType(EVENT).event(EventType.SCAN).handler(this.scanHandler).end();
//
// // 默认
// newRouter.rule().async(false).handler(this.msgHandler).end();
//
// return newRouter;
// }
}
package com.sien.common.config;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import io.geekidea.springbootplus.config.properties.WxPayProperties;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Binary Wang
*/
@Configuration
@ConditionalOnClass(WxPayService.class)
@EnableConfigurationProperties(WxPayProperties.class)
@AllArgsConstructor
public class WxPayConfiguration {
private WxPayProperties properties;
@Bean
@ConditionalOnMissingBean
public WxPayService wxService() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId()));
payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));
payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey()));
payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId()));
payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId()));
payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath()));
// 可以指定是否使用沙箱环境
payConfig.setUseSandboxEnv(false);
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
}
package com.sien.common.controller;
import com.sien.common.param.app.AppSmsRegisterParam;
import com.sien.common.param.app.AppUserInfoParam;
import com.sien.common.param.app.AppUserPhoneUpdateParam;
import com.sien.common.service.AppUserApiService;
import com.sien.common.service.AppUserService;
import com.sien.common.vo.AppUserQueryVo;
import com.sien.common.vo.app.LoginAppUserTokenVo;
import com.sien.common.vo.app.MyInfoVo;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.controller.BaseController;
import io.geekidea.springbootplus.framework.log.annotation.Module;
import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* APP用户 控制器
*
* @author wei
* @since 2020-09-23
*/
@Slf4j
@RestController
@Module("api-app")
@Api(value = "用户API", tags = {"APP用户相关"})
@RequestMapping("/user")
public class AppUserController extends BaseController {
@Autowired
private AppUserService appUserService;
@Autowired
private AppUserApiService appUserApiService;
/**
* 获取自己的信息详情
*/
@GetMapping("/myInfo/")
@OperationLog(name = "APP用户详情", type = OperationLogType.INFO)
@ApiOperation(value = "获取APP用户详情", response = AppUserQueryVo.class)
public ApiResult<MyInfoVo> getAppUser() throws Exception {
MyInfoVo appUserQueryVo = appUserService.getMyInfo();
return ApiResult.ok(appUserQueryVo);
}
@PostMapping("/registerOrLogin")
@Validated
@ApiOperation(value = "手机号注册+登陆", notes = "app用户注册+登陆", response = LoginAppUserTokenVo.class)
public ApiResult<LoginAppUserTokenVo> registerOrLogin(@Validated @RequestBody AppSmsRegisterParam appSmsRegisterParam, @RequestHeader(required = false) String language) throws Exception {
return appUserApiService.register(appSmsRegisterParam, language);
}
/**
* 补充或修改APP用户信息
*/
@PostMapping("/updateAppUserInfo")
@OperationLog(name = "补充或修改APP用户信息", type = OperationLogType.ADD)
@ApiOperation(value = "补充或修改APP用户信息", notes = "不需要修改的字段传入null或直接不传入该字段,如果传入空双引号的话会将数据置为空 ", response = ApiResult.class)
public ApiResult<Boolean> updateAppUserInfo(@RequestBody AppUserInfoParam appUserInfoParam) throws Exception {
boolean flag = appUserApiService.updateAppUser(appUserInfoParam);
return ApiResult.result(flag);
}
/**
* 修改手机号
*/
@PostMapping("/updatePhone")
@OperationLog(name = "修改手机号", type = OperationLogType.ADD)
@ApiOperation(value = "修改手机号", response = ApiResult.class)
public ApiResult<Boolean> updatePhone(@RequestBody AppUserPhoneUpdateParam userPhoneUpdateParam) throws Exception {
return appUserApiService.updatePhone(userPhoneUpdateParam.getPhoneArea(), userPhoneUpdateParam.getPhone(), userPhoneUpdateParam.getCode(), userPhoneUpdateParam.getCodeNew());
}
// @GetMapping("/userInfoList")
// @ApiOperation(value = "批量获取用户信息")
// public ApiResult<List<AppUser>> getUserInfoList(@RequestParam("uids") Set<Long> uids) {
// return appUserApiService.getAppUserList(uids);
// }
/**
* APP用户分页列表
*//*
@PostMapping("/getPageList")
@OperationLog(name = "APP用户分页列表", type = OperationLogType.PAGE)
@ApiOperation(value = "APP用户分页列表", response = AppUserQueryVo.class)
@RequiresRoles("sys:admin")
public ApiResult<Paging<AppUserQueryVo>> getAppUserPageList(@Validated @RequestBody AppUserPageParam appUserPageParam) throws Exception {
Paging<AppUserQueryVo> paging = appUserService.getAppUserPageList(appUserPageParam);
return ApiResult.ok(paging);
}*/
// @GetMapping("/userInfo")
// @ApiOperation(value = "根据token获取userInfo", notes = "客服模块调用")
// public AppUser getUserInfo() {
// return appUserApiService.getUserInfo(Long.valueOf(JwtUtil.getUsername(JwtTokenUtil.getToken())));
// }
// /**
// * 添加或修改推送token
// */
// @PostMapping("/addDeviceToken")
// @OperationLog(name = "添加或修改推送token", type = OperationLogType.ADD)
// @ApiOperation(value = "添加或修改推送token", notes = "添加和修改都调用此接口", response = ApiResult.class)
// public ApiResult<Boolean> addDeviceToken(@RequestBody DeviceTokenParam deviceTokenParam) throws Exception {
// boolean flag = appUserService.updateDeviceToken(deviceTokenParam, deviceTokenParam.getDeviceType());
// return ApiResult.result(flag);
// }
/* *//**
* 添加APP用户
*//*
@PostMapping("/add")
@OperationLog(name = "添加APP用户", type = OperationLogType.ADD)
@ApiOperation(value = "添加APP用户", response = ApiResult.class)
@RequiresRoles("sys:admin")
public ApiResult<Boolean> addAppUser(@Validated(Add.class) @RequestBody AppUser appUser) throws Exception {
boolean flag = appUserService.saveAppUser(appUser);
return ApiResult.result(flag);
}
*//**
* 修改APP用户
*//*
@PostMapping("/update")
@OperationLog(name = "修改APP用户", type = OperationLogType.UPDATE)
@ApiOperation(value = "修改APP用户", response = ApiResult.class)
@RequiresRoles("sys:admin")
public ApiResult<Boolean> updateAppUser(@Validated(Update.class) @RequestBody AppUser appUser) throws Exception {
boolean flag = appUserService.updateAppUser(appUser);
return ApiResult.result(flag);
}
*//**
* 删除APP用户
*//*
@PostMapping("/delete/{id}")
@OperationLog(name = "删除APP用户", type = OperationLogType.DELETE)
@ApiOperation(value = "删除APP用户", response = ApiResult.class)
@RequiresRoles("sys:admin")
public ApiResult<Boolean> deleteAppUser(@PathVariable("id") Long id) throws Exception {
boolean flag = appUserService.deleteAppUser(id);
return ApiResult.result(flag);
}
*/
/**
* 获取APP用户详情
*/
/*
@GetMapping("/info/{id}")
@OperationLog(name = "APP用户详情", type = OperationLogType.INFO)
@ApiOperation(value = "APP用户详情", response = AppUserQueryVo.class)
@RequiresRoles("sys:admin")
public ApiResult<AppUserQueryVo> getAppUser(@PathVariable("id") Long id) throws Exception {
AppUserQueryVo appUserQueryVo = appUserService.getAppUserById(id);
return ApiResult.ok(appUserQueryVo);
}
*/
}
package com.sien.common.controller;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.sien.common.param.DonationRecordPageParam;
import com.sien.common.param.app.DonationRecordAdd;
import com.sien.common.service.DonationRecordService;
import com.sien.common.vo.DonationRecordQueryVo;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.controller.BaseController;
import io.geekidea.springbootplus.framework.core.pagination.Paging;
import io.geekidea.springbootplus.framework.core.validator.groups.Add;
import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 捐款记录 控制器
*
* @author hewei
* @since 2021-02-25
*/
@Slf4j
@RestController
@RequestMapping("/donationRecord")
@Api(value = "捐款记录API", tags = {"捐款记录"})
public class DonationRecordController extends BaseController {
@Autowired
private DonationRecordService donationRecordService;
/**
* 添加捐款记录
*/
@PostMapping("/add")
@OperationLog(name = "我要捐款", type = OperationLogType.ADD)
@ApiOperation(value = "我要捐款")
public ApiResult<WxPayMpOrderResult> addDonationRecord(@Validated(Add.class) @RequestBody DonationRecordAdd donationRecordAdd) throws Exception {
return donationRecordService.add(donationRecordAdd);
}
/**
* 我的捐款记录分页列表
*/
// @PostMapping("/getMyPageList")
// @OperationLog(name = "我的捐款记录分页列表", type = OperationLogType.PAGE)
// @ApiOperation(value = "我的捐款记录分页列表")
// public ApiResult<List<DonationRecord>> getDonationRecordPageList() throws Exception {
// return donationRecordService.getDonationRecordAllList();
// }
/**
* 我的捐款记录分页列表
*/
@PostMapping("/getMyPageList")
@OperationLog(name = "我的捐款记录分页列表", type = OperationLogType.PAGE)
@ApiOperation(value = "我的捐款记录分页列表")
public ApiResult<Paging<DonationRecordQueryVo>> getDonationRecordPageList(@Validated @RequestBody DonationRecordPageParam donationRecordPageParam) throws Exception {
Paging<DonationRecordQueryVo> paging = donationRecordService.getDonationRecordPageList(donationRecordPageParam);
return ApiResult.ok(paging);
}
// /**
// * 获取捐款记录详情
// */
// @GetMapping("/info/{id}")
// @OperationLog(name = "捐款记录详情", type = OperationLogType.INFO)
// @ApiOperation(value = "捐款记录详情")
// public ApiResult<DonationRecordQueryVo> getDonationRecord(@PathVariable("id") Long id)throws Exception{
// DonationRecordQueryVo donationRecordQueryVo = donationRecordService.getDonationRecordById(id);
// return ApiResult.ok(donationRecordQueryVo);
// }
// /**
// * 修改捐款记录
// */
// @PostMapping("/update")
// @OperationLog(name = "修改捐款记录", type = OperationLogType.UPDATE)
// @ApiOperation(value = "修改捐款记录")
// public ApiResult<Boolean> updateDonationRecord(@Validated(Update.class) @RequestBody DonationRecord donationRecord)throws Exception{
// boolean flag= donationRecordService.updateDonationRecord(donationRecord);
// return ApiResult.result(flag);
// }
//
// /**
// * 删除捐款记录
// */
// @PostMapping("/delete/{id}")
// @OperationLog(name = "删除捐款记录", type = OperationLogType.DELETE)
// @ApiOperation(value = "删除捐款记录")
// public ApiResult<Boolean> deleteDonationRecord(@PathVariable("id") Long id)throws Exception{
// boolean flag= donationRecordService.deleteDonationRecord(id);
// return ApiResult.result(flag);
// }
}
package com.sien.common.controller;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.sien.common.param.VipOpenParam;
import com.sien.common.service.VipRecordService;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.controller.BaseController;
import io.geekidea.springbootplus.framework.core.validator.groups.Add;
import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Vip开通记录 控制器
*
* @author hewei
* @since 2021-02-25
*/
@Slf4j
@RestController
@RequestMapping("/vipRecord")
@Api(value = "Vip开通API", tags = {"Vip开通"})
public class VipRecordController extends BaseController {
@Autowired
private VipRecordService vipRecordService;
/**
* Vip开通
*/
@PostMapping("/open")
@OperationLog(name = "开通Vip", type = OperationLogType.ADD)
@ApiOperation(value = "开通Vip")
public ApiResult<WxPayMpOrderResult> openVip(@Validated(Add.class) @RequestBody VipOpenParam vipOpenParam) throws Exception {
return vipRecordService.openVip(vipOpenParam);
}
// /**
// * 添加Vip开通记录
// */
// @PostMapping("/add")
// @OperationLog(name = "添加Vip开通记录", type = OperationLogType.ADD)
// @ApiOperation(value = "添加Vip开通记录")
// public ApiResult<Boolean> addVipRecord(@Validated(Add.class) @RequestBody VipRecord vipRecord)throws Exception{
// boolean flag= vipRecordService.saveVipRecord(vipRecord);
// return ApiResult.result(flag);
// }
//
// /**
// * 修改Vip开通记录
// */
// @PostMapping("/update")
// @OperationLog(name = "修改Vip开通记录", type = OperationLogType.UPDATE)
// @ApiOperation(value = "修改Vip开通记录")
// public ApiResult<Boolean> updateVipRecord(@Validated(Update.class) @RequestBody VipRecord vipRecord)throws Exception{
// boolean flag= vipRecordService.updateVipRecord(vipRecord);
// return ApiResult.result(flag);
// }
//
// /**
// * 删除Vip开通记录
// */
// @PostMapping("/delete/{id}")
// @OperationLog(name = "删除Vip开通记录", type = OperationLogType.DELETE)
// @ApiOperation(value = "删除Vip开通记录")
// public ApiResult<Boolean> deleteVipRecord(@PathVariable("id") Long id)throws Exception{
// boolean flag= vipRecordService.deleteVipRecord(id);
// return ApiResult.result(flag);
// }
//
// /**
// * 获取Vip开通记录详情
// */
// @GetMapping("/info/{id}")
// @OperationLog(name = "Vip开通记录详情", type = OperationLogType.INFO)
// @ApiOperation(value = "Vip开通记录详情")
// public ApiResult<VipRecordQueryVo> getVipRecord(@PathVariable("id") Long id)throws Exception{
// VipRecordQueryVo vipRecordQueryVo = vipRecordService.getVipRecordById(id);
// return ApiResult.ok(vipRecordQueryVo);
// }
//
// /**
// * Vip开通记录分页列表
// */
// @PostMapping("/getPageList")
// @OperationLog(name = "Vip开通记录分页列表", type = OperationLogType.PAGE)
// @ApiOperation(value = "Vip开通记录分页列表")
// public ApiResult<Paging<VipRecordQueryVo>>getVipRecordPageList(@Validated @RequestBody VipRecordPageParam vipRecordPageParam)throws Exception{
// Paging<VipRecordQueryVo> paging = vipRecordService.getVipRecordPageList(vipRecordPageParam);
// return ApiResult.ok(paging);
// }
}
package com.sien.common.controller;
import com.sien.common.service.AppUserService;
import com.sien.common.vo.app.LoginAppUserTokenVo;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.controller.BaseController;
import io.geekidea.springbootplus.framework.log.annotation.Module;
import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* APP用户 控制器
*
* @author wei
* @since 2020-09-23
*/
@Slf4j
@RestController
@Module("api-app")
@Api(value = "微信登陆相关", tags = {"微信登陆相关"})
@RequestMapping("/wechatUser")
public class WechatUserController extends BaseController {
@Autowired
private AppUserService appUserService;
/**
* 根据微信重定向code查询用户是否绑定
*/
@GetMapping("/check/")
@OperationLog(name = "根据微信重定向code查询用户是否绑定", type = OperationLogType.INFO)
@ApiOperation(value = "根据微信重定向code查询用户是否绑定", notes = "用户每次从微信公众号菜单页跳转,都必须调用此接口,来判断用户是否已经绑定用户," +
" 如果已经绑定就直接返回绑定用户的token,如没有绑定则需要用户进行注册,", response = LoginAppUserTokenVo.class)
public ApiResult<LoginAppUserTokenVo> checkAppUser(@RequestParam("code") String code) throws Exception {
return appUserService.checkWechatUserBinding(code);
}
}
package com.sien.common.factory;
import io.geekidea.springbootplus.config.properties.WxMpProperties;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.stream.Collectors;
public class GetWx {
private static WxMpProperties properties;
public static WxMpService getWxMpService() {
// 代码里 getConfigs()处报错的同学,请注意仔细阅读项目说明,你的IDE需要引入lombok插件!!!!
final List<WxMpProperties.MpConfig> configs = properties.getConfigs();
if (configs == null) {
throw new RuntimeException("添加下相关配置,注意别配错了!");
}
WxMpService service = new WxMpServiceImpl();
service.setMultiConfigStorages(configs
.stream().map(a -> {
WxMpDefaultConfigImpl configStorage;
//
configStorage = new WxMpDefaultConfigImpl();
configStorage.setAppId(a.getAppId());
configStorage.setSecret(a.getSecret());
configStorage.setToken(a.getToken());
configStorage.setAesKey(a.getAesKey());
return configStorage;
}).collect(Collectors.toMap(WxMpDefaultConfigImpl::getAppId, a -> a, (o, n) -> o)));
return service;
}
@Autowired
public void setProperties(WxMpProperties properties) {
this.properties = properties;
}
}
package com.sien.common.service;
import com.sien.common.entity.AppUser;
import com.sien.common.param.AppUserPageParam;
import com.sien.common.param.app.AppSmsRegisterParam;
import com.sien.common.param.app.DeviceTokenParam;
import com.sien.common.vo.AppUserQueryVo;
import com.sien.common.vo.app.LoginAppUserTokenVo;
import com.sien.common.vo.app.MyInfoVo;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.service.BaseService;
import io.geekidea.springbootplus.framework.core.pagination.Paging;
import me.chanjar.weixin.common.error.WxErrorException;
;
/**
* APP用户 服务类
*
* @author wei
* @since 2020-09-23
*/
public interface AppUserService extends BaseService<AppUser> {
/**
* 根据手机号判断用户是否存在
* @param phoneArea
* @param phone
* @return
*/
boolean hasUserByPhoneNumer(String phoneArea,String phone);
// /**
// * 注册
// *
// * @param appSmsRegisterParam
// * @param language
// * @return
// */
// ApiResult<LoginAppUserTokenVo> register(AppSmsRegisterParam appSmsRegisterParam, String language);
/**
* 登陆
*
* @param loginParam
* @param language
* @return
*/
ApiResult<LoginAppUserTokenVo> login(AppSmsRegisterParam loginParam, String language,Boolean hasRegister);
/**
* 保存
*
* @param appUser
* @return
* @throws Exception
*/
boolean saveAppUser(AppUser appUser) throws Exception;
/**
* 修改
*
* @param appUser
* @return
* @throws Exception
*/
boolean updateAppUser(AppUser appUser) throws Exception;
/**
* 添加或修改推送token
*
* @param deviceTokenParam
* @param deviceType
* @return
* @throws Exception
*/
boolean updateDeviceToken(DeviceTokenParam deviceTokenParam, int deviceType) throws Exception;
/**
* 删除
*
* @param id
* @return
* @throws Exception
*/
boolean deleteAppUser(Long id) throws Exception;
/**
* 根据ID获取查询对象
*
* @param id
* @return
* @throws Exception
*/
AppUserQueryVo getAppUserById(Long id) throws Exception;
/**
* 获取自己的信息详情
*/
MyInfoVo getMyInfo() throws Exception;
/**
* 查询用户是否绑定微信
*/
ApiResult<LoginAppUserTokenVo> checkWechatUserBinding(String code) throws WxErrorException;
/**
* 获取分页对象
*
* @param appUserPageParam
* @return
* @throws Exception
*/
Paging<AppUserQueryVo> getAppUserPageList(AppUserPageParam appUserPageParam) throws Exception;
}
package com.sien.common.service;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.sien.common.entity.DonationRecord;
import com.sien.common.param.DonationRecordPageParam;
import com.sien.common.param.app.DonationRecordAdd;
import com.sien.common.vo.DonationRankAndTotal;
import com.sien.common.vo.DonationRecordQueryVo;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.service.BaseService;
import io.geekidea.springbootplus.framework.core.pagination.Paging;
import java.util.List;
/**
* 捐款记录 服务类
*
* @author hewei
* @since 2021-02-25
*/
public interface DonationRecordService extends BaseService<DonationRecord> {
/**
* 查询捐款排名和总捐款
*
* @param userId
* @return
*/
DonationRankAndTotal getDonationRankAndTotal(Long userId);
/**
* 保存
*
* @param donationRecord
* @return
* @throws Exception
*/
boolean saveDonationRecord(DonationRecord donationRecord) throws Exception;
/**
* 回调
*/
String donationOrderNotifyResult(String xmlData);
/**
* 我要捐款
*
* @param donationRecordAdd
* @return
* @throws Exception
*/
ApiResult<WxPayMpOrderResult> add(DonationRecordAdd donationRecordAdd) throws Exception;
/**
* 修改
*
* @param donationRecord
* @return
* @throws Exception
*/
boolean updateDonationRecord(DonationRecord donationRecord) throws Exception;
/**
* 删除
*
* @param id
* @return
* @throws Exception
*/
boolean deleteDonationRecord(Long id) throws Exception;
/**
* 根据ID获取查询对象
*
* @param id
* @return
* @throws Exception
*/
DonationRecordQueryVo getDonationRecordById(Long id) throws Exception;
/**
* 获取分页对象
*
* @param donationRecordPageParam
* @return
* @throws Exception
*/
Paging<DonationRecordQueryVo> getDonationRecordPageList(DonationRecordPageParam donationRecordPageParam) throws Exception;
ApiResult<List<DonationRecord>> getDonationRecordAllList() throws Exception;
}
package com.sien.common.service;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.sien.common.entity.VipRecord;
import com.sien.common.param.VipOpenParam;
import com.sien.common.param.VipRecordPageParam;
import com.sien.common.vo.VipRecordQueryVo;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.service.BaseService;
import io.geekidea.springbootplus.framework.core.pagination.Paging;
/**
* Vip开通记录 服务类
*
* @author hewei
* @since 2021-02-25
*/
public interface VipRecordService extends BaseService<VipRecord> {
/**
* 支付回调
* @param xmlData
* @return
*/
String vipOrderNotifyResult(String xmlData);
/**
* 保存
*
* @param vipRecord
* @return
* @throws Exception
*/
boolean saveVipRecord(VipRecord vipRecord)throws Exception;
/**
* 开通vip
*
* @param vipOpenParam
* @return
* @throws Exception
*/
ApiResult<WxPayMpOrderResult> openVip(VipOpenParam vipOpenParam) throws Exception;
/**
* 修改
*
* @param vipRecord
* @return
* @throws Exception
*/
boolean updateVipRecord(VipRecord vipRecord)throws Exception;
/**
* 删除
*
* @param id
* @return
* @throws Exception
*/
boolean deleteVipRecord(Long id)throws Exception;
/**
* 根据ID获取查询对象
*
* @param id
* @return
* @throws Exception
*/
VipRecordQueryVo getVipRecordById(Long id)throws Exception;
/**
* 获取分页对象
*
* @param vipRecordPageParam
* @return
* @throws Exception
*/
Paging<VipRecordQueryVo> getVipRecordPageList(VipRecordPageParam vipRecordPageParam) throws Exception;
}
package com.sien.common.service.impl;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sien.common.entity.AppUser;
import com.sien.common.service.AppSmsService;
import com.sien.common.service.AppUserService;
import com.sien.common.sms.SendSms;
import com.sien.common.vo.AppUserQueryVo;
import com.sien.common.vo.SmsCode;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.time.Duration;
@Service
@Slf4j
public class AppSmsServiceImpl implements AppSmsService {
@Autowired
private AppUserService appUserService;
/**
* 获取当前环境
*/
@Value("${spring.profiles.active}")
private String profiles;
@Autowired
private RedisTemplate redisTemplate;
/**
* 测试环境
*/
private static final String DEV_PROFILE = "dev";
/**
* 测试环境默认短信验证码
*/
private static final String DEFAULT_DEV_SMS_CODE = "666666";
/**
* 注册 短信验证码redis的key值
*/
private static final String SMS_REGIEST = "sms:app:register:%s_%s";
/**
* 修改 短信验证码redis的key值
*/
private static final String SMS_UPDATE = "sms:app:update:%s_%s";
/**
* 修改 短信验证码redis的key值,新手机
*/
private static final String SMS_UPDATE_NEW = "sms:app:updateNew:%s_%s";
/**
* 短信验证码redis的key值
*/
private static final String SMS_LOGIN = "sms:app:login:%s_%s";
@Override
public void deleteRegisterCode(String area, String number) {
redisTemplate.delete(String.format(SMS_REGIEST, area, number));
}
@Override
public void deleteUpdatePhoneCode(String area, String number) {
redisTemplate.delete(String.format(SMS_UPDATE, area, number));
}
@Override
public void deleteUpdatePhoneCodeNew(String area, String number) {
redisTemplate.delete(String.format(SMS_UPDATE_NEW, area, number));
}
@Override
public ApiResult<Object> registerOrLoginCode(String area, String number) {
return getSmsCodeApiResult(String.format(SMS_REGIEST, area, number), area, number);
}
@Override
public ApiResult<Object> updatePhoneCodeSendToOld() throws Exception {
JwtToken jwtToken = (JwtToken) SecurityUtils.getSubject().getPrincipal();
AppUserQueryVo appUserById = appUserService.getAppUserById(jwtToken.getUserId());
// 向旧手机发送验证码
String key = String.format(SMS_UPDATE, appUserById.getPhoneArea(), appUserById.getPhone());
ApiResult<Object> smsCodeApiResult = getSmsCodeApiResult(key, appUserById.getPhoneArea(), appUserById.getPhone());
return smsCodeApiResult;
}
@Override
public ApiResult<Object> updatePhoneCodeSendToNew(String phoneArea, String phone) {
// 判断手机号是否已经注册
AppUser one = appUserService.getOne(new QueryWrapper<AppUser>().lambda()
.eq(AppUser::getPhoneArea, phoneArea)
.eq(AppUser::getPhone, phone));
if (one != null) {
return ApiResult.fail(ApiCode.UPDATA_PHONE_USE, null);
}
// 向新手机发送验证码
String key = String.format(SMS_UPDATE_NEW, phoneArea, phone);
ApiResult<Object> smsCodeApiResult = getSmsCodeApiResult(key, phoneArea, phone);
return smsCodeApiResult;
}
@Override
public ApiResult<Boolean> checkPhoneCodeOld(String code) throws Exception {
JwtToken jwtToken = (JwtToken) SecurityUtils.getSubject().getPrincipal();
AppUserQueryVo appUserById = appUserService.getAppUserById(jwtToken.getUserId());
boolean equalsRegisterCode = this.equalsUpdatePhoneCodeOld(appUserById.getPhoneArea(), appUserById.getPhone(), code);
if (!equalsRegisterCode) {
return ApiResult.fail(ApiCode.SMS_CODE_ERROR);
}
return ApiResult.ok();
}
/**
* 获取短信验证码
*
* @param key
* @param area
* @param number
* @return
*/
private ApiResult<Object> getSmsCodeApiResult(String key, String area, String number) {
// 生成验证码code
String randomCode = getRandomCode();
// 过期时间(秒)
long expire = 120L;
Duration expireDuration = Duration.ofSeconds(expire);
// 存至redis
redisTemplate.opsForValue().set(key, randomCode, expireDuration);
SmsCode smsCode = new SmsCode();
smsCode.setSmsCode(randomCode);
log.info(area + "," + number + ":" + randomCode);
// 调用短信平台发送短信代码
try {
SendSms.send("+" + area + number, randomCode);
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage());
return ApiResult.fail(ApiCode.FAIL, null);
}
return ApiResult.ok(null);
}
@Override
public ApiResult LoginType(String area, String number) {
return getSmsCodeApiResult(String.format(SMS_LOGIN, area, number), area, number);
}
@Override
public boolean equalsRegisterCode(String area, String number, String code) {
return equalsSms(SMS_REGIEST, area, number, code);
}
@Override
public boolean equalsUpdatePhoneCodeOld(String area, String number, String code) {
return equalsSms(SMS_UPDATE, area, number, code);
}
@Override
public boolean equalsUpdatePhoneCodeNew(String area, String number, String code) {
return equalsSms(SMS_UPDATE_NEW, area, number, code);
}
private boolean equalsSms(String type, String area, String number, String code) {
String formatKey = String.format(type, area, number);
Object key = redisTemplate.opsForValue().get(formatKey);
if (key == null) {
return false;
}
return String.valueOf(key).equals(code);
}
@Override
public boolean equalsLoginCode(String area, String number, String code) {
return equalsSms(SMS_LOGIN, area, number, code);
}
/**
* 生成验证码code
*
* @return
*/
String getRandomCode() {
// 如果为测试环境则生成默认
if (profiles.equals(DEV_PROFILE) || profiles.equals("test")) {
return DEFAULT_DEV_SMS_CODE;
} else {
return RandomUtil.randomNumbers(6);
}
}
}
package com.sien.common.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sien.common.entity.AppUser;
import com.sien.common.param.app.AppSmsRegisterParam;
import com.sien.common.param.app.AppUserInfoParam;
import com.sien.common.service.AppSmsService;
import com.sien.common.service.AppUserApiService;
import com.sien.common.service.AppUserService;
import com.sien.common.vo.AppUserQueryVo;
import com.sien.common.vo.app.LoginAppUserTokenVo;
import io.geekidea.springbootplus.config.properties.WxMpProperties;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Service
public class AppUserApiServiceImpl implements AppUserApiService {
@Autowired
private AppSmsService appSmsService;
@Autowired
private AppUserService appUserService;
// @Autowired
// private WxMpService wxMpService;
@Autowired
private WxMpProperties properties;
private WxMpService getWxMpService() {
// 代码里 getConfigs()处报错的同学,请注意仔细阅读项目说明,你的IDE需要引入lombok插件!!!!
final List<WxMpProperties.MpConfig> configs = this.properties.getConfigs();
if (configs == null) {
throw new RuntimeException("添加下相关配置,注意别配错了!");
}
WxMpService service = new WxMpServiceImpl();
service.setMultiConfigStorages(configs
.stream().map(a -> {
WxMpDefaultConfigImpl configStorage;
//
configStorage = new WxMpDefaultConfigImpl();
configStorage.setAppId(a.getAppId());
configStorage.setSecret(a.getSecret());
configStorage.setToken(a.getToken());
configStorage.setAesKey(a.getAesKey());
return configStorage;
}).collect(Collectors.toMap(WxMpDefaultConfigImpl::getAppId, a -> a, (o, n) -> o)));
return service;
}
@Override
public ApiResult<Boolean> updatePhone(String phoneArea, String phone, String code, String codeNew) throws Exception {
JwtToken jwtToken = (JwtToken) SecurityUtils.getSubject().getPrincipal();
AppUserQueryVo appUserById = appUserService.getAppUserById(jwtToken.getUserId());
// 校验旧手机验证码
boolean equalsRegisterCode = appSmsService.equalsUpdatePhoneCodeOld(appUserById.getPhoneArea(), appUserById.getPhone(), code);
if (!equalsRegisterCode) {
return ApiResult.fail(ApiCode.SMS_CODE_ERROR);
}
// 校验新手机验证码
boolean equalsRegisterCodeNew = appSmsService.equalsUpdatePhoneCodeNew(phoneArea, phone, codeNew);
if (!equalsRegisterCodeNew) {
return ApiResult.fail(ApiCode.SMS_CODE_ERROR_NEW);
}
// 删除已使用的旧手机验证码
appSmsService.deleteUpdatePhoneCode(appUserById.getPhoneArea(), appUserById.getPhone());
// 删除已使用的新手机验证码
appSmsService.deleteUpdatePhoneCodeNew(phoneArea, phone);
// 判断手机号是否已经注册
AppUser one = appUserService.getOne(new QueryWrapper<AppUser>().lambda()
.eq(AppUser::getPhoneArea, phoneArea)
.eq(AppUser::getPhone, phone));
if (one != null) {
return ApiResult.fail(ApiCode.BUSINESS_EXCEPTION);
}
AppUser appUser = new AppUser();
appUser.setPhoneArea(phoneArea);
appUser.setPhone(phone);
appUser.setId(jwtToken.getUserId());
boolean b = appUserService.updateById(appUser);
if (b) {
return ApiResult.ok();
}
return ApiResult.fail();
}
@Override
public AppUser getUserInfo(Long userId) {
return appUserService.getOne(new QueryWrapper<AppUser>().lambda().eq(AppUser::getId, userId));
}
@Override
public ApiResult<List<AppUser>> getAppUserList(Set<Long> uids) {
return ApiResult.ok(appUserService.list(new QueryWrapper<AppUser>().lambda().in(AppUser::getId, uids)));
}
@Override
public ApiResult<LoginAppUserTokenVo> register(AppSmsRegisterParam loginParam, String language) throws Exception {
// 校验验证码
boolean equalsRegisterCode = appSmsService.equalsRegisterCode(loginParam.getPhoneArea(), loginParam.getPhone(), loginParam.getSmsCode());
if (!equalsRegisterCode) {
return ApiResult.fail(ApiCode.SMS_CODE_REGIST_ERROR, new LoginAppUserTokenVo());
}
// 删除已使用的验证码
appSmsService.deleteRegisterCode(loginParam.getPhoneArea(), loginParam.getPhone());
// 判断是否已经注册
if (appUserService.hasUserByPhoneNumer(loginParam.getPhoneArea(), loginParam.getPhone())) {
// 如果已经注册直接走登陆的代码
return appUserService.login(loginParam, language, true);
}
// 没注册则保存到数据库
AppUser appUserByOpenId = appUserService.getOne(new QueryWrapper<AppUser>().lambda().eq(AppUser::getWechatOpenId, loginParam.getOpenId()));
appUserByOpenId.setPhoneArea(loginParam.getPhoneArea());
appUserByOpenId.setPhone(loginParam.getPhone());
appUserByOpenId.setUsername(loginParam.getUserName());
boolean isDbOk = appUserService.updateById(appUserByOpenId);
if (!isDbOk) {
return ApiResult.fail(ApiCode.SPRING_BOOT_PLUS_EXCEPTION, new LoginAppUserTokenVo());
}
// 走登陆的代码
return appUserService.login(loginParam, language, false);
}
// @Override
// public ApiResult<LoginAppUserTokenVo> login(AppSmsRegisterParam loginParam, String language) throws Exception {
// return null;
// }
@Override
public boolean updateAppUser(AppUserInfoParam appUserInfoParam) throws Exception {
AppUser appUser = new AppUser();
BeanUtils.copyProperties(appUserInfoParam, appUser);
JwtToken jwtToken = (JwtToken) SecurityUtils.getSubject().getPrincipal();
appUser.setId(jwtToken.getUserId());
return appUserService.updateAppUser(appUser);
}
}
package com.sien.common.service.impl;
import cn.hutool.captcha.generator.RandomGenerator;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.sien.common.entity.AppUser;
import com.sien.common.entity.VipRecord;
import com.sien.common.mapper.VipRecordMapper;
import com.sien.common.param.VipOpenParam;
import com.sien.common.param.VipRecordPageParam;
import com.sien.common.service.AppUserService;
import com.sien.common.service.VipPriceService;
import com.sien.common.service.VipRecordService;
import com.sien.common.vo.AppUserQueryVo;
import com.sien.common.vo.VipRecordQueryVo;
import com.sien.common.vo.app.VipPriceQueryVo;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
import io.geekidea.springbootplus.framework.core.pagination.PageInfo;
import io.geekidea.springbootplus.framework.core.pagination.Paging;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
/**
* Vip开通记录 服务实现类
*
* @author hewei
* @since 2021-02-25
*/
@Slf4j
@Service
public class VipRecordServiceImpl extends BaseServiceImpl<VipRecordMapper, VipRecord> implements VipRecordService {
private static final String BODY = "厦门市四恩慈善会开通会员";
private static final String NOTIFY_URL = "/api/pay/notify/vipOrder";
@Autowired
private VipRecordMapper vipRecordMapper;
@Autowired
private WxPayService wxService;
@Autowired
private AppUserService appUserService;
@Resource
HttpServletRequest httpServletRequest;
@Autowired
private VipPriceService vipPriceService;
@Value("${spring-boot-plus.domain}")
private String domain;
@Override
public String vipOrderNotifyResult(String xmlData) {
WxPayOrderNotifyResult notifyResult;
try {
notifyResult = this.wxService.parseOrderNotifyResult(xmlData);
} catch (WxPayException e) {
// 是否能解密成功
e.printStackTrace();
return WxPayNotifyResponse.fail("错误");
}
VipRecord vipRecord = super.getOne(new QueryWrapper<VipRecord>().lambda().eq(VipRecord::getWxMerPayId, notifyResult.getOutTradeNo()));
// 如果已经支付 直接响应成功
if (vipRecord.getPayStatus() == 1) {
return WxPayNotifyResponse.successResp("成功");
}
vipRecord.setPayStatus(1);
vipRecord.setPayTime(new Date());
super.updateById(vipRecord);
return WxPayNotifyResponse.successResp("成功");
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveVipRecord(VipRecord vipRecord) throws Exception {
return super.save(vipRecord);
}
private WxPayMpOrderResult getWxPayMpOrderResult(String outTradeNo, String openid, String spbillCreateIp, int totalFee) throws WxPayException {
WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = new WxPayUnifiedOrderRequest();
wxPayUnifiedOrderRequest.setBody(VipRecordServiceImpl.BODY);
// 商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
wxPayUnifiedOrderRequest.setOutTradeNo(outTradeNo);
//订单总金额,单位为分,详见支付金额
wxPayUnifiedOrderRequest.setTotalFee(totalFee);
// APP和网页支付提交用户端ip
wxPayUnifiedOrderRequest.setSpbillCreateIp(spbillCreateIp);
// 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
wxPayUnifiedOrderRequest.setNotifyUrl(domain + VipRecordServiceImpl.NOTIFY_URL);
wxPayUnifiedOrderRequest.setOpenid(openid);
String jsapi = "JSAPI";
wxPayUnifiedOrderRequest.setTradeType(jsapi);
WxPayMpOrderResult order = null;
// try {
order = this.wxService.createOrder(wxPayUnifiedOrderRequest);
// } catch (WxPayException e) {
// e.printStackTrace();
// }
return order;
}
@Override
public ApiResult<WxPayMpOrderResult> openVip(VipOpenParam vipOpenParam) throws Exception {
JwtToken jwtToken = (JwtToken) SecurityUtils.getSubject().getPrincipal();
AppUserQueryVo appUserById = appUserService.getAppUserById(jwtToken.getUserId());
// 获取vip价格表
VipPriceQueryVo vipPriceById = vipPriceService.getVipPriceById(vipOpenParam.getVipLevelId());
if (vipPriceById == null) {
return ApiResult.fail(ApiCode.FAIL, null);
}
// 应付金额
BigDecimal money = vipPriceById.getPrice().multiply(new BigDecimal(vipOpenParam.getYears()));
// 设置到期时间
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, vipOpenParam.getYears());
Date expiredAt = calendar.getTime();
// 商家订单号
String outTradeNo = new RandomGenerator(32).generate();
VipRecord vipRecord = new VipRecord();
vipRecord.setMoney(money);
vipRecord.setExpiredAt(expiredAt);
vipRecord.setYears(vipOpenParam.getYears());
vipRecord.setPayStatus(0);
vipRecord.setWxMerPayId(outTradeNo);
// // 判断是否为替他人
if (vipOpenParam.getIsReplace() == 1) {
// 判断接收人不能为自己
if (appUserById.getPhoneArea().equals(vipOpenParam.getPhoneArea()) &&
appUserById.getPhone().equals(vipOpenParam.getUserPhone())) {
return ApiResult.fail(ApiCode.PHONE_IS_ME, new WxPayMpOrderResult());
}
AppUser user = appUserService.getOne(new QueryWrapper<AppUser>().lambda()
.eq(AppUser::getPhone, vipOpenParam.getUserPhone())
.eq(AppUser::getPhoneArea, vipOpenParam.getPhoneArea()));
//判断接收方是否存在
if (user == null) {
return ApiResult.fail(ApiCode.USER_NOT_FOUND, new WxPayMpOrderResult());
}
vipRecord.setFkUserId(user.getId());
vipRecord.setFkRechargeUser(jwtToken.getUserId());
} else {
vipRecord.setFkUserId(jwtToken.getUserId());
vipRecord.setFkRechargeUser(-1L);
}
// 保存数据库
boolean save = super.save(vipRecord);
if (save) {
// 保存成功调用微信支付
String ip = httpServletRequest.getRemoteAddr();
BigDecimal num2 = new BigDecimal(100);
int totalFee = money.multiply(num2).intValue();
WxPayMpOrderResult wxPayMpOrderResult = getWxPayMpOrderResult(outTradeNo, appUserById.getWechatOpenId(), ip, totalFee);
return ApiResult.ok(wxPayMpOrderResult);
} else {
return ApiResult.fail(ApiCode.FAIL, null);
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean updateVipRecord(VipRecord vipRecord) throws Exception {
return super.updateById(vipRecord);
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean deleteVipRecord(Long id) throws Exception {
return super.removeById(id);
}
@Override
public VipRecordQueryVo getVipRecordById(Long id) throws Exception {
return vipRecordMapper.getVipRecordById(id);
}
@Override
public Paging<VipRecordQueryVo> getVipRecordPageList(VipRecordPageParam vipRecordPageParam) throws Exception {
Page<VipRecordQueryVo> page = new PageInfo<>(vipRecordPageParam, OrderItem.desc(getLambdaColumn(VipRecord::getCreateTime)));
IPage<VipRecordQueryVo> iPage = vipRecordMapper.getVipRecordPageList(page, vipRecordPageParam);
return new Paging<VipRecordQueryVo>(iPage);
}
}
package tillo.app_ws;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import tillo.app_ws.model.Constants;
import tillo.app_ws.receive.ReadWsData;
import tillo.app_ws.service.AppUserChannelsService;
import javax.annotation.Resource;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @Description app端 长连接事件处理
* @Author hewei hwei1233@163.com
* @Date 2019-07-26
*/
@Component
@ChannelHandler.Sharable
@Slf4j
public class AppImHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
@Resource
private ReadWsData readWsData;
@Resource
private AppUserChannelsService appUserChannelsService;
private final static ThreadFactory NAMED_THREAD_FACTORY = new ThreadFactoryBuilder()
.setNamePrefix("business-").build();
//.setPriority(6)
/**
* 核心业务处理线程池
* 属于io密集型业务
* io密集型任务配置尽可能多的线程数量
*/
// private final static ExecutorService THREAD_POOL_EXECUTOR =
// new ThreadPoolExecutor(Constants.CPU_PROCESSORS * 2, Constants.CPU_PROCESSORS * 2 + 2,
// 1L, TimeUnit.MILLISECONDS,
// new LinkedBlockingQueue<Runnable>(), NAMED_THREAD_FACTORY, new ThreadPoolExecutor.CallerRunsPolicy());
private final static ExecutorService THREAD_POOL_EXECUTOR =
// new ThreadPoolExecutor(6, 10,
// 0L, TimeUnit.MILLISECONDS,
// new LinkedBlockingQueue<Runnable>(2048), NAMED_THREAD_FACTORY, new ThreadPoolExecutor.AbortPolicy());
new ThreadPoolExecutor(Constants.CPU_PROCESSORS * 120, Constants.CPU_PROCESSORS * 130 + 2,
1L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(), NAMED_THREAD_FACTORY, new ThreadPoolExecutor.CallerRunsPolicy());
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
String data = msg.text();
try {
if (data.isEmpty()) {
return;
}
/*
* 在此进入耗时业务线程池, 将不再阻塞netty的I/O线程,提高网络吞吐
*/
THREAD_POOL_EXECUTOR.execute(() ->
execute(ctx, data)
);
} catch (Exception e) {
//返回错误
ctx.channel().writeAndFlush(new TextWebSocketFrame("error=" + e.toString() + ",data=" + data));
log.error(e.getMessage() + data, e);
}
}
private void execute(ChannelHandlerContext ctx, String data) {
Long userIdByChannel = appUserChannelsService.getUserIdByChannel(ctx);
log.debug("appWS收到" + userIdByChannel + ":" + data + ",channelId:" + ctx.channel().id().asLongText());
String language = ctx.channel().attr(AppUserChannelsService.LANGUAGE).get();
readWsData.convertModel(data, userIdByChannel, language);
}
/**
* 检测到异常
*
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
//排除当客户端意外关闭的情况,不是发送指定指令通知服务器退出,就会产生此错误。
if (ctx.channel().isActive()) {
Long userIdByChannel = appUserChannelsService.getUserIdByChannel(ctx);
log.error("uid:" + userIdByChannel + ",ws异常,channelId:" + ctx.channel().id().asLongText(), cause);
}
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) {
Long userIdByChannel = appUserChannelsService.getUserIdByChannel(ctx);
log.debug("uid:" + userIdByChannel + ",app端连接WS成功" + ",channelId:" + ctx.channel().id().asLongText());
}
/**
* 客户端不活跃
*
* @param ctx
* @throws Exception
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) {
Long userIdByChannel = appUserChannelsService.getUserIdByChannel(ctx);
log.debug("uid:" + userIdByChannel + "," + "不活跃" + ",channelId:" + ctx.channel().id().asLongText());
appUserChannelsService.remove(ctx);
}
/**
* 移除时触发, 不活跃的情况下会移除,会再次触发该事件
*/
@Override
public void handlerRemoved(ChannelHandlerContext ctx) {
Long userIdByChannel = appUserChannelsService.getUserIdByChannel(ctx);
log.debug("uid:" + userIdByChannel + "," + "handlerRemoved" + ",channelId:" + ctx.channel().id().asLongText());
}
}
package tillo.app_ws.annotation;
import tillo.app_ws.enums.WsRequestCmdEnum;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Description netty 接收类型注解,用于策略模式
* @Author hewei hwei1233@163.com
* @Date 2020-01-02
*/
@Retention(RetentionPolicy.RUNTIME)//RUNTIME运行时保留
@Target(ElementType.TYPE) //type描述类、接口
@Documented
public @interface ReceiveTypeAnnotation {
WsRequestCmdEnum type();
}
package tillo.app_ws.cache;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
/**
* @author hewei123@163.com
* @Description 用户与redis绑定
* @createTime 2020年04月14日 16:21:00
*/
@Service
@Slf4j
public class UserCache {
// @Autowired
// private RedisUtils redisUtils;
/**
* 在线状态
*/
public static final Integer ONLINE = 1;
/**
* 离线状态
*/
public static final Integer OFFLINE = 0;
/**
* key name
*/
private static final String KEY_BASE = "wsu:";
/**
* 用户在线状态fieldKey
*/
private static final String ONLINE_STATUS_KEY = "st";
/**
* 用户所连机器ip的fieldKey
*/
private static final String PRIVATE_IP_KEY = "lip";
// 用户语言暂时没用到
// private static final String LANGUAGE = "la";
// 用户公网ip,在公网部署集群需要用到
// private static final String PUBLIC_IP = "iip";
/**
* 排除无效的mac地址
*/
private final static byte[][] INVALID_MACS = {
{0x00, 0x05, 0x69}, // VMWare
{0x00, 0x1C, 0x14}, // VMWare
{0x00, 0x0C, 0x29}, // VMWare
{0x00, 0x50, 0x56}, // VMWare
{0x08, 0x00, 0x27}, // Virtualbox
{0x0A, 0x00, 0x27}, // Virtualbox
{0x00, 0x03, (byte) 0xFF}, // Virtual-PC
{0x00, 0x15, 0x5D} // Hyper-V
};
/**
* 内网ip
*/
private static String lAN_IP = "";
// 获取机器内网ip
static {
lAN_IP = getLocalIpAddress();
log.debug("lAN_IP:" + lAN_IP);
}
/**
* 判断是否为虚拟mac地址
*
* @param mac
* @return
*/
public static boolean isVmMac(byte[] mac) {
if (null == mac) {
return false;
}
for (byte[] invalid : INVALID_MACS) {
if (invalid[0] == mac[0] && invalid[1] == mac[1] && invalid[2] == mac[2]) {
return true;
}
}
return false;
}
/**
* 获取本机地址
*/
public static String getLocalIpAddress() {
try {
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
while (networkInterfaces.hasMoreElements()) {
NetworkInterface ni = networkInterfaces.nextElement();
/*
排除docker虚拟网卡
*/
String docker0 = "docker0";
if (ni.getName().equals(docker0)) {
continue;
}
if (!ni.isUp() || ni.isLoopback() || ni.isVirtual()) {
continue;
}
if (isVmMac(ni.getHardwareAddress())) {
continue;
}
Enumeration<InetAddress> inetAddresses = ni.getInetAddresses();
while (inetAddresses.hasMoreElements()) {
InetAddress inetAddress = inetAddresses.nextElement();
if (inetAddress.isLinkLocalAddress()) {
continue;
}
return inetAddress.getHostAddress();
}
}
} catch (SocketException e) {
log.debug("获取本机IP地址失败。" + e);
}
return StringUtils.EMPTY;
}
/**
* 用户上线绑定机器ip
*
* @param id
*/
public void online(String id) {
log.debug("ws用户上线保存redis连接ip:" + lAN_IP, ",uid:" );
// redisUtils.hset(KEY_BASE + id, PRIVATE_IP_KEY, lAN_IP);
// redisUtils.hset(KEY_BASE + id, ONLINE_STATUS_KEY, String.valueOf(ONLINE));
}
/**
* 用户下线删除绑定机器ip
*
* @param id
*/
public void offline(String id) {
log.debug("ws用户离线删除redis key,uid:" + id);
// redisUtils.kdel(KEY_BASE + id);
}
// /**
// * 根据用户id获取存在redis中的数据 例如绑定的服务器ip地址
// *
// * @param id
// * @return
// */
// public AppHashValueModel getById(String id) {
//
// Map<String, String> hgetll = redisUtils.hgetll(KEY_BASE + id);
// if (hgetll.isEmpty()) {
// return null;
// }
// AppHashValueModel appHashValueModel = new AppHashValueModel();
// appHashValueModel.setLanIp(hgetll.get(PRIVATE_IP_KEY));
// appHashValueModel.setOnlineStatus(Integer.parseInt(hgetll.get(ONLINE_STATUS_KEY)));
// return appHashValueModel;
//
// }
//
}
package tillo.app_ws.enums;
/**
* @Description ws请求类型
* @Author hewei hwei1233@163.com
* @Date 2019-12-05
*/
public enum WsRequestCmdEnum {
/**
* 数据
*/
DATA(1),
/**
* ping
*/
PING(2);
private final int cmdCode;
WsRequestCmdEnum(int uriCode) {
this.cmdCode = uriCode;
}
public int getCmdCode() {
return cmdCode;
}
/**
* 根据uriCode获取
*
* @param uriCode
* @return
*/
public static WsRequestCmdEnum getByCode(int uriCode) {
for (WsRequestCmdEnum wsRequestUriPathEnum : values()) {
if (wsRequestUriPathEnum.getCmdCode() == uriCode) {
return wsRequestUriPathEnum;
}
}
return null;
}
}
package tillo.app_ws.enums;
/**
* @Description ws响应类型
* @Author hewei hwei1233@163.com
* @Date 2019-12-05
*/
public enum WsResponseCmdEnum {
/**
* 离线消息下发完成指令
*/
OFFLINE_MSG_SUC(100),
/**
* 主动下发消息指令
*/
WRITE_MSG(101);
private final int cmdCode;
WsResponseCmdEnum(int uriCode) {
this.cmdCode = uriCode;
}
/**
* 根据uriCode获取
*
* @param uriCode
* @return
*/
public static WsResponseCmdEnum getByCode(int uriCode) {
for (WsResponseCmdEnum wsResponsePathEnum : values()) {
if (wsResponsePathEnum.getCmdCode() == uriCode) {
return wsResponsePathEnum;
}
}
return null;
}
public int getCmdCode() {
return cmdCode;
}
}
package tillo.app_ws.model;
import lombok.extern.slf4j.Slf4j;
/**
* @author wjm
* @Description 常量
* @date 2018/12/20
*/
@Slf4j
public class Constants {
/*
* 当前服务器cpu核心数量()
*/
public static final Integer CPU_PROCESSORS = Runtime.getRuntime().availableProcessors();
static {
log.info("CPU_PROCESSORS:" + CPU_PROCESSORS);
}
/**
* 加密消息
*/
public static final Integer IS_CRYPTO_MESSAGE = 1;
/**
* 加密消息类型
*/
public static final Integer CRYPTO_TYPE = 3;
/**
* 网页版长连接url
*/
public static final String WEB_WS_URL = "/webws";
/**
* WEBWS_V_2
*/
public static final String WEBWS_V_2 = "/webwsV2";
/**
* app长连接url
*/
public static final String APP_WS_URL = "/appws";
/**
* 存储当前登录用户id的字段名
*/
public static final String CURRENT_USER_ID = "CURRENT_USER_ID";
/**
* token有效期(小时)30天
*/
public static final int TOKEN_EXPIRES_HOUR = 720;
/**
* token
*/
public static final String TOKEN = "token";
/**
* 密码加密盐
*/
public static final String SALT = "tillo2018";
/**
* http字符传
*/
public static final String HTTP = "http";
/**
* 标点符号--逗号
*/
public static final String COMMA = ",";
/**
* 标点符号--百分号
*/
public static final String PERCENT = "%";
/**
* 标点符号--问号
*/
public static final String MARK = "?";
/**
* 群聊取模数
*/
public static final Integer GROUP_MODULUS = 32;
/**
* 字符串0
*/
public static final String ZERO = "0";
/**
* 横线
*/
public static final String LINE = "-";
}
package tillo.app_ws.model;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class FileHelperOfflineModel implements Serializable {
private String messageId;
@ApiModelProperty(value = "消息内容")
private String content;
@ApiModelProperty(value = "消息类型 text, audio, image, file, video, location")
private String type;
@ApiModelProperty(value = "资源id")
private String sourceId;
@ApiModelProperty(value = "时长(s)")
private Integer duration;
@ApiModelProperty(value = "位置信息")
private String locationInfo;
@ApiModelProperty(value = "图片/视频尺寸相关信息")
private String measureInfo;
@ApiModelProperty(value = "消息回执id")
private String backId;
@ApiModelProperty(value = "文件名")
private String fileName;
@ApiModelProperty(value = "文件大小")
private String fileSize;
@ApiModelProperty(value = "消息渠道 singleChat, groupChat,systemNotice,syncMySend")
private String route;
@ApiModelProperty(value = "时间戳")
private Date timestamp;
}
package tillo.app_ws.model;
/**
* @author wjm
* @Description 请求头常量管理
* @date 2020/4/14
*/
public class RequestHeaderConstants {
/**
* 存放Authorization的header字段
*/
public static final String AUTHORIZATION = "Authorization";
/**
* 存放userIdn的header字段
*/
public static final String USER_ID = "userId";
/**
* 存放platform的header字段
*/
public static final String PLATFORM = "platform";
/**
* 存放手机唯一设备ID的deviceId的header字段
*/
public static final String DEVICE_ID = "deviceId";
/**
* 存放language的header字段
*/
public static final String LANGUAGE = "language";
/**
* 存放版本号versionNo的header字段
*/
public static final String VERSION_NO = "versionNo";
/**
* 存放版本号IOS版本号的header字段 ps: ios13
*/
public static final String IOS_VERSION = "iOSVersion";
/**
* 存放手机机型信息的header字段
*/
public static final String MOBILE_MODEL = "model";
}
package tillo.app_ws.model;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* @Description null
* @Author hewei hwei1233@163.com
* @Date 2019-12-05
*/
@Data
@Accessors(chain = true)
public class ResponseModel implements Serializable {
/**
* 枚举类UriPathEnum 请求uri的编码
* 由于websocket使用同一个通道发送数据,需要区分不同类型请求
*/
private long path;
/**
* 状态码
*/
private int code;
/**
* 状态描述
*/
private String msg;
/**
* json数据
*/
private Object data;
/**
* 请求id, 以判空是否发送成功, 服务端处理完成后返回
* 可以以当前时间戳为id
*/
private String reqId;
}
package tillo.app_ws.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 自定义请求状态码
*
* @author ScienJus
* @date 2015/7/15.
*/
@Getter
@AllArgsConstructor
public enum ResultStatus {
/**
* 成功
*/
SUCCESS(200, "Success", "成功"),
PRIVILEGE_IS_ERROR(401, "Unauthorized", "未授权限,请联系管理员");
/**
* 返回码
*/
private int code;
/**
* 默认返回英文结果描述 en
*/
private String message;
/**
* 返回中文结果描述 zh_simple
*/
private String zhMessage;
}
package tillo.app_ws.model.request;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @Description null
* @Author hewei hwei1233@163.com
* @Date 2019-12-05
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ReceiveModel implements Serializable {
/**
* 枚举类UriPathEnum 请求uri的编码
* 由于websocket使用同一个通道发送数据,需要区分不同类型请求
*/
private Integer path;
/**
* json数据
*/
private String data;
/**
* 请求id, 以判空是否请求成功, 服务端处理完成后 返回此id
* 由前端生成,可以用uuid,也可以用时间戳
*/
private String reqId;
}
package tillo.app_ws.model.request;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @Description 拉取所有房间离线消息
* @Author hewei hwei1233@163.com
* @Date 2020-01-03
*/
@Data
public class RequestOfflineMsgModel implements Serializable {
/**
* 优先权,需要优先拉取的房间id
* 如为空,可不传
*/
private List<Long> priority;
/**
* 身份公钥
*/
private String identityKey;
}
package tillo.app_ws.receive;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tillo.app_ws.enums.WsRequestCmdEnum;
import tillo.app_ws.model.request.ReceiveModel;
import tillo.app_ws.service.WriteDataService;
import tillo.app_ws.strategy.AbstractReceiveStrategy;
import tillo.app_ws.strategy.ReceiveStrategyContext;
import javax.annotation.Resource;
/**
* @Description ws 数据接收 转换类
* @Author hewei hwei1233@163.com
* @Date 2019-12-03
*/
@Service
public class ReadWsData {
private static Logger log = LoggerFactory.getLogger(ReadWsData.class);
// idea此处报红 属于正常
// @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Autowired
private ReceiveStrategyContext receiveStrategyContext;
@Resource
private WriteDataService writeDataService;
private static final String PING = "p";
/**
* 在此开始进入业务流程子线程,将不占netty的io线程
*
* @param data
* @param userId
* @param language
* @throws Exception
*/
public void convertModel(String data, Long userId, String language) {
if (PING.equals(data)) {
log.debug("收到心跳:" + userId);
return;
}
ReceiveModel requestModel = JSON.parseObject(data, ReceiveModel.class);
if (null == requestModel || null == requestModel.getPath()) {
return;
}
try {
// User user = userService.findById(userId);
this.doProcess(language, requestModel);
} catch (Exception e) {
log.error("系统繁忙:" + data + ",userId:" + userId, e);
// writeDataService.nullDataSuccess(requestModel, ResultStatus.SYS_BUSY, userId, language);
}
}
private void doProcess(String language, ReceiveModel requestModel) {
WsRequestCmdEnum wsRequestUriPathEnum = WsRequestCmdEnum.getByCode(requestModel.getPath());
// 使用策略模式, 根据不同类型请求调用不同实现类
AbstractReceiveStrategy receiveStrategy = receiveStrategyContext.getStrategy(wsRequestUriPathEnum);
receiveStrategy.process(requestModel, language);
}
}
package tillo.app_ws.service;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.AttributeKey;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Description 维护netty用户channel对象
* @Author hewei hwei1233@163.com
* @Date 2019-08-01
*/
public interface AppUserChannelsService {
/**
* channel对象
* 用户id为key
* context为值
*/
Map<String, NioSocketChannel> CHANNEL_MAP = new ConcurrentHashMap<>();
/**
* USER_ID
*/
AttributeKey<Long> USER_ID = AttributeKey.valueOf("userId");
/**
* LANGUAGE
*/
AttributeKey<String> LANGUAGE = AttributeKey.valueOf("language");
/**
* APP_VERSION
*/
AttributeKey<String> APP_VERSION = AttributeKey.valueOf("appVersion");
AttributeKey<String> TOKEN = AttributeKey.valueOf("TOKEN");
AttributeKey<String> DEVICEID = AttributeKey.valueOf("DEVICEID");
AttributeKey<String> PLATFORM = AttributeKey.valueOf("PLATFORM");
/**
* 根据userID获取channel
* @param userId
* @return
*/
NioSocketChannel get(String userId);
/**
* userID绑定channel
* @param userId
* @param channel
*/
void put(String userId, NioSocketChannel channel);
/**
* 移除channel
* @param channelHandlerContext
*/
void remove(ChannelHandlerContext channelHandlerContext);
/**
* 根据channel返回userIds
*
* @param channelHandlerContext
* @return
*/
Long getUserIdByChannel(ChannelHandlerContext channelHandlerContext);
/**
* 下发数据
*
* @param msg
* @param userId
* @return
*/
boolean writeData(String msg, Long userId);
/**
* rpc异步下发数据
*
* @param msg
* @param userId
* @return
*/
boolean rpcWriteData(String msg, Long userId);
/**
* rpc-异步下发踢人数据及关闭旧通道
*
* @param msg
* @param userId
* @return
*/
boolean rpcKickWriteData(String msg, Long userId);
/**
* rpc-异步关闭旧通道
*
* @param userId
* @return
*/
boolean rpcCloseOldChannel(Long userId);
Boolean isOnLocal(Long userId);
}
package tillo.app_ws.service;
import tillo.app_ws.model.ResponseModel;
import tillo.app_ws.model.ResultStatus;
import tillo.app_ws.model.request.ReceiveModel;
/**
* @Description ws响应数据,各种状态码封装
* @Author hewei hwei1233@163.com
* @Date 2019-12-05
*/
public interface WriteDataService {
/**
* 可自定义状态码 带data
*
* @param requestModel
* @param resultStatus
* @param data
* @param userId
* @param language
*/
void dataAndStatus(ReceiveModel requestModel, ResultStatus resultStatus, Object data, Long userId, String language);
/**
* 固定"成功"状态码 带data
*
* @param requestModel
* @param data
* @param userId
* @param language
*/
void successAndData(ReceiveModel requestModel, Object data, Long userId, String language);
/**
* 固定"成功"状态码 无data
*
* @param requestModel
* @param resultStatus
* @param userId
* @param language
*/
void nullDataSuccess(ReceiveModel requestModel, ResultStatus resultStatus, Long userId, String language);
/**
* 固定"参数错误"状态码 无data
*
* @param requestModel
* @param userId
* @param language
*/
void paramErrorAndNullData(ReceiveModel requestModel, Long userId, String language);
/**
* 调用ws处理响应逻辑
*
* @param responseModel
* @param userId
*/
void write(ResponseModel responseModel, Long userId);
}
package tillo.app_ws.service.impl;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import tillo.app_ws.model.ResponseModel;
import tillo.app_ws.model.ResultStatus;
import tillo.app_ws.model.request.ReceiveModel;
import tillo.app_ws.service.AppUserChannelsService;
import tillo.app_ws.service.WriteDataService;
import java.util.HashMap;
/**
* @Description 下发数据
* @Author hewei hwei1233@163.com
* @Date 2019-12-05
*/
@Component
public class WriteDataServiceImpl implements WriteDataService {
@Autowired
private AppUserChannelsService appUserChannelsService;
@Override
public void successAndData(ReceiveModel requestModel, Object data, Long userId, String language) {
this.dataAndStatus(requestModel, ResultStatus.SUCCESS, data, userId, language);
}
@Override
public void nullDataSuccess(ReceiveModel requestModel, ResultStatus resultStatus, Long userId, String language) {
this.dataAndStatus(requestModel, resultStatus, new HashMap<>(1), userId, language);
}
@Override
public void paramErrorAndNullData(ReceiveModel requestModel, Long userId, String language) {
// this.nullDataSuccess(requestModel, ResultStatus.PARAM_ERROR, userId, language);
}
@Override
public void dataAndStatus(ReceiveModel requestModel, ResultStatus resultStatus, Object data, Long userId, String language) {
// ResultModel<String> resultModel = new ResultModel<>(resultStatus, language);
//
// ResponseModel responseModel = new ResponseModel();
// responseModel.setMsg(resultModel.getMessage());
// responseModel.setPath(requestModel.getPath());
// responseModel.setReqId(requestModel.getReqId());
// responseModel.setData(data);
// responseModel.setCode(resultModel.getStatus());
// this.write(responseModel, userId);
}
@Override
public void write(ResponseModel responseModel, Long userId) {
String json = JSON.toJSONString(responseModel);
appUserChannelsService.writeData(json, userId);
}
}
package tillo.app_ws.strategy;
import tillo.app_ws.model.request.ReceiveModel;
/**
* @Description 接收netty不同类型请求
* 抽象类 策略设计模式
* @Author hewei hwei1233@163.com
* @Date 2020-01-02
*/
public abstract class AbstractReceiveStrategy {
/**
* 处理业务流程
*
* @param requestModel
* @param language
* @throws Exception
*/
abstract public void process(ReceiveModel requestModel, String language);
}
package tillo.app_ws.strategy;
import org.springframework.util.CollectionUtils;
import tillo.app_ws.enums.WsRequestCmdEnum;
import tillo.app_ws.utils.SpringBeanUtils;
import java.util.Map;
/**
* @Description 策略模式 上下文
* 维护指令码与策略实现的对应
* @Author hewei hwei1233@163.com
* @Date 2020-01-03
*/
public class ReceiveStrategyContext {
private Map<WsRequestCmdEnum, Class> strategyMap;
public ReceiveStrategyContext(Map<WsRequestCmdEnum, Class> strategyMap) {
this.strategyMap = strategyMap;
}
public AbstractReceiveStrategy getStrategy(WsRequestCmdEnum wsRequestPathEnum) {
if (wsRequestPathEnum == null) {
throw new IllegalArgumentException("not fond enum");
}
if (CollectionUtils.isEmpty(strategyMap)) {
throw new IllegalArgumentException("strategy map is empty,please check you strategy package path");
}
Class aClass = strategyMap.get(wsRequestPathEnum);
if (aClass == null) {
throw new IllegalArgumentException("not fond strategy for type:" + wsRequestPathEnum.getCmdCode());
}
return (AbstractReceiveStrategy) SpringBeanUtils.getBean(aClass);
}
}
package tillo.app_ws.strategy;
import com.google.common.collect.Maps;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
import tillo.app_ws.annotation.ReceiveTypeAnnotation;
import tillo.app_ws.enums.WsRequestCmdEnum;
import tillo.app_ws.utils.ClassScanner;
import java.util.Map;
import java.util.Set;
/**
* @Description 策略 bean注解扫描注册类
* 扫描自定义注解,将指令码与实现类绑定,将对应关系添加到上下文对象中
* <p>
* BeanStrategyProcessor是spring在容器初始化后对外暴露的扩展点,
* spring ioc容器允许beanFactoryPostProcessor在容器加载注册BeanDefinition后读取BeanDefinition,并能修改它。
* @Author hewei hwei1233@163.com
* @Date 2020-01-02
*/
@Component
public class ReceiveStrategyProcessor implements BeanFactoryPostProcessor {
// 扫码注解的包路径
private static final String STRATEGY_PACK = "com.sql.tillo.app_ws.strategy,com.sql.tillo.app_ws.process";
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
Map<WsRequestCmdEnum, Class> handlerMap = Maps.newHashMapWithExpectedSize(5);
// 扫码ReceiveTypeAnnotation注解的类
Set<Class<?>> classSet = ClassScanner.scan(STRATEGY_PACK, ReceiveTypeAnnotation.class);
classSet.forEach(clazz -> {
// 获取注解中的类型值,与枚举类一一对应
WsRequestCmdEnum type = clazz.getAnnotation(ReceiveTypeAnnotation.class).type();
handlerMap.put(type, clazz);
});
// 初始化Contenxt, 将其注册到spring容器当中
ReceiveStrategyContext context = new ReceiveStrategyContext(handlerMap);
try {
configurableListableBeanFactory.registerResolvableDependency(Class.forName(ReceiveStrategyContext.class.getName()), context);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
package tillo.app_ws.strategy.concrete;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import tillo.app_ws.annotation.ReceiveTypeAnnotation;
import tillo.app_ws.enums.WsRequestCmdEnum;
import tillo.app_ws.model.request.ReceiveModel;
import tillo.app_ws.strategy.AbstractReceiveStrategy;
/**
* @Description 处理app单聊消息
*/
@ReceiveTypeAnnotation(type = WsRequestCmdEnum.DATA)
@Service
@Slf4j
public class SingleConcreteReceiveStrategy extends AbstractReceiveStrategy {
@Override
public void process(ReceiveModel requestModel, String language) {
}
}
package tillo.app_ws.utils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.StringUtils;
import org.springframework.util.SystemPropertyUtils;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* @Description 扫描注解 工具类
* @Author hewei hwei1233@163.com
* @Date 2020-01-03
*/
public class ClassScanner implements ResourceLoaderAware {
private final List<TypeFilter> includeFilters = new LinkedList<TypeFilter>();
private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
@SafeVarargs
public static Set<Class<?>> scan(String[] basePackages, Class<? extends Annotation>... annotations) {
ClassScanner cs = new ClassScanner();
if (ArrayUtils.isNotEmpty(annotations)) {
for (Class anno : annotations) {
cs.addIncludeFilter(new AnnotationTypeFilter(anno));
}
}
Set<Class<?>> classes = new HashSet<>();
for (String s : basePackages) {
classes.addAll(cs.doScan(s));
}
return classes;
}
@SafeVarargs
public static Set<Class<?>> scan(String basePackages, Class<? extends Annotation>... annotations) {
return ClassScanner.scan(StringUtils.tokenizeToStringArray(basePackages, ",; \t\n"), annotations);
}
public final ResourceLoader getResourceLoader() {
return this.resourcePatternResolver;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourcePatternResolver = ResourcePatternUtils
.getResourcePatternResolver(resourceLoader);
this.metadataReaderFactory = new CachingMetadataReaderFactory(
resourceLoader);
}
public void addIncludeFilter(TypeFilter includeFilter) {
this.includeFilters.add(includeFilter);
}
public void addExcludeFilter(TypeFilter excludeFilter) {
this.excludeFilters.add(0, excludeFilter);
}
public void resetFilters(boolean useDefaultFilters) {
this.includeFilters.clear();
this.excludeFilters.clear();
}
public Set<Class<?>> doScan(String basePackage) {
Set<Class<?>> classes = new HashSet<>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ org.springframework.util.ClassUtils
.convertClassNameToResourcePath(SystemPropertyUtils
.resolvePlaceholders(basePackage))
+ "/**/*.class";
Resource[] resources = this.resourcePatternResolver
.getResources(packageSearchPath);
for (int i = 0; i < resources.length; i++) {
Resource resource = resources[i];
if (resource.isReadable()) {
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
if ((includeFilters.size() == 0 && excludeFilters.size() == 0) || matches(metadataReader)) {
try {
classes.add(Class.forName(metadataReader
.getClassMetadata().getClassName()));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
} catch (IOException ex) {
throw new BeanDefinitionStoreException(
"I/O failure during classpath scanning", ex);
}
return classes;
}
protected boolean matches(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
return true;
}
}
return false;
}
}
package tillo.app_ws.utils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.util.CharsetUtil;
import org.apache.http.MethodNotSupportedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import springfox.documentation.RequestHandler;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static io.netty.buffer.Unpooled.copiedBuffer;
/**
* @Description netty请求工具类
* @Author hewei hwei1233@163.com
* @Date 2019-07-19
*/
public class FullHttpRequestUtils {
private static Logger logger = LoggerFactory.getLogger(RequestHandler.class);
/**
* 参数解析
*
* @param fullReq
* @return
* @throws MethodNotSupportedException
*/
public static Map<String, String> parameterParse(FullHttpRequest fullReq) throws MethodNotSupportedException {
HttpMethod method = fullReq.method();
Map<String, String> parmMap = new HashMap<>(10);
// GET请求
if (HttpMethod.GET.equals(method) || HttpMethod.DELETE.equals(method)) {
QueryStringDecoder decoder = new QueryStringDecoder(fullReq.uri());
Map<String, List<String>> parameters = decoder.parameters();
parameters.forEach((key, value) -> {
// entry.getValue()是一个List, 只取第一个元素
parmMap.put(key, value.get(0));
});
} else {
// 不支持其它方法
throw new MethodNotSupportedException("");
}
return parmMap;
}
/**
* 获取body参数
*
* @param request
* @return
*/
public static String getBody(FullHttpRequest request) {
ByteBuf buf = request.content();
return buf.toString(CharsetUtil.UTF_8);
}
/**
* 发送的返回值
*
* @param ctx 返回
* @param context 消息
* @param status 状态
*/
public static void send(ChannelHandlerContext ctx, String context, HttpResponseStatus status) {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, copiedBuffer(context, CharsetUtil.UTF_8));
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json;charset=utf-8");
logger.debug("response:\n" + response.toString() + "\ncontext:" + context);
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
package tillo.app_ws.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author hewei123@163.com
* @Description redis 通用工具类
* @createTime 2020年04月14日 16:07:00
*/
@Component
public class RedisUtils {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 获取hash中field对应的值
*
* @param key
* @param field
* @return
*/
public String hget(String key, String field) {
Object val = redisTemplate.opsForHash().get(key, field);
return val == null ? null : val.toString();
}
/**
* 添加or更新hash的值
*
* @param key
* @param field
* @param value
*/
public void hset(String key, String field, String value) {
redisTemplate.opsForHash().put(key, field, value);
}
/**
* 删除hash中field这一对kv
*
* @param key
* @param field
*/
public void hdel(String key, String field) {
redisTemplate.opsForHash().delete(key, field);
}
/**
* 删除key
*
* @param key 如果传入hash类型的key,则把整个hash中所有field删除
*/
public void kdel(String key) {
redisTemplate.delete(key);
}
/**
* 取hash类型 该key所有参数
*
* @param key
* @return
*/
public Map<String, String> hgetll(String key) {
return redisTemplate.execute((RedisCallback<Map<String, String>>) con -> {
Map<byte[], byte[]> result = con.hGetAll(key.getBytes());
if (CollectionUtils.isEmpty(result)) {
return new HashMap<>(0);
}
Map<String, String> ans = new HashMap<>(result.size());
for (Map.Entry<byte[], byte[]> entry : result.entrySet()) {
ans.put(new String(entry.getKey()), new String(entry.getValue()));
}
return ans;
});
}
/**
* 取该hash的key中指定多个参数
*
* @param key
* @param fields
* @return
*/
public Map<String, String> hmget(String key, List<String> fields) {
List<String> result = redisTemplate.<String, String>opsForHash().multiGet(key, fields);
Map<String, String> ans = new HashMap<>(fields.size());
int index = 0;
for (String field : fields) {
if (result.get(index) == null) {
continue;
}
ans.put(field, result.get(index));
}
return ans;
}
/**
* 向指定key中存放set<String>集合
*
* @param key
* @param value
*/
public void addForSet(String key, String value) {
redisTemplate.opsForSet().add(key, value);
}
/**
* 移除指定key中et<String>集合的某值
*
* @param key
*/
public void removeForSet(String key, String value) {
redisTemplate.opsForSet().remove(key, value);
}
/**
* 获取指定key中存放set<String>的集合
*
* @param key
*/
public Set<String> getForSetMembers(String key) {
return redisTemplate.opsForSet().members(key);
}
/**
* 删除指定key缓存
*
* @param key
*/
public void deleteByKey(String key) {
redisTemplate.delete(key);
}
}
package tillo.app_ws.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @Description 在非spring管理的类中获取spring注册的bean
* @Author hewei hwei1233@163.com
* @Date 2020-01-03
*/
@Component
public class SpringBeanUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
if (context != null) {
applicationContext = context;
}
}
public static Object getBean(String name) {
return applicationContext.getBean(name);
}
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
}
package tillo.netty.core;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.springframework.stereotype.Component;
import tillo.app_ws.model.Constants;
import javax.annotation.Resource;
@Component
public class NettyChannelInitializer extends ChannelInitializer<SocketChannel> {
@Resource
private channelInboundHandler channelInboundHandler;
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
// http
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new HttpObjectAggregator(2048576000));
// 服务端api接口
pipeline.addLast("SingleHttpRequestHandler", channelInboundHandler);
// "/appws"路径 升级长连接
pipeline.addLast("appWebSocketServerProtocolHandler", new WebSocketServerProtocolHandler(Constants.APP_WS_URL));
}
}
package tillo.netty.core;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.springframework.stereotype.Component;
@Component
public class NettyStart {
private final NettyChannelInitializer nettyChannelInitializer;
private static EventLoopGroup boss = new NioEventLoopGroup(1);
private static EventLoopGroup work = new NioEventLoopGroup();
private static ServerBootstrap serverBootstrap = new ServerBootstrap();
static {
serverBootstrap.group(boss, work);
//Netty4使用对象池,重用缓冲区
serverBootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
serverBootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
//设置 心跳保活 socket 的参数选项 keepAlive
serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
// 设置不延迟发送TCP_NODELAY=true
serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true);
/* //read缓存区 8*1024=8k
serverBootstrap.option(ChannelOption.SO_RCVBUF,8*1024);
// write缓存区 8*1024=8k
serverBootstrap.option(ChannelOption.SO_SNDBUF,8*1024);
//SO_LINGER*/
// 初始化服务端可连接队列
serverBootstrap.option(ChannelOption.SO_BACKLOG, 1000);
// 配置io模型为nio非阻塞
serverBootstrap.channel(NioServerSocketChannel.class);
}
public NettyStart(NettyChannelInitializer nettyChannelInitializer) {
this.nettyChannelInitializer = nettyChannelInitializer;
}
/**
* Netty创建全部都是实现自AbstractBootstrap。
* 客户端的是Bootstrap,服务端的则是 ServerBootstrap。
**/
public void run(int port) {
try {
//设置过滤器
serverBootstrap.childHandler(nettyChannelInitializer);
// 服务器绑定端口监听
ChannelFuture f = serverBootstrap.bind(port).sync();
f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭EventLoopGroup,释放掉所有资源包括创建的线程
boss.shutdownGracefully();
work.shutdownGracefully();
}
}
}
package tillo.netty.core;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.FullHttpRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import tillo.netty.handler.NettyApiRequest;
import java.net.InetAddress;
@Component
@ChannelHandler.Sharable
public class channelInboundHandler extends ChannelInboundHandlerAdapter {
private final Logger logger = LoggerFactory.getLogger(channelInboundHandler.class);
private final NettyApiRequest singleRequest;
public channelInboundHandler(NettyApiRequest singleRequest) {
this.singleRequest = singleRequest;
}
/**
* 收到消息时,返回信息
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
FullHttpRequest httpRequest = (FullHttpRequest) msg;
try {
singleRequest.handle(ctx, msg, httpRequest);
} catch (Exception e) {
logger.error("SingleNettyServer处理请求失败!", e);
// this.sendBad(ctx);
} finally {
//释放请求
httpRequest.release();
}
}
/**
* 发送错误信息
*
* @param ctx
*/
// private void sendBad(ChannelHandlerContext ctx) {
// String result = null;
//
// try {
// result = JsonUtil.obj2Json(ResultModel.error(ResultStatus.REQUEST_ERROR));
// } catch (IOException ex) {
// ex.printStackTrace();
// }
// FullHttpRequestUtils.send(ctx, result, HttpResponseStatus.OK);
// }
/**
* 建立连接时,返回消息
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("连接的客户端地址:{}", ctx.channel().remoteAddress());
}
ctx.writeAndFlush("客户端" + InetAddress.getLocalHost().getHostName() + "成功与服务端建立连接! ");
super.channelActive(ctx);
}
}
package tillo.netty.handler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import tillo.app_ws.AppImHandler;
import tillo.app_ws.model.Constants;
import tillo.app_ws.model.RequestHeaderConstants;
import tillo.app_ws.service.AppUserChannelsService;
import tillo.app_ws.utils.FullHttpRequestUtils;
import tillo.netty.core.channelInboundHandler;
import javax.annotation.Resource;
import java.util.Map;
/**
* @Description 聊天模块 http请求处理
* @Author hewei hwei1233@163.com
* @Date 2019-07-19
*/
@Component
public class NettyApiRequest {
private final Logger logger = LoggerFactory.getLogger(NettyApiRequest.class);
@Resource
private AppUserChannelsService appUserChannelsService;
@Resource
private AppImHandler appImHandler;
/**
* http请求接收
*
* @param ctx
* @param msg
* @param httpRequest
* @throws Exception
*/
public void handle(ChannelHandlerContext ctx, Object msg, FullHttpRequest httpRequest) throws Exception {
if (!(msg instanceof FullHttpRequest)) {
// String context = JsonUtil.obj2Json(ResultModel.error(ResultStatus.REQUEST_ERROR));
String context = "JsonUtil.obj2Json(ResultModel.error(ResultStatus.REQUEST_ERROR))";
FullHttpRequestUtils.send(ctx, context, HttpResponseStatus.OK);
return;
}
String path = httpRequest.uri();
String body = FullHttpRequestUtils.getBody(httpRequest);
if (logger.isDebugEnabled()) {
logger.debug("httpRequest:\n" + httpRequest.toString() + "\n" + body);
}
if (path.contains(Constants.APP_WS_URL)) {
/*
app聊天http升级webSocket
*/
this.initAppWs(ctx, httpRequest);
}
}
/**
* app 初始化websocket
*
* @param ctx
* @param httpRequest
* @throws org.apache.http.MethodNotSupportedException
*/
private void initAppWs(ChannelHandlerContext ctx, FullHttpRequest httpRequest) throws Exception {
Map<String, String> paramMap = FullHttpRequestUtils.parameterParse(httpRequest);
String token = paramMap.get(Constants.TOKEN);
String deviceId = paramMap.get(RequestHeaderConstants.DEVICE_ID);
String language = paramMap.get(RequestHeaderConstants.LANGUAGE);
String platform = paramMap.get(RequestHeaderConstants.PLATFORM);
// 当前版本号 VERSIONNO
String versionNo = paramMap.get(RequestHeaderConstants.VERSION_NO);
// 请求头userId
String headerUserId = paramMap.get(RequestHeaderConstants.USER_ID);
// 设置uri前缀
httpRequest.setUri(Constants.WEB_WS_URL);
// 保持当前连接
ctx.fireChannelRead(httpRequest.retain());
// 设置属性值 userid - channel
// ctx.channel().attr(AppUserChannelsService.USER_ID).set(userId);
ctx.channel().attr(AppUserChannelsService.LANGUAGE).set(language);
// 添加长连接handler
ctx.pipeline().addAfter("WebSocketServerProtocolHandler", "appImHandler", appImHandler);
// 保存用户上下文对象
appUserChannelsService.put(String.valueOf(headerUserId), (NioSocketChannel) ctx.channel());
//移除当前api处理handler, 不再参与长连接处理
// ctx.pipeline().remove("SingleHttpRequestHandler");
ctx.pipeline().remove(channelInboundHandler.class);
}
}
......@@ -13,7 +13,6 @@ spring-boot-plus:
log-print-type: NONE
request-log-format: false
response-log-format: false
domain: https://sienapi.da300.com
server:
port: 8181
......
......@@ -12,7 +12,6 @@ spring-boot-plus:
log-print-type: LINE
request-log-format: false
response-log-format: false
domain: https://sienapi.da300.com
spring:
......
......@@ -2,7 +2,7 @@
############################# 访问路径、端口tomcat start #############################
server:
port: 80
port: 8886
servlet:
context-path: /api
tomcat:
......@@ -11,10 +11,14 @@ server:
uri-encoding: UTF-8
############################# 访问路径、端口tomcat end ###############################
netty:
port: 8889
################################ spring config start ###############################
spring:
application:
name: SiEn
name: im
http:
encoding:
charset: UTF-8
......@@ -184,9 +188,7 @@ spring-boot-plus:
anon:
# 排除登录 注册 登出
- /user/registerOrLogin,/user/login
- /sms/registerOrLoginCode
- /wechatUser/check
- /wxapi/checkToken
# 排除静态资源
- /static/**,/templates/**
# 排除Swagger
......@@ -195,9 +197,6 @@ spring-boot-plus:
# - /actuator/**
- # 排除首页
- /,/index.html
# 微信支付回调
- /pay/notify/donationOrder
- /pay/notify/vipOrder
# 多行字符串权限配置
......@@ -259,41 +258,6 @@ mybatis-plus:
mapper-locations: classpath*:mapper/**/*Mapper.xml
################################ mybatis-plus end ##################################
wx:
# token: sDsdaSDADad
# # 测试号的appid,测试号管理界面有
# appid: wx7aac805012428dc5
# # 测试号的appsecret,测试号管理界面有
# appsecret:
# # =服务调用的url地址,用于微信web的oauth2授权回调等,若没有可为空。
# callback-url: wx7aac805012428dc5
#
mp:
useRedis: false
redisConfig:
host: 127.0.0.1
port: 6379
configs:
- appId: wxe4a696e431b0456b # 第一个公众号的appid
secret: 87dbca535a713cd505be2f928fa84c29 # 公众号的appsecret
token: 111 # 接口配置里的Token值
aesKey: 111 # 接口配置里的EncodingAESKey值
- appId: 2222 # 第二个公众号的appid,以下同上
secret: 1111
token: 111
aesKey: 111
pay:
appId: wxe4a696e431b0456b #微信公众号或者小程序等的appid
mchId: 1606818547 #微信支付商户号
mchKey: H0p6dhiXRr5FocmzZluNVqyG7QALISj1 #微信支付商户密钥
# subAppId: #服务商模式下的子商户公众账号ID
# subMchId: #服务商模式下的子商户号
keyPath: classpath:src/main/java/io/geekidea/springbootplus/config/properties/apiclient_cert.p12 # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
############################### HikariCP 数据源配置 start ################################
---
spring:
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
......
/*
* Copyright 2019-2029 geekidea(https://github.com/geekidea)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.geekidea.springbootplus.framework.log.entity;
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.geekidea.springbootplus.framework.log.mapper.SysOperationLogMapper">
......
......@@ -65,10 +65,7 @@
<module>framework</module>
<module>generator</module>
<module>common</module>
<module>api-app</module>
<!-- <module>distribution</module>-->
<!-- <module>admin</module>-->
<!-- <module>api-system</module>-->
</modules>
<dependencyManagement>
......@@ -296,12 +293,6 @@
<dependency>
<groupId>io.geekidea.springbootplus</groupId>
<artifactId>api-app</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.geekidea.springbootplus</groupId>
<artifactId>scheduled</artifactId>
<version>${project.version}</version>
</dependency>
......
/target/
/classes
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/build/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
.DS_Store
*.log
logs
*.rdb
# scheduled 项目任务调度模块
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.geekidea.springbootplus</groupId>
<artifactId>parent</artifactId>
<version>2.0</version>
</parent>
<artifactId>scheduled</artifactId>
<name>scheduled</name>
<description>任务调度JOB模块</description>
<dependencies>
<!-- <dependency>-->
<!-- <groupId>io.geekidea.springbootplus</groupId>-->
<!-- <artifactId>example</artifactId>-->
<!-- </dependency>-->
</dependencies>
</project>
/*
* Copyright 2019-2029 geekidea(https://github.com/geekidea)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.geekidea.springbootplus.scheduled;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* @author geekidea
* @date 2020/3/16
**/
@Slf4j
@Component
public class HelloScheduled {
/**
* 每小时执行一次
*/
@Scheduled(cron = "0 0 0/1 * * ? ")
public void hello() throws Exception {
log.debug("HelloScheduled...");
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment